aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--BDInfo/BDInfo.csproj77
-rw-r--r--BDInfo/BDInfo.nuget.targets6
-rw-r--r--BDInfo/BDInfoSettings.cs105
-rw-r--r--BDInfo/BDROM.cs437
-rw-r--r--BDInfo/BitVector32.cs308
-rw-r--r--BDInfo/LanguageCodes.cs493
-rw-r--r--BDInfo/Properties/AssemblyInfo.cs (renamed from MediaBrowser.Model.Portable/Properties/AssemblyInfo.cs)14
-rw-r--r--BDInfo/ReadMe.txt5
-rw-r--r--BDInfo/TSCodecAC3.cs309
-rw-r--r--BDInfo/TSCodecAVC.cs148
-rw-r--r--BDInfo/TSCodecDTS.cs159
-rw-r--r--BDInfo/TSCodecDTSHD.cs246
-rw-r--r--BDInfo/TSCodecLPCM.cs123
-rw-r--r--BDInfo/TSCodecMPEG2.cs208
-rw-r--r--BDInfo/TSCodecMVC.cs36
-rw-r--r--BDInfo/TSCodecTrueHD.cs186
-rw-r--r--BDInfo/TSCodecVC1.cs131
-rw-r--r--BDInfo/TSInterleavedFile.cs38
-rw-r--r--BDInfo/TSPlaylistFile.cs1292
-rw-r--r--BDInfo/TSStream.cs801
-rw-r--r--BDInfo/TSStreamBuffer.cs142
-rw-r--r--BDInfo/TSStreamClip.cs113
-rw-r--r--BDInfo/TSStreamClipFile.cs253
-rw-r--r--BDInfo/TSStreamFile.cs1553
-rw-r--r--BDInfo/project.json17
-rw-r--r--CONTRIBUTING.md0
-rw-r--r--DvdLib/BigEndianBinaryReader.cs33
-rw-r--r--DvdLib/DvdLib.csproj71
-rw-r--r--DvdLib/DvdLib.nuget.targets6
-rw-r--r--DvdLib/Ifo/AudioAttributes.cs41
-rw-r--r--DvdLib/Ifo/Cell.cs24
-rw-r--r--DvdLib/Ifo/CellPlaybackInfo.cs54
-rw-r--r--DvdLib/Ifo/CellPositionInfo.cs21
-rw-r--r--DvdLib/Ifo/Chapter.cs21
-rw-r--r--DvdLib/Ifo/Dvd.cs161
-rw-r--r--DvdLib/Ifo/DvdTime.cs34
-rw-r--r--DvdLib/Ifo/PgcCommandTable.cs20
-rw-r--r--DvdLib/Ifo/Program.cs17
-rw-r--r--DvdLib/Ifo/ProgramChain.cs117
-rw-r--r--DvdLib/Ifo/Title.cs64
-rw-r--r--DvdLib/Ifo/UserOperation.cs38
-rw-r--r--DvdLib/Ifo/VideoAttributes.cs51
-rw-r--r--DvdLib/Properties/AssemblyInfo.cs29
-rw-r--r--DvdLib/project.json17
-rw-r--r--Emby.Common.Implementations/Archiving/ZipClient.cs (renamed from MediaBrowser.Common.Implementations/Archiving/ZipClient.cs)44
-rw-r--r--Emby.Common.Implementations/BaseApplicationHost.cs (renamed from MediaBrowser.Common.Implementations/BaseApplicationHost.cs)318
-rw-r--r--Emby.Common.Implementations/BaseApplicationPaths.cs (renamed from MediaBrowser.Common.Implementations/BaseApplicationPaths.cs)16
-rw-r--r--Emby.Common.Implementations/Configuration/BaseConfigurationManager.cs (renamed from MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs)29
-rw-r--r--Emby.Common.Implementations/Configuration/ConfigurationHelper.cs (renamed from MediaBrowser.Common.Implementations/Configuration/ConfigurationHelper.cs)15
-rw-r--r--Emby.Common.Implementations/Cryptography/CryptographyProvider.cs40
-rw-r--r--Emby.Common.Implementations/Devices/DeviceId.cs (renamed from MediaBrowser.Common.Implementations/Devices/DeviceId.cs)10
-rw-r--r--Emby.Common.Implementations/Diagnostics/CommonProcess.cs108
-rw-r--r--Emby.Common.Implementations/Diagnostics/ProcessFactory.cs12
-rw-r--r--Emby.Common.Implementations/Emby.Common.Implementations.xproj23
-rw-r--r--Emby.Common.Implementations/EnvironmentInfo/EnvironmentInfo.cs119
-rw-r--r--Emby.Common.Implementations/HttpClientManager/HttpClientInfo.cs (renamed from MediaBrowser.Common.Implementations/HttpClientManager/HttpClientInfo.cs)2
-rw-r--r--Emby.Common.Implementations/HttpClientManager/HttpClientManager.cs (renamed from MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs)114
-rw-r--r--Emby.Common.Implementations/IO/IsoManager.cs (renamed from MediaBrowser.Common.Implementations/IO/IsoManager.cs)6
-rw-r--r--Emby.Common.Implementations/IO/ManagedFileSystem.cs794
-rw-r--r--Emby.Common.Implementations/Logging/NLogger.cs (renamed from MediaBrowser.Common.Implementations/Logging/NLogger.cs)2
-rw-r--r--Emby.Common.Implementations/Logging/NlogManager.cs544
-rw-r--r--Emby.Common.Implementations/Net/DisposableManagedObjectBase.cs74
-rw-r--r--Emby.Common.Implementations/Net/NetSocket.cs97
-rw-r--r--Emby.Common.Implementations/Net/SocketAcceptor.cs127
-rw-r--r--Emby.Common.Implementations/Net/SocketFactory.cs160
-rw-r--r--Emby.Common.Implementations/Net/UdpSocket.cs242
-rw-r--r--Emby.Common.Implementations/Networking/NetworkManager.cs (renamed from MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs)258
-rw-r--r--Emby.Common.Implementations/Properties/AssemblyInfo.cs19
-rw-r--r--Emby.Common.Implementations/Reflection/AssemblyInfo.cs31
-rw-r--r--Emby.Common.Implementations/ScheduledTasks/DailyTrigger.cs (renamed from MediaBrowser.Common/ScheduledTasks/DailyTrigger.cs)8
-rw-r--r--Emby.Common.Implementations/ScheduledTasks/IntervalTrigger.cs (renamed from MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs)8
-rw-r--r--Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs (renamed from MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs)173
-rw-r--r--Emby.Common.Implementations/ScheduledTasks/StartupTrigger.cs (renamed from MediaBrowser.Common/ScheduledTasks/StartupTrigger.cs)8
-rw-r--r--Emby.Common.Implementations/ScheduledTasks/SystemEventTrigger.cs (renamed from MediaBrowser.Common/ScheduledTasks/SystemEventTrigger.cs)44
-rw-r--r--Emby.Common.Implementations/ScheduledTasks/TaskManager.cs (renamed from MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs)51
-rw-r--r--Emby.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs (renamed from MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs)31
-rw-r--r--Emby.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs (renamed from MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs)27
-rw-r--r--Emby.Common.Implementations/ScheduledTasks/Tasks/ReloadLoggerFileTask.cs (renamed from MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/ReloadLoggerFileTask.cs)21
-rw-r--r--Emby.Common.Implementations/ScheduledTasks/WeeklyTrigger.cs (renamed from MediaBrowser.Common/ScheduledTasks/WeeklyTrigger.cs)2
-rw-r--r--Emby.Common.Implementations/Serialization/JsonSerializer.cs (renamed from MediaBrowser.Common.Implementations/Serialization/JsonSerializer.cs)10
-rw-r--r--Emby.Common.Implementations/Serialization/XmlSerializer.cs (renamed from MediaBrowser.Common.Implementations/Serialization/XmlSerializer.cs)37
-rw-r--r--Emby.Common.Implementations/TextEncoding/TextEncoding.cs43
-rw-r--r--Emby.Common.Implementations/Threading/CommonTimer.cs39
-rw-r--r--Emby.Common.Implementations/Threading/TimerFactory.cs21
-rw-r--r--Emby.Common.Implementations/Xml/XmlReaderSettingsFactory.cs22
-rw-r--r--Emby.Common.Implementations/project.json71
-rw-r--r--Emby.Dlna/Common/Argument.cs (renamed from MediaBrowser.Dlna/Common/Argument.cs)2
-rw-r--r--Emby.Dlna/Common/DeviceIcon.cs (renamed from MediaBrowser.Dlna/Common/DeviceIcon.cs)2
-rw-r--r--Emby.Dlna/Common/DeviceService.cs (renamed from MediaBrowser.Dlna/Common/DeviceService.cs)2
-rw-r--r--Emby.Dlna/Common/ServiceAction.cs (renamed from MediaBrowser.Dlna/Common/ServiceAction.cs)2
-rw-r--r--Emby.Dlna/Common/StateVariable.cs (renamed from MediaBrowser.Dlna/Common/StateVariable.cs)2
-rw-r--r--Emby.Dlna/ConfigurationExtension.cs (renamed from MediaBrowser.Dlna/ConfigurationExtension.cs)2
-rw-r--r--Emby.Dlna/ConnectionManager/ConnectionManager.cs (renamed from MediaBrowser.Dlna/ConnectionManager/ConnectionManager.cs)11
-rw-r--r--Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs (renamed from MediaBrowser.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs)6
-rw-r--r--Emby.Dlna/ConnectionManager/ControlHandler.cs (renamed from MediaBrowser.Dlna/ConnectionManager/ControlHandler.cs)22
-rw-r--r--Emby.Dlna/ConnectionManager/ServiceActionListBuilder.cs (renamed from MediaBrowser.Dlna/ConnectionManager/ServiceActionListBuilder.cs)4
-rw-r--r--Emby.Dlna/ContentDirectory/ContentDirectory.cs (renamed from MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs)17
-rw-r--r--Emby.Dlna/ContentDirectory/ContentDirectoryBrowser.cs (renamed from MediaBrowser.Dlna/ContentDirectory/ContentDirectoryBrowser.cs)9
-rw-r--r--Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs (renamed from MediaBrowser.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs)6
-rw-r--r--Emby.Dlna/ContentDirectory/ControlHandler.cs (renamed from MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs)272
-rw-r--r--Emby.Dlna/ContentDirectory/ServiceActionListBuilder.cs (renamed from MediaBrowser.Dlna/ContentDirectory/ServiceActionListBuilder.cs)4
-rw-r--r--Emby.Dlna/Didl/DidlBuilder.cs (renamed from MediaBrowser.Dlna/Didl/DidlBuilder.cs)393
-rw-r--r--Emby.Dlna/Didl/Filter.cs (renamed from MediaBrowser.Dlna/Didl/Filter.cs)2
-rw-r--r--Emby.Dlna/Didl/StringWriterWithEncoding.cs62
-rw-r--r--Emby.Dlna/DlnaManager.cs (renamed from MediaBrowser.Dlna/DlnaManager.cs)95
-rw-r--r--Emby.Dlna/Emby.Dlna.csproj (renamed from MediaBrowser.Dlna/MediaBrowser.Dlna.csproj)237
-rw-r--r--Emby.Dlna/Eventing/EventManager.cs (renamed from MediaBrowser.Dlna/Eventing/EventManager.cs)2
-rw-r--r--Emby.Dlna/Eventing/EventSubscription.cs (renamed from MediaBrowser.Dlna/Eventing/EventSubscription.cs)2
-rw-r--r--Emby.Dlna/Images/logo120.jpg (renamed from MediaBrowser.Dlna/Images/logo120.jpg)bin5054 -> 5054 bytes
-rw-r--r--Emby.Dlna/Images/logo120.png (renamed from MediaBrowser.Dlna/Images/logo120.png)bin840 -> 840 bytes
-rw-r--r--Emby.Dlna/Images/logo240.jpg (renamed from MediaBrowser.Dlna/Images/logo240.jpg)bin8197 -> 8197 bytes
-rw-r--r--Emby.Dlna/Images/logo240.png (renamed from MediaBrowser.Dlna/Images/logo240.png)bin1681 -> 1681 bytes
-rw-r--r--Emby.Dlna/Images/logo48.jpg (renamed from MediaBrowser.Dlna/Images/logo48.jpg)bin2099 -> 2099 bytes
-rw-r--r--Emby.Dlna/Images/logo48.png (renamed from MediaBrowser.Dlna/Images/logo48.png)bin699 -> 699 bytes
-rw-r--r--Emby.Dlna/Images/people48.jpg (renamed from MediaBrowser.Dlna/Images/people48.jpg)bin1101 -> 1101 bytes
-rw-r--r--Emby.Dlna/Images/people48.png (renamed from MediaBrowser.Dlna/Images/people48.png)bin688 -> 688 bytes
-rw-r--r--Emby.Dlna/Images/people480.jpg (renamed from MediaBrowser.Dlna/Images/people480.jpg)bin8691 -> 8691 bytes
-rw-r--r--Emby.Dlna/Images/people480.png (renamed from MediaBrowser.Dlna/Images/people480.png)bin6351 -> 6351 bytes
-rw-r--r--Emby.Dlna/Main/DlnaEntryPoint.cs (renamed from MediaBrowser.Dlna/Main/DlnaEntryPoint.cs)78
-rw-r--r--Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs (renamed from MediaBrowser.Dlna/MediaReceiverRegistrar/ControlHandler.cs)21
-rw-r--r--Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs (renamed from MediaBrowser.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs)11
-rw-r--r--Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs (renamed from MediaBrowser.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs)6
-rw-r--r--Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs (renamed from MediaBrowser.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs)4
-rw-r--r--Emby.Dlna/PlayTo/CurrentIdEventArgs.cs (renamed from MediaBrowser.Dlna/PlayTo/CurrentIdEventArgs.cs)2
-rw-r--r--Emby.Dlna/PlayTo/Device.cs (renamed from MediaBrowser.Dlna/PlayTo/Device.cs)87
-rw-r--r--Emby.Dlna/PlayTo/DeviceInfo.cs (renamed from MediaBrowser.Dlna/PlayTo/DeviceInfo.cs)4
-rw-r--r--Emby.Dlna/PlayTo/PlayToController.cs (renamed from MediaBrowser.Dlna/PlayTo/PlayToController.cs)6
-rw-r--r--Emby.Dlna/PlayTo/PlayToManager.cs (renamed from MediaBrowser.Dlna/PlayTo/PlayToManager.cs)30
-rw-r--r--Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs (renamed from MediaBrowser.Dlna/PlayTo/PlaybackProgressEventArgs.cs)2
-rw-r--r--Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs (renamed from MediaBrowser.Dlna/PlayTo/PlaybackStartEventArgs.cs)2
-rw-r--r--Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs (renamed from MediaBrowser.Dlna/PlayTo/PlaybackStoppedEventArgs.cs)2
-rw-r--r--Emby.Dlna/PlayTo/PlaylistItem.cs (renamed from MediaBrowser.Dlna/PlayTo/PlaylistItem.cs)2
-rw-r--r--Emby.Dlna/PlayTo/PlaylistItemFactory.cs (renamed from MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs)2
-rw-r--r--Emby.Dlna/PlayTo/SsdpHttpClient.cs (renamed from MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs)4
-rw-r--r--Emby.Dlna/PlayTo/TRANSPORTSTATE.cs (renamed from MediaBrowser.Dlna/PlayTo/TRANSPORTSTATE.cs)2
-rw-r--r--Emby.Dlna/PlayTo/TransportCommands.cs (renamed from MediaBrowser.Dlna/PlayTo/TransportCommands.cs)6
-rw-r--r--Emby.Dlna/PlayTo/TransportStateEventArgs.cs (renamed from MediaBrowser.Dlna/PlayTo/TransportStateEventArgs.cs)2
-rw-r--r--Emby.Dlna/PlayTo/UpnpContainer.cs (renamed from MediaBrowser.Dlna/PlayTo/UpnpContainer.cs)4
-rw-r--r--Emby.Dlna/PlayTo/uBaseObject.cs (renamed from MediaBrowser.Dlna/PlayTo/uBaseObject.cs)12
-rw-r--r--Emby.Dlna/PlayTo/uParser.cs (renamed from MediaBrowser.Dlna/PlayTo/uParser.cs)2
-rw-r--r--Emby.Dlna/PlayTo/uParserObject.cs (renamed from MediaBrowser.Dlna/PlayTo/uParserObject.cs)2
-rw-r--r--Emby.Dlna/PlayTo/uPnpNamespaces.cs (renamed from MediaBrowser.Dlna/PlayTo/uPnpNamespaces.cs)2
-rw-r--r--Emby.Dlna/Profiles/BubbleUpnpProfile.cs (renamed from MediaBrowser.Dlna/Profiles/BubbleUpnpProfile.cs)2
-rw-r--r--Emby.Dlna/Profiles/DefaultProfile.cs (renamed from MediaBrowser.Dlna/Profiles/DefaultProfile.cs)6
-rw-r--r--Emby.Dlna/Profiles/DenonAvrProfile.cs (renamed from MediaBrowser.Dlna/Profiles/DenonAvrProfile.cs)2
-rw-r--r--Emby.Dlna/Profiles/DirectTvProfile.cs (renamed from MediaBrowser.Dlna/Profiles/DirectTvProfile.cs)2
-rw-r--r--Emby.Dlna/Profiles/DishHopperJoeyProfile.cs (renamed from MediaBrowser.Dlna/Profiles/DishHopperJoeyProfile.cs)2
-rw-r--r--Emby.Dlna/Profiles/Foobar2000Profile.cs (renamed from MediaBrowser.Dlna/Profiles/Foobar2000Profile.cs)2
-rw-r--r--Emby.Dlna/Profiles/KodiProfile.cs (renamed from MediaBrowser.Dlna/Profiles/KodiProfile.cs)2
-rw-r--r--Emby.Dlna/Profiles/LgTvProfile.cs (renamed from MediaBrowser.Dlna/Profiles/LgTvProfile.cs)2
-rw-r--r--Emby.Dlna/Profiles/LinksysDMA2100Profile.cs (renamed from MediaBrowser.Dlna/Profiles/LinksysDMA2100Profile.cs)2
-rw-r--r--Emby.Dlna/Profiles/MediaMonkeyProfile.cs (renamed from MediaBrowser.Dlna/Profiles/MediaMonkeyProfile.cs)2
-rw-r--r--Emby.Dlna/Profiles/PanasonicVieraProfile.cs (renamed from MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs)2
-rw-r--r--Emby.Dlna/Profiles/PopcornHourProfile.cs (renamed from MediaBrowser.Dlna/Profiles/PopcornHourProfile.cs)2
-rw-r--r--Emby.Dlna/Profiles/SamsungSmartTvProfile.cs (renamed from MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs)2
-rw-r--r--Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs (renamed from MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013.cs)2
-rw-r--r--Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs (renamed from MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2014.cs)2
-rw-r--r--Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs (renamed from MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2015.cs)2
-rw-r--r--Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs (renamed from MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2016.cs)2
-rw-r--r--Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs (renamed from MediaBrowser.Dlna/Profiles/SonyBlurayPlayerProfile.cs)2
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2010Profile.cs (renamed from MediaBrowser.Dlna/Profiles/SonyBravia2010Profile.cs)2
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2011Profile.cs (renamed from MediaBrowser.Dlna/Profiles/SonyBravia2011Profile.cs)2
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2012Profile.cs (renamed from MediaBrowser.Dlna/Profiles/SonyBravia2012Profile.cs)2
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2013Profile.cs (renamed from MediaBrowser.Dlna/Profiles/SonyBravia2013Profile.cs)2
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2014Profile.cs (renamed from MediaBrowser.Dlna/Profiles/SonyBravia2014Profile.cs)2
-rw-r--r--Emby.Dlna/Profiles/SonyPs3Profile.cs (renamed from MediaBrowser.Dlna/Profiles/SonyPs3Profile.cs)2
-rw-r--r--Emby.Dlna/Profiles/SonyPs4Profile.cs (renamed from MediaBrowser.Dlna/Profiles/SonyPs4Profile.cs)2
-rw-r--r--Emby.Dlna/Profiles/VlcProfile.cs (renamed from MediaBrowser.Dlna/Profiles/VlcProfile.cs)2
-rw-r--r--Emby.Dlna/Profiles/WdtvLiveProfile.cs (renamed from MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs)2
-rw-r--r--Emby.Dlna/Profiles/Xbox360Profile.cs (renamed from MediaBrowser.Dlna/Profiles/Xbox360Profile.cs)2
-rw-r--r--Emby.Dlna/Profiles/XboxOneProfile.cs (renamed from MediaBrowser.Dlna/Profiles/XboxOneProfile.cs)2
-rw-r--r--Emby.Dlna/Profiles/Xml/BubbleUPnp.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/BubbleUPnp.xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/Default.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/Default.xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/Denon AVR.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/DirecTV HD-DVR.xml)8
-rw-r--r--Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/Dish Hopper-Joey.xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/Kodi.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/Kodi.xml)8
-rw-r--r--Emby.Dlna/Profiles/Xml/LG Smart TV.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/MediaMonkey.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/Panasonic Viera.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/Popcorn Hour.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/Popcorn Hour.xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony Bravia (2010).xml (renamed from MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml (renamed from MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml (renamed from MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml (renamed from MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml (renamed from MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2014).xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 4.xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/Vlc.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/Vlc.xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/WDTV Live.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/Xbox 360.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/Xbox One.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml)10
-rw-r--r--Emby.Dlna/Profiles/Xml/foobar2000.xml (renamed from MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml)10
-rw-r--r--Emby.Dlna/Properties/AssemblyInfo.cs30
-rw-r--r--Emby.Dlna/Server/DescriptionXmlBuilder.cs (renamed from MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs)133
-rw-r--r--Emby.Dlna/Server/UpnpDevice.cs (renamed from MediaBrowser.Dlna/Server/UpnpDevice.cs)7
-rw-r--r--Emby.Dlna/Service/BaseControlHandler.cs260
-rw-r--r--Emby.Dlna/Service/BaseService.cs (renamed from MediaBrowser.Dlna/Service/BaseService.cs)4
-rw-r--r--Emby.Dlna/Service/ControlErrorHandler.cs55
-rw-r--r--Emby.Dlna/Service/ServiceXmlBuilder.cs (renamed from MediaBrowser.Dlna/Service/ServiceXmlBuilder.cs)19
-rw-r--r--Emby.Dlna/Ssdp/DeviceDiscovery.cs (renamed from MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs)33
-rw-r--r--Emby.Dlna/Ssdp/Extensions.cs (renamed from MediaBrowser.Dlna/Ssdp/Extensions.cs)3
-rw-r--r--Emby.Dlna/project.json17
-rw-r--r--Emby.Drawing.ImageMagick/Emby.Drawing.ImageMagick.csproj (renamed from Mono.Nat/Mono.Nat.csproj)69
-rw-r--r--Emby.Drawing.ImageMagick/ImageHelpers.cs43
-rw-r--r--Emby.Drawing.ImageMagick/ImageMagickEncoder.cs (renamed from Emby.Drawing/ImageMagick/ImageMagickEncoder.cs)25
-rw-r--r--Emby.Drawing.ImageMagick/PercentPlayedDrawer.cs (renamed from Emby.Drawing/ImageMagick/PercentPlayedDrawer.cs)0
-rw-r--r--Emby.Drawing.ImageMagick/PlayedIndicatorDrawer.cs (renamed from Emby.Drawing/ImageMagick/PlayedIndicatorDrawer.cs)4
-rw-r--r--Emby.Drawing.ImageMagick/Properties/AssemblyInfo.cs (renamed from MediaBrowser.Model.net35/Properties/AssemblyInfo.cs)16
-rw-r--r--Emby.Drawing.ImageMagick/StripCollageBuilder.cs (renamed from Emby.Drawing/ImageMagick/StripCollageBuilder.cs)4
-rw-r--r--Emby.Drawing.ImageMagick/UnplayedCountIndicator.cs (renamed from Emby.Drawing/ImageMagick/UnplayedCountIndicator.cs)4
-rw-r--r--Emby.Drawing.ImageMagick/packages.config4
-rw-r--r--Emby.Drawing.Net/DynamicImageHelpers.cs (renamed from Emby.Drawing/GDI/DynamicImageHelpers.cs)6
-rw-r--r--Emby.Drawing.Net/Emby.Drawing.Net.csproj78
-rw-r--r--Emby.Drawing.Net/GDIImageEncoder.cs (renamed from Emby.Drawing/GDI/GDIImageEncoder.cs)10
-rw-r--r--Emby.Drawing.Net/ImageExtensions.cs (renamed from Emby.Drawing/GDI/ImageExtensions.cs)2
-rw-r--r--Emby.Drawing.Net/ImageHelpers.cs (renamed from Emby.Drawing/ImageHelpers.cs)2
-rw-r--r--Emby.Drawing.Net/PercentPlayedDrawer.cs (renamed from Emby.Drawing/GDI/PercentPlayedDrawer.cs)2
-rw-r--r--Emby.Drawing.Net/PlayedIndicatorDrawer.cs (renamed from Emby.Drawing/GDI/PlayedIndicatorDrawer.cs)2
-rw-r--r--Emby.Drawing.Net/Properties/AssemblyInfo.cs (renamed from MediaBrowser.Dlna/Properties/AssemblyInfo.cs)16
-rw-r--r--Emby.Drawing.Net/UnplayedCountIndicator.cs (renamed from Emby.Drawing/GDI/UnplayedCountIndicator.cs)2
-rw-r--r--Emby.Drawing.Net/empty.png (renamed from Emby.Drawing/GDI/empty.png)bin158 -> 158 bytes
-rw-r--r--Emby.Drawing/Common/ImageHeader.cs6
-rw-r--r--Emby.Drawing/Emby.Drawing.csproj53
-rw-r--r--Emby.Drawing/ImageProcessor.cs107
-rw-r--r--Emby.Drawing/packages.config6
-rw-r--r--Emby.Drawing/project.json17
-rw-r--r--Emby.Photos/Emby.Photos.csproj68
-rw-r--r--Emby.Photos/Emby.Photos.nuget.targets6
-rw-r--r--Emby.Photos/PhotoProvider.cs (renamed from MediaBrowser.Providers/Photos/PhotoProvider.cs)22
-rw-r--r--Emby.Photos/Properties/AssemblyInfo.cs (renamed from MediaBrowser.Common.Implementations/Properties/AssemblyInfo.cs)12
-rw-r--r--Emby.Photos/project.json17
-rw-r--r--Emby.Server.Core/ApplicationHost.cs1661
-rw-r--r--Emby.Server.Core/Configuration/ServerConfigurationManager.cs (renamed from MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs)14
-rw-r--r--Emby.Server.Core/Emby.Server.Core.xproj34
-rw-r--r--Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs (renamed from MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs)81
-rw-r--r--Emby.Server.Core/HttpServerFactory.cs109
-rw-r--r--Emby.Server.Core/IO/LibraryMonitor.cs631
-rw-r--r--Emby.Server.Core/Localization/TextLocalizer.cs56
-rw-r--r--Emby.Server.Core/Properties/AssemblyInfo.cs19
-rw-r--r--Emby.Server.Core/ServerApplicationPaths.cs (renamed from MediaBrowser.Server.Implementations/ServerApplicationPaths.cs)10
-rw-r--r--Emby.Server.Core/UnhandledExceptionWriter.cs (renamed from MediaBrowser.Server.Startup.Common/UnhandledExceptionWriter.cs)3
-rw-r--r--Emby.Server.Core/project.json131
-rw-r--r--Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs (renamed from MediaBrowser.Server.Implementations/EntryPoints/ActivityLogEntryPoint.cs)21
-rw-r--r--Emby.Server.Implementations/Activity/ActivityManager.cs (renamed from MediaBrowser.Server.Implementations/Activity/ActivityManager.cs)3
-rw-r--r--Emby.Server.Implementations/Activity/ActivityRepository.cs222
-rw-r--r--Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs (renamed from MediaBrowser.Server.Implementations/Branding/BrandingConfigurationFactory.cs)2
-rw-r--r--Emby.Server.Implementations/Browser/BrowserLauncher.cs (renamed from MediaBrowser.Server.Startup.Common/Browser/BrowserLauncher.cs)6
-rw-r--r--Emby.Server.Implementations/Channels/ChannelConfigurations.cs (renamed from MediaBrowser.Server.Implementations/Channels/ChannelConfigurations.cs)2
-rw-r--r--Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs (renamed from MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs)2
-rw-r--r--Emby.Server.Implementations/Channels/ChannelImageProvider.cs (renamed from MediaBrowser.Server.Implementations/Channels/ChannelImageProvider.cs)2
-rw-r--r--Emby.Server.Implementations/Channels/ChannelManager.cs (renamed from MediaBrowser.Server.Implementations/Channels/ChannelManager.cs)96
-rw-r--r--Emby.Server.Implementations/Channels/ChannelPostScanTask.cs (renamed from MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs)4
-rw-r--r--Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs (renamed from MediaBrowser.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs)25
-rw-r--r--Emby.Server.Implementations/Collections/CollectionImageProvider.cs (renamed from MediaBrowser.Server.Implementations/Collections/CollectionImageProvider.cs)8
-rw-r--r--Emby.Server.Implementations/Collections/CollectionManager.cs (renamed from MediaBrowser.Server.Implementations/Collections/CollectionManager.cs)4
-rw-r--r--Emby.Server.Implementations/Collections/CollectionsDynamicFolder.cs (renamed from MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs)6
-rw-r--r--Emby.Server.Implementations/Connect/ConnectData.cs (renamed from MediaBrowser.Server.Implementations/Connect/ConnectData.cs)2
-rw-r--r--Emby.Server.Implementations/Connect/ConnectEntryPoint.cs (renamed from MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs)36
-rw-r--r--Emby.Server.Implementations/Connect/ConnectManager.cs (renamed from MediaBrowser.Server.Implementations/Connect/ConnectManager.cs)14
-rw-r--r--Emby.Server.Implementations/Connect/Responses.cs (renamed from MediaBrowser.Server.Implementations/Connect/Responses.cs)2
-rw-r--r--Emby.Server.Implementations/Connect/Validator.cs (renamed from MediaBrowser.Server.Implementations/Connect/Validator.cs)2
-rw-r--r--Emby.Server.Implementations/Data/BaseSqliteRepository.cs401
-rw-r--r--Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs (renamed from MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs)173
-rw-r--r--Emby.Server.Implementations/Data/ManagedConnection.cs82
-rw-r--r--Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs239
-rw-r--r--Emby.Server.Implementations/Data/SqliteExtensions.cs394
-rw-r--r--Emby.Server.Implementations/Data/SqliteFileOrganizationRepository.cs284
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs (renamed from MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs)4056
-rw-r--r--Emby.Server.Implementations/Data/SqliteUserDataRepository.cs422
-rw-r--r--Emby.Server.Implementations/Data/SqliteUserRepository.cs167
-rw-r--r--Emby.Server.Implementations/Data/TypeMapper.cs (renamed from MediaBrowser.Server.Implementations/Persistence/TypeMapper.cs)21
-rw-r--r--Emby.Server.Implementations/Devices/CameraUploadsDynamicFolder.cs41
-rw-r--r--Emby.Server.Implementations/Devices/DeviceManager.cs (renamed from MediaBrowser.Server.Implementations/Devices/DeviceManager.cs)15
-rw-r--r--Emby.Server.Implementations/Devices/DeviceRepository.cs (renamed from MediaBrowser.Server.Implementations/Devices/DeviceRepository.cs)18
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs (renamed from MediaBrowser.Server.Implementations/Dto/DtoService.cs)168
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj435
-rw-r--r--Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs (renamed from MediaBrowser.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs)14
-rw-r--r--Emby.Server.Implementations/EntryPoints/KeepServerAwake.cs65
-rw-r--r--Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs (renamed from MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs)17
-rw-r--r--Emby.Server.Implementations/EntryPoints/LoadRegistrations.cs (renamed from MediaBrowser.Server.Implementations/EntryPoints/LoadRegistrations.cs)12
-rw-r--r--Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs (renamed from MediaBrowser.Server.Implementations/EntryPoints/RecordingNotifier.cs)10
-rw-r--r--Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs (renamed from MediaBrowser.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs)2
-rw-r--r--Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs (renamed from MediaBrowser.Server.Implementations/EntryPoints/ServerEventNotifier.cs)4
-rw-r--r--Emby.Server.Implementations/EntryPoints/StartupWizard.cs (renamed from MediaBrowser.Server.Startup.Common/EntryPoints/StartupWizard.cs)6
-rw-r--r--Emby.Server.Implementations/EntryPoints/SystemEvents.cs47
-rw-r--r--Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs (renamed from MediaBrowser.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs)25
-rw-r--r--Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs (renamed from MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs)2
-rw-r--r--Emby.Server.Implementations/EntryPoints/UsageReporter.cs (renamed from MediaBrowser.Server.Implementations/EntryPoints/UsageReporter.cs)2
-rw-r--r--Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs (renamed from MediaBrowser.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs)13
-rw-r--r--Emby.Server.Implementations/FFMpeg/FFMpegInfo.cs (renamed from MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegInfo.cs)2
-rw-r--r--Emby.Server.Implementations/FFMpeg/FFMpegInstallInfo.cs (renamed from MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegInstallInfo.cs)2
-rw-r--r--Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs (renamed from MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegLoader.cs)28
-rw-r--r--Emby.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs (renamed from MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs)38
-rw-r--r--Emby.Server.Implementations/FileOrganization/Extensions.cs (renamed from MediaBrowser.Server.Implementations/FileOrganization/Extensions.cs)2
-rw-r--r--Emby.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs (renamed from MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs)6
-rw-r--r--Emby.Server.Implementations/FileOrganization/FileOrganizationService.cs (renamed from MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs)8
-rw-r--r--Emby.Server.Implementations/FileOrganization/NameUtils.cs (renamed from MediaBrowser.Server.Implementations/FileOrganization/NameUtils.cs)21
-rw-r--r--Emby.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs (renamed from MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs)29
-rw-r--r--Emby.Server.Implementations/FileOrganization/TvFolderOrganizer.cs (renamed from MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs)8
-rw-r--r--Emby.Server.Implementations/HttpServer/GetSwaggerResource.cs (renamed from MediaBrowser.Server.Implementations/HttpServer/GetSwaggerResource.cs)4
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs729
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpResultFactory.cs (renamed from MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs)283
-rw-r--r--Emby.Server.Implementations/HttpServer/IHttpListener.cs (renamed from MediaBrowser.Server.Implementations/HttpServer/IHttpListener.cs)4
-rw-r--r--Emby.Server.Implementations/HttpServer/LoggerUtils.cs (renamed from MediaBrowser.Server.Implementations/HttpServer/LoggerUtils.cs)2
-rw-r--r--Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs (renamed from MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs)89
-rw-r--r--Emby.Server.Implementations/HttpServer/ResponseFilter.cs (renamed from MediaBrowser.Server.Implementations/HttpServer/ResponseFilter.cs)46
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthService.cs (renamed from MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs)2
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs (renamed from MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs)6
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/SessionContext.cs (renamed from MediaBrowser.Server.Implementations/HttpServer/Security/SessionContext.cs)8
-rw-r--r--Emby.Server.Implementations/HttpServer/SocketSharp/Extensions.cs12
-rw-r--r--Emby.Server.Implementations/HttpServer/SocketSharp/HttpUtility.cs (renamed from MediaBrowser.Server.Implementations/HttpServer/SocketSharp/HttpUtility.cs)35
-rw-r--r--Emby.Server.Implementations/HttpServer/SocketSharp/RequestMono.cs (renamed from MediaBrowser.Server.Implementations/HttpServer/SocketSharp/RequestMono.cs)136
-rw-r--r--Emby.Server.Implementations/HttpServer/SocketSharp/SharpWebSocket.cs (renamed from MediaBrowser.Server.Implementations/HttpServer/SocketSharp/SharpWebSocket.cs)20
-rw-r--r--Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs (renamed from MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs)117
-rw-r--r--Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs (renamed from MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs)304
-rw-r--r--Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs193
-rw-r--r--Emby.Server.Implementations/HttpServer/StreamWriter.cs (renamed from MediaBrowser.Server.Implementations/HttpServer/StreamWriter.cs)74
-rw-r--r--Emby.Server.Implementations/HttpServer/SwaggerService.cs (renamed from MediaBrowser.Server.Implementations/HttpServer/SwaggerService.cs)18
-rw-r--r--Emby.Server.Implementations/IO/FileRefresher.cs (renamed from MediaBrowser.Server.Implementations/IO/FileRefresher.cs)45
-rw-r--r--Emby.Server.Implementations/IO/MbLinkShortcutHandler.cs (renamed from MediaBrowser.Server.Startup.Common/MbLinkShortcutHandler.cs)8
-rw-r--r--Emby.Server.Implementations/IO/ThrottledStream.cs (renamed from MediaBrowser.Controller/IO/ThrottledStream.cs)7
-rw-r--r--Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs (renamed from MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs)18
-rw-r--r--Emby.Server.Implementations/Intros/DefaultIntroProvider.cs384
-rw-r--r--Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs (renamed from MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs)8
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs (renamed from MediaBrowser.Server.Implementations/Library/LibraryManager.cs)248
-rw-r--r--Emby.Server.Implementations/Library/LocalTrailerPostScanTask.cs (renamed from MediaBrowser.Server.Implementations/Library/LocalTrailerPostScanTask.cs)2
-rw-r--r--Emby.Server.Implementations/Library/MediaSourceManager.cs (renamed from MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs)15
-rw-r--r--Emby.Server.Implementations/Library/MusicManager.cs (renamed from MediaBrowser.Server.Implementations/Library/MusicManager.cs)2
-rw-r--r--Emby.Server.Implementations/Library/PathExtensions.cs (renamed from MediaBrowser.Server.Implementations/Library/PathExtensions.cs)2
-rw-r--r--Emby.Server.Implementations/Library/ResolverHelper.cs (renamed from MediaBrowser.Server.Implementations/Library/ResolverHelper.cs)8
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs (renamed from MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs)14
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs (renamed from MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs)11
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs (renamed from MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs)8
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs (renamed from MediaBrowser.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs)49
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs77
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/FolderResolver.cs (renamed from MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs)2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs (renamed from MediaBrowser.Server.Implementations/Library/Resolvers/ItemResolver.cs)2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs (renamed from MediaBrowser.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs)2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs (renamed from MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs)39
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs (renamed from MediaBrowser.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs)4
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs103
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs (renamed from MediaBrowser.Server.Implementations/Library/Resolvers/PlaylistResolver.cs)2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs (renamed from MediaBrowser.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs)8
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs (renamed from MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs)2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs (renamed from MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs)2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs (renamed from MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs)13
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs (renamed from MediaBrowser.Server.Implementations/Library/Resolvers/VideoResolver.cs)2
-rw-r--r--Emby.Server.Implementations/Library/SearchEngine.cs (renamed from MediaBrowser.Server.Implementations/Library/SearchEngine.cs)9
-rw-r--r--Emby.Server.Implementations/Library/UserDataManager.cs (renamed from MediaBrowser.Server.Implementations/Library/UserDataManager.cs)11
-rw-r--r--Emby.Server.Implementations/Library/UserManager.cs (renamed from MediaBrowser.Server.Implementations/Library/UserManager.cs)54
-rw-r--r--Emby.Server.Implementations/Library/UserViewManager.cs (renamed from MediaBrowser.Server.Implementations/Library/UserViewManager.cs)34
-rw-r--r--Emby.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs (renamed from MediaBrowser.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs)2
-rw-r--r--Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs (renamed from MediaBrowser.Server.Implementations/Library/Validators/ArtistsValidator.cs)2
-rw-r--r--Emby.Server.Implementations/Library/Validators/GameGenresPostScanTask.cs (renamed from MediaBrowser.Server.Implementations/Library/Validators/GameGenresPostScanTask.cs)2
-rw-r--r--Emby.Server.Implementations/Library/Validators/GameGenresValidator.cs (renamed from MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs)2
-rw-r--r--Emby.Server.Implementations/Library/Validators/GenresPostScanTask.cs (renamed from MediaBrowser.Server.Implementations/Library/Validators/GenresPostScanTask.cs)2
-rw-r--r--Emby.Server.Implementations/Library/Validators/GenresValidator.cs (renamed from MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs)2
-rw-r--r--Emby.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs (renamed from MediaBrowser.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs)2
-rw-r--r--Emby.Server.Implementations/Library/Validators/MusicGenresValidator.cs (renamed from MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs)2
-rw-r--r--Emby.Server.Implementations/Library/Validators/PeopleValidator.cs (renamed from MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs)69
-rw-r--r--Emby.Server.Implementations/Library/Validators/StudiosPostScanTask.cs (renamed from MediaBrowser.Server.Implementations/Library/Validators/StudiosPostScanTask.cs)2
-rw-r--r--Emby.Server.Implementations/Library/Validators/StudiosValidator.cs (renamed from MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs)2
-rw-r--r--Emby.Server.Implementations/Library/Validators/YearsPostScanTask.cs (renamed from MediaBrowser.Server.Implementations/Library/Validators/YearsPostScanTask.cs)2
-rw-r--r--Emby.Server.Implementations/LiveTv/ChannelImageProvider.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/ChannelImageProvider.cs)2
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs)14
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs)622
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTVRegistration.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTVRegistration.cs)2
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs)53
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs)2
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs)2
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs)12
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs)44
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs)6
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs)17
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs1311
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs)52
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/LiveStreamHelper.cs)6
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs)2
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs)189
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs)169
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs)13
-rw-r--r--Emby.Server.Implementations/LiveTv/ProgramImageProvider.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/ProgramImageProvider.cs)16
-rw-r--r--Emby.Server.Implementations/LiveTv/RecordingImageProvider.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/RecordingImageProvider.cs)2
-rw-r--r--Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs)24
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs)12
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs)3
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs)31
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs)13
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs)16
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs276
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs)6
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs)12
-rw-r--r--Emby.Server.Implementations/Localization/Core/ar.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/ar.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/bg-BG.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/bg-BG.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/ca.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/ca.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/core.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/core.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/cs.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/cs.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/da.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/da.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/de.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/de.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/el.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/el.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/en-GB.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/en-GB.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/en-US.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/en-US.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/es-AR.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/es-AR.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/es-MX.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/es-MX.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/es.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/es.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/fi.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/fi.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/fr-CA.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/fr-CA.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/fr.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/fr.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/gsw.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/gsw.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/he.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/he.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/hr.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/hr.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/hu.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/hu.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/id.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/id.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/it.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/it.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/kk.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/kk.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/ko.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/ko.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/ms.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/ms.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/nb.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/nb.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/nl.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/nl.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/pl.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/pl.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt-BR.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/pt-BR.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt-PT.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/pt-PT.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/ro.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/ro.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/ru.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/ru.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/sl-SI.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/sl-SI.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/sv.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/sv.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/tr.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/tr.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/uk.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/uk.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/vi.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/vi.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-CN.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/zh-CN.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-HK.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/zh-HK.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-TW.json (renamed from MediaBrowser.Server.Implementations/Localization/Core/zh-TW.json)0
-rw-r--r--Emby.Server.Implementations/Localization/LocalizationManager.cs (renamed from MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs)67
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/au.txt (renamed from MediaBrowser.Server.Implementations/Localization/Ratings/au.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/be.txt (renamed from MediaBrowser.Server.Implementations/Localization/Ratings/be.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/br.txt (renamed from MediaBrowser.Server.Implementations/Localization/Ratings/br.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/ca.txt (renamed from MediaBrowser.Server.Implementations/Localization/Ratings/ca.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/co.txt (renamed from MediaBrowser.Server.Implementations/Localization/Ratings/co.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/de.txt (renamed from MediaBrowser.Server.Implementations/Localization/Ratings/de.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/dk.txt (renamed from MediaBrowser.Server.Implementations/Localization/Ratings/dk.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/fr.txt (renamed from MediaBrowser.Server.Implementations/Localization/Ratings/fr.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/gb.txt (renamed from MediaBrowser.Server.Implementations/Localization/Ratings/gb.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/ie.txt (renamed from MediaBrowser.Server.Implementations/Localization/Ratings/ie.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/jp.txt (renamed from MediaBrowser.Server.Implementations/Localization/Ratings/jp.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/kz.txt (renamed from MediaBrowser.Server.Implementations/Localization/Ratings/kz.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/mx.txt (renamed from MediaBrowser.Server.Implementations/Localization/Ratings/mx.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/nl.txt (renamed from MediaBrowser.Server.Implementations/Localization/Ratings/nl.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/nz.txt (renamed from MediaBrowser.Server.Implementations/Localization/Ratings/nz.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/ru.txt (renamed from MediaBrowser.Server.Implementations/Localization/Ratings/ru.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/us.txt (renamed from MediaBrowser.Server.Implementations/Localization/Ratings/us.txt)0
-rw-r--r--Emby.Server.Implementations/Localization/countries.json (renamed from MediaBrowser.Server.Implementations/Localization/countries.json)0
-rw-r--r--Emby.Server.Implementations/Localization/iso6392.txt (renamed from MediaBrowser.Server.Implementations/Localization/iso6392.txt)0
-rw-r--r--Emby.Server.Implementations/MediaEncoder/EncodingManager.cs (renamed from MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs)12
-rw-r--r--Emby.Server.Implementations/Migrations/IVersionMigration.cs (renamed from MediaBrowser.Server.Startup.Common/Migrations/IVersionMigration.cs)2
-rw-r--r--Emby.Server.Implementations/Migrations/LibraryScanMigration.cs49
-rw-r--r--Emby.Server.Implementations/Migrations/UpdateLevelMigration.cs (renamed from MediaBrowser.Server.Startup.Common/Migrations/UpdateLevelMigration.cs)4
-rw-r--r--Emby.Server.Implementations/News/NewsEntryPoint.cs275
-rw-r--r--Emby.Server.Implementations/News/NewsService.cs (renamed from MediaBrowser.Server.Implementations/News/NewsService.cs)7
-rw-r--r--Emby.Server.Implementations/Notifications/CoreNotificationTypes.cs (renamed from MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs)4
-rw-r--r--Emby.Server.Implementations/Notifications/IConfigurableNotificationService.cs (renamed from MediaBrowser.Server.Implementations/Notifications/IConfigurableNotificationService.cs)2
-rw-r--r--Emby.Server.Implementations/Notifications/InternalNotificationService.cs (renamed from MediaBrowser.Server.Implementations/Notifications/InternalNotificationService.cs)2
-rw-r--r--Emby.Server.Implementations/Notifications/NotificationConfigurationFactory.cs (renamed from MediaBrowser.Server.Implementations/Notifications/NotificationConfigurationFactory.cs)2
-rw-r--r--Emby.Server.Implementations/Notifications/NotificationManager.cs (renamed from MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs)2
-rw-r--r--Emby.Server.Implementations/Notifications/Notifications.cs (renamed from MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs)15
-rw-r--r--Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs337
-rw-r--r--Emby.Server.Implementations/Notifications/WebSocketNotifier.cs (renamed from MediaBrowser.Server.Implementations/EntryPoints/Notifications/WebSocketNotifier.cs)2
-rw-r--r--Emby.Server.Implementations/Photos/PhotoAlbumImageProvider.cs (renamed from MediaBrowser.Server.Implementations/Photos/PhotoAlbumImageProvider.cs)5
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs (renamed from MediaBrowser.Server.Implementations/Playlists/PlaylistImageProvider.cs)10
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistManager.cs (renamed from MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs)13
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistsDynamicFolder.cs32
-rw-r--r--Emby.Server.Implementations/Properties/AssemblyInfo.cs30
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs (renamed from MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs)45
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/PeopleValidationTask.cs (renamed from MediaBrowser.Server.Implementations/ScheduledTasks/PeopleValidationTask.cs)28
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs (renamed from MediaBrowser.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs)17
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/RefreshIntrosTask.cs (renamed from MediaBrowser.Server.Implementations/ScheduledTasks/RefreshIntrosTask.cs)6
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs (renamed from MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs)27
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/SystemUpdateTask.cs (renamed from MediaBrowser.Server.Implementations/ScheduledTasks/SystemUpdateTask.cs)15
-rw-r--r--Emby.Server.Implementations/Security/AuthenticationRepository.cs318
-rw-r--r--Emby.Server.Implementations/Security/EncryptionManager.cs (renamed from MediaBrowser.Server.Implementations/Security/EncryptionManager.cs)4
-rw-r--r--Emby.Server.Implementations/Security/MBLicenseFile.cs214
-rw-r--r--Emby.Server.Implementations/Security/PluginSecurityManager.cs (renamed from MediaBrowser.Common.Implementations/Security/PluginSecurityManager.cs)64
-rw-r--r--Emby.Server.Implementations/Security/RegRecord.cs (renamed from MediaBrowser.Common.Implementations/Security/RegRecord.cs)2
-rw-r--r--Emby.Server.Implementations/ServerManager/ServerManager.cs (renamed from MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs)36
-rw-r--r--Emby.Server.Implementations/ServerManager/WebSocketConnection.cs (renamed from MediaBrowser.Server.Implementations/ServerManager/WebSocketConnection.cs)17
-rw-r--r--Emby.Server.Implementations/Session/HttpSessionController.cs (renamed from MediaBrowser.Server.Implementations/Session/HttpSessionController.cs)2
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs (renamed from MediaBrowser.Server.Implementations/Session/SessionManager.cs)62
-rw-r--r--Emby.Server.Implementations/Session/SessionWebSocketListener.cs (renamed from MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs)5
-rw-r--r--Emby.Server.Implementations/Session/WebSocketController.cs (renamed from MediaBrowser.Server.Implementations/Session/WebSocketController.cs)2
-rw-r--r--Emby.Server.Implementations/Social/SharingManager.cs (renamed from MediaBrowser.Server.Implementations/Social/SharingManager.cs)7
-rw-r--r--Emby.Server.Implementations/Social/SharingRepository.cs116
-rw-r--r--Emby.Server.Implementations/Sorting/AirTimeComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/AirTimeComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/AlbumArtistComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/AlbumArtistComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/AlbumComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/AlbumComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/AlphanumComparator.cs (renamed from MediaBrowser.Server.Implementations/Sorting/AlphanumComparator.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/ArtistComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/ArtistComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/BudgetComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/BudgetComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/CommunityRatingComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/CommunityRatingComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/CriticRatingComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/CriticRatingComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/DateCreatedComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/DateCreatedComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/DatePlayedComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/GameSystemComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/GameSystemComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/IsFolderComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/IsFolderComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/IsPlayedComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/IsPlayedComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/IsUnplayedComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/IsUnplayedComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/MetascoreComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/MetascoreComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/NameComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/NameComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/OfficialRatingComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/OfficialRatingComparer.cs)4
-rw-r--r--Emby.Server.Implementations/Sorting/PlayCountComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/PlayCountComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/PlayersComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/PlayersComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/PremiereDateComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/PremiereDateComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/ProductionYearComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/ProductionYearComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/RandomComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/RandomComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/RevenueComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/RevenueComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/RuntimeComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/RuntimeComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/SeriesSortNameComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/SortNameComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/SortNameComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/StartDateComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/StartDateComparer.cs)2
-rw-r--r--Emby.Server.Implementations/Sorting/StudioComparer.cs (renamed from MediaBrowser.Server.Implementations/Sorting/StudioComparer.cs)2
-rw-r--r--Emby.Server.Implementations/StartupOptions.cs (renamed from MediaBrowser.Server.Startup.Common/StartupOptions.cs)9
-rw-r--r--Emby.Server.Implementations/Sync/AppSyncProvider.cs (renamed from MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs)2
-rw-r--r--Emby.Server.Implementations/Sync/CloudSyncProfile.cs (renamed from MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs)2
-rw-r--r--Emby.Server.Implementations/Sync/IHasSyncQuality.cs (renamed from MediaBrowser.Server.Implementations/Sync/IHasSyncQuality.cs)2
-rw-r--r--Emby.Server.Implementations/Sync/MediaSync.cs (renamed from MediaBrowser.Server.Implementations/Sync/MediaSync.cs)27
-rw-r--r--Emby.Server.Implementations/Sync/MultiProviderSync.cs (renamed from MediaBrowser.Server.Implementations/Sync/MultiProviderSync.cs)13
-rw-r--r--Emby.Server.Implementations/Sync/ServerSyncScheduledTask.cs (renamed from MediaBrowser.Server.Implementations/Sync/ServerSyncScheduledTask.cs)35
-rw-r--r--Emby.Server.Implementations/Sync/SyncConfig.cs (renamed from MediaBrowser.Server.Implementations/Sync/SyncConfig.cs)2
-rw-r--r--Emby.Server.Implementations/Sync/SyncConvertScheduledTask.cs (renamed from MediaBrowser.Server.Implementations/Sync/SyncConvertScheduledTask.cs)35
-rw-r--r--Emby.Server.Implementations/Sync/SyncHelper.cs (renamed from MediaBrowser.Server.Implementations/Sync/SyncHelper.cs)2
-rw-r--r--Emby.Server.Implementations/Sync/SyncJobOptions.cs (renamed from MediaBrowser.Server.Implementations/Sync/SyncJobOptions.cs)2
-rw-r--r--Emby.Server.Implementations/Sync/SyncJobProcessor.cs (renamed from MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs)10
-rw-r--r--Emby.Server.Implementations/Sync/SyncManager.cs (renamed from MediaBrowser.Server.Implementations/Sync/SyncManager.cs)26
-rw-r--r--Emby.Server.Implementations/Sync/SyncNotificationEntryPoint.cs (renamed from MediaBrowser.Server.Implementations/Sync/SyncNotificationEntryPoint.cs)2
-rw-r--r--Emby.Server.Implementations/Sync/SyncRegistrationInfo.cs (renamed from MediaBrowser.Server.Implementations/Sync/SyncRegistrationInfo.cs)2
-rw-r--r--Emby.Server.Implementations/Sync/SyncRepository.cs820
-rw-r--r--Emby.Server.Implementations/Sync/SyncedMediaSourceProvider.cs (renamed from MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs)4
-rw-r--r--Emby.Server.Implementations/Sync/TargetDataProvider.cs (renamed from MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs)19
-rw-r--r--Emby.Server.Implementations/TV/SeriesPostScanTask.cs (renamed from MediaBrowser.Providers/TV/SeriesPostScanTask.cs)51
-rw-r--r--Emby.Server.Implementations/TV/TVSeriesManager.cs (renamed from MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs)129
-rw-r--r--Emby.Server.Implementations/Udp/UdpServer.cs (renamed from MediaBrowser.Server.Implementations/Udp/UdpServer.cs)141
-rw-r--r--Emby.Server.Implementations/Updates/InstallationManager.cs (renamed from MediaBrowser.Common.Implementations/Updates/InstallationManager.cs)59
-rw-r--r--Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs (renamed from MediaBrowser.Server.Implementations/UserViews/CollectionFolderImageProvider.cs)10
-rw-r--r--Emby.Server.Implementations/UserViews/DynamicImageProvider.cs (renamed from MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs)36
-rw-r--r--Emby.Server.Implementations/packages.config8
-rw-r--r--Emby.Server.sln291
-rw-r--r--MediaBrowser.Api/ApiEntryPoint.cs54
-rw-r--r--MediaBrowser.Api/BaseApiService.cs102
-rw-r--r--MediaBrowser.Api/BasePeriodicWebSocketListener.cs (renamed from MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs)36
-rw-r--r--MediaBrowser.Api/BrandingService.cs2
-rw-r--r--MediaBrowser.Api/ChannelService.cs2
-rw-r--r--MediaBrowser.Api/ConfigurationService.cs7
-rw-r--r--MediaBrowser.Api/ConnectService.cs8
-rw-r--r--MediaBrowser.Api/Devices/DeviceService.cs3
-rw-r--r--MediaBrowser.Api/DisplayPreferencesService.cs2
-rw-r--r--MediaBrowser.Api/Dlna/DlnaServerService.cs59
-rw-r--r--MediaBrowser.Api/Dlna/DlnaService.cs2
-rw-r--r--MediaBrowser.Api/EnvironmentService.cs44
-rw-r--r--MediaBrowser.Api/FilterService.cs2
-rw-r--r--MediaBrowser.Api/GamesService.cs9
-rw-r--r--MediaBrowser.Api/Images/ImageByNameService.cs27
-rw-r--r--MediaBrowser.Api/Images/ImageRequest.cs4
-rw-r--r--MediaBrowser.Api/Images/ImageService.cs21
-rw-r--r--MediaBrowser.Api/Images/RemoteImageService.cs29
-rw-r--r--MediaBrowser.Api/ItemLookupService.cs41
-rw-r--r--MediaBrowser.Api/ItemRefreshService.cs6
-rw-r--r--MediaBrowser.Api/ItemUpdateService.cs4
-rw-r--r--MediaBrowser.Api/Library/FileOrganizationService.cs6
-rw-r--r--MediaBrowser.Api/Library/LibraryService.cs64
-rw-r--r--MediaBrowser.Api/Library/LibraryStructureService.cs8
-rw-r--r--MediaBrowser.Api/LiveTv/LiveTvService.cs47
-rw-r--r--MediaBrowser.Api/LocalizationService.cs5
-rw-r--r--MediaBrowser.Api/MediaBrowser.Api.csproj41
-rw-r--r--MediaBrowser.Api/MediaBrowser.Api.nuget.targets6
-rw-r--r--MediaBrowser.Api/Movies/CollectionService.cs10
-rw-r--r--MediaBrowser.Api/Movies/MoviesService.cs27
-rw-r--r--MediaBrowser.Api/Movies/TrailersService.cs14
-rw-r--r--MediaBrowser.Api/Music/AlbumsService.cs10
-rw-r--r--MediaBrowser.Api/Music/InstantMixService.cs8
-rw-r--r--MediaBrowser.Api/NewsService.cs5
-rw-r--r--MediaBrowser.Api/NotificationsService.cs2
-rw-r--r--MediaBrowser.Api/PackageReviewService.cs2
-rw-r--r--MediaBrowser.Api/PackageService.cs2
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs235
-rw-r--r--MediaBrowser.Api/Playback/Hls/BaseHlsService.cs35
-rw-r--r--MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs36
-rw-r--r--MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs20
-rw-r--r--MediaBrowser.Api/Playback/Hls/VideoHlsService.cs17
-rw-r--r--MediaBrowser.Api/Playback/MediaInfoService.cs10
-rw-r--r--MediaBrowser.Api/Playback/Progressive/AudioService.cs15
-rw-r--r--MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs35
-rw-r--r--MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs16
-rw-r--r--MediaBrowser.Api/Playback/Progressive/VideoService.cs23
-rw-r--r--MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs16
-rw-r--r--MediaBrowser.Api/Playback/StreamRequest.cs3
-rw-r--r--MediaBrowser.Api/Playback/StreamState.cs1
-rw-r--r--MediaBrowser.Api/Playback/TranscodingThrottler.cs16
-rw-r--r--MediaBrowser.Api/PlaylistService.cs8
-rw-r--r--MediaBrowser.Api/PluginService.cs3
-rw-r--r--MediaBrowser.Api/Reports/Common/ReportViewType.cs1
-rw-r--r--MediaBrowser.Api/Reports/ReportRequests.cs15
-rw-r--r--MediaBrowser.Api/Reports/ReportsService.cs34
-rw-r--r--MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs19
-rw-r--r--MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs13
-rw-r--r--MediaBrowser.Api/SearchService.cs2
-rw-r--r--MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs7
-rw-r--r--MediaBrowser.Api/Session/SessionsService.cs29
-rw-r--r--MediaBrowser.Api/SimilarItemsHelper.cs2
-rw-r--r--MediaBrowser.Api/Social/SharingService.cs11
-rw-r--r--MediaBrowser.Api/StartupWizardService.cs5
-rw-r--r--MediaBrowser.Api/Subtitles/SubtitleService.cs12
-rw-r--r--MediaBrowser.Api/Sync/SyncJobWebSocketListener.cs8
-rw-r--r--MediaBrowser.Api/Sync/SyncJobsWebSocketListener.cs8
-rw-r--r--MediaBrowser.Api/Sync/SyncService.cs8
-rw-r--r--MediaBrowser.Api/System/ActivityLogService.cs5
-rw-r--r--MediaBrowser.Api/System/ActivityLogWebSocketListener.cs8
-rw-r--r--MediaBrowser.Api/System/SystemInfoWebSocketListener.cs7
-rw-r--r--MediaBrowser.Api/System/SystemService.cs10
-rw-r--r--MediaBrowser.Api/TestService.cs77
-rw-r--r--MediaBrowser.Api/TvShowsService.cs16
-rw-r--r--MediaBrowser.Api/UserLibrary/ArtistsService.cs22
-rw-r--r--MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs11
-rw-r--r--MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs2
-rw-r--r--MediaBrowser.Api/UserLibrary/GameGenresService.cs15
-rw-r--r--MediaBrowser.Api/UserLibrary/GenresService.cs15
-rw-r--r--MediaBrowser.Api/UserLibrary/ItemsService.cs10
-rw-r--r--MediaBrowser.Api/UserLibrary/MusicGenresService.cs15
-rw-r--r--MediaBrowser.Api/UserLibrary/PersonsService.cs22
-rw-r--r--MediaBrowser.Api/UserLibrary/PlaystateService.cs20
-rw-r--r--MediaBrowser.Api/UserLibrary/StudiosService.cs15
-rw-r--r--MediaBrowser.Api/UserLibrary/UserLibraryService.cs29
-rw-r--r--MediaBrowser.Api/UserLibrary/UserViewsService.cs14
-rw-r--r--MediaBrowser.Api/UserLibrary/YearsService.cs15
-rw-r--r--MediaBrowser.Api/UserService.cs24
-rw-r--r--MediaBrowser.Api/VideosService.cs12
-rw-r--r--MediaBrowser.Api/packages.config6
-rw-r--r--MediaBrowser.Api/project.json17
-rw-r--r--MediaBrowser.Common.Implementations/Logging/NlogManager.cs264
-rw-r--r--MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj153
-rw-r--r--MediaBrowser.Common.Implementations/Security/MBLicenseFile.cs157
-rw-r--r--MediaBrowser.Common.Implementations/Security/MbAdmin.cs13
-rw-r--r--MediaBrowser.Common.Implementations/Security/SuppporterInfoResponse.cs14
-rw-r--r--MediaBrowser.Common.Implementations/packages.config9
-rw-r--r--MediaBrowser.Common/Configuration/IApplicationPaths.cs6
-rw-r--r--MediaBrowser.Common/Extensions/BaseExtensions.cs19
-rw-r--r--MediaBrowser.Common/IApplicationHost.cs2
-rw-r--r--MediaBrowser.Common/MediaBrowser.Common.csproj37
-rw-r--r--MediaBrowser.Common/MediaBrowser.Common.nuget.targets6
-rw-r--r--MediaBrowser.Common/Net/HttpRequestOptions.cs9
-rw-r--r--MediaBrowser.Common/Net/HttpResponseInfo.cs6
-rw-r--r--MediaBrowser.Common/Net/INetworkManager.cs27
-rw-r--r--MediaBrowser.Common/Plugins/BasePlugin.cs123
-rw-r--r--MediaBrowser.Common/Plugins/IPlugin.cs14
-rw-r--r--MediaBrowser.Common/Properties/AssemblyInfo.cs3
-rw-r--r--MediaBrowser.Common/ScheduledTasks/IHasKey.cs7
-rw-r--r--MediaBrowser.Common/ScheduledTasks/ScheduledTaskHelpers.cs194
-rw-r--r--MediaBrowser.Common/Threading/PeriodicTimer.cs72
-rw-r--r--MediaBrowser.Common/Updates/GithubUpdater.cs (renamed from MediaBrowser.Common.Implementations/Updates/GithubUpdater.cs)2
-rw-r--r--MediaBrowser.Common/Updates/IInstallationManager.cs3
-rw-r--r--MediaBrowser.Common/project.json17
-rw-r--r--MediaBrowser.Controller/Channels/Channel.cs2
-rw-r--r--MediaBrowser.Controller/Dlna/ControlRequest.cs3
-rw-r--r--MediaBrowser.Controller/Dlna/IDeviceDiscovery.cs20
-rw-r--r--MediaBrowser.Controller/Dlna/SsdpMessageEventArgs.cs23
-rw-r--r--MediaBrowser.Controller/Drawing/IImageEncoder.cs (renamed from Emby.Drawing/IImageEncoder.cs)5
-rw-r--r--MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs2
-rw-r--r--MediaBrowser.Controller/Entities/AggregateFolder.cs5
-rw-r--r--MediaBrowser.Controller/Entities/Audio/Audio.cs23
-rw-r--r--MediaBrowser.Controller/Entities/Audio/AudioPodcast.cs12
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs8
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicArtist.cs4
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicGenre.cs13
-rw-r--r--MediaBrowser.Controller/Entities/AudioBook.cs64
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs105
-rw-r--r--MediaBrowser.Controller/Entities/BasePluginFolder.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Book.cs8
-rw-r--r--MediaBrowser.Controller/Entities/CollectionFolder.cs83
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs105
-rw-r--r--MediaBrowser.Controller/Entities/Game.cs2
-rw-r--r--MediaBrowser.Controller/Entities/GameGenre.cs13
-rw-r--r--MediaBrowser.Controller/Entities/GameSystem.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Genre.cs13
-rw-r--r--MediaBrowser.Controller/Entities/IHasImages.cs4
-rw-r--r--MediaBrowser.Controller/Entities/IHasOriginalTitle.cs8
-rw-r--r--MediaBrowser.Controller/Entities/IHasSeries.cs2
-rw-r--r--MediaBrowser.Controller/Entities/IHasUserData.cs6
-rw-r--r--MediaBrowser.Controller/Entities/IThemeMedia.cs8
-rw-r--r--MediaBrowser.Controller/Entities/InternalItemsQuery.cs4
-rw-r--r--MediaBrowser.Controller/Entities/ItemImageInfo.cs2
-rw-r--r--MediaBrowser.Controller/Entities/LinkedChild.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Movies/BoxSet.cs7
-rw-r--r--MediaBrowser.Controller/Entities/Movies/Movie.cs21
-rw-r--r--MediaBrowser.Controller/Entities/MusicVideo.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Person.cs8
-rw-r--r--MediaBrowser.Controller/Entities/Photo.cs2
-rw-r--r--MediaBrowser.Controller/Entities/PhotoAlbum.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Studio.cs13
-rw-r--r--MediaBrowser.Controller/Entities/TV/Episode.cs36
-rw-r--r--MediaBrowser.Controller/Entities/TV/Season.cs24
-rw-r--r--MediaBrowser.Controller/Entities/TV/Series.cs113
-rw-r--r--MediaBrowser.Controller/Entities/Trailer.cs18
-rw-r--r--MediaBrowser.Controller/Entities/User.cs1
-rw-r--r--MediaBrowser.Controller/Entities/UserItemData.cs2
-rw-r--r--MediaBrowser.Controller/Entities/UserRootFolder.cs2
-rw-r--r--MediaBrowser.Controller/Entities/UserView.cs2
-rw-r--r--MediaBrowser.Controller/Entities/UserViewBuilder.cs12
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs24
-rw-r--r--MediaBrowser.Controller/Entities/Year.cs11
-rw-r--r--MediaBrowser.Controller/Extensions/StringExtensions.cs17
-rw-r--r--MediaBrowser.Controller/IO/FileData.cs3
-rw-r--r--MediaBrowser.Controller/IServerApplicationHost.cs13
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs12
-rw-r--r--MediaBrowser.Controller/Library/IUserDataManager.cs6
-rw-r--r--MediaBrowser.Controller/Library/ItemResolveArgs.cs16
-rw-r--r--MediaBrowser.Controller/Library/NameExtensions.cs19
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvManager.cs3
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs11
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvChannel.cs11
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvProgram.cs39
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs2
-rw-r--r--MediaBrowser.Controller/LiveTv/RecordingGroup.cs2
-rw-r--r--MediaBrowser.Controller/LiveTv/TimerInfo.cs4
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj60
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.nuget.targets6
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs3
-rw-r--r--MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs3
-rw-r--r--MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs3
-rw-r--r--MediaBrowser.Controller/Net/AuthenticatedAttribute.cs20
-rw-r--r--MediaBrowser.Controller/Net/IAsyncStreamSource.cs18
-rw-r--r--MediaBrowser.Controller/Net/IHasAuthorization.cs12
-rw-r--r--MediaBrowser.Controller/Net/IHasResultFactory.cs2
-rw-r--r--MediaBrowser.Controller/Net/IHasSession.cs12
-rw-r--r--MediaBrowser.Controller/Net/IHttpResultFactory.cs10
-rw-r--r--MediaBrowser.Controller/Net/IHttpServer.cs13
-rw-r--r--MediaBrowser.Controller/Net/IRestfulService.cs12
-rw-r--r--MediaBrowser.Controller/Net/IServerManager.cs4
-rw-r--r--MediaBrowser.Controller/Net/IServiceRequest.cs7
-rw-r--r--MediaBrowser.Controller/Net/IWebSocketConnection.cs4
-rw-r--r--MediaBrowser.Controller/Net/LoggedAttribute.cs44
-rw-r--r--MediaBrowser.Controller/Net/ServiceRequest.cs42
-rw-r--r--MediaBrowser.Controller/Net/ServiceStackServiceRequest.cs62
-rw-r--r--MediaBrowser.Controller/Net/StaticResultOptions.cs6
-rw-r--r--MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs7
-rw-r--r--MediaBrowser.Controller/Persistence/IItemRepository.cs6
-rw-r--r--MediaBrowser.Controller/Playlists/Playlist.cs2
-rw-r--r--MediaBrowser.Controller/Properties/AssemblyInfo.cs3
-rw-r--r--MediaBrowser.Controller/Providers/DirectoryService.cs6
-rw-r--r--MediaBrowser.Controller/Providers/IDirectoryService.cs4
-rw-r--r--MediaBrowser.Controller/Providers/ImageRefreshOptions.cs1
-rw-r--r--MediaBrowser.Controller/Providers/LocalImageInfo.cs4
-rw-r--r--MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs4
-rw-r--r--MediaBrowser.Controller/Resolvers/IItemResolver.cs4
-rw-r--r--MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs4
-rw-r--r--MediaBrowser.Controller/Session/SessionInfo.cs5
-rw-r--r--MediaBrowser.Controller/Sync/IServerSyncProvider.cs12
-rw-r--r--MediaBrowser.Controller/TV/ITVSeriesManager.cs2
-rw-r--r--MediaBrowser.Controller/packages.config7
-rw-r--r--MediaBrowser.Controller/project.json17
-rw-r--r--MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs289
-rw-r--r--MediaBrowser.Dlna/Server/Headers.cs176
-rw-r--r--MediaBrowser.Dlna/Service/BaseControlHandler.cs137
-rw-r--r--MediaBrowser.Dlna/Service/ControlErrorHandler.cs41
-rw-r--r--MediaBrowser.Dlna/packages.config6
-rw-r--r--MediaBrowser.LocalMetadata/BaseXmlProvider.cs8
-rw-r--r--MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs6
-rw-r--r--MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs6
-rw-r--r--MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj31
-rw-r--r--MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.nuget.targets6
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs (renamed from MediaBrowser.Controller/Providers/BaseItemXmlParser.cs)397
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs30
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs9
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs11
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs11
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/MovieXmlParser.cs16
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/MusicVideoXmlParser.cs15
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs83
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs15
-rw-r--r--MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs11
-rw-r--r--MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs11
-rw-r--r--MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs12
-rw-r--r--MediaBrowser.LocalMetadata/Providers/GameSystemXmlProvider.cs11
-rw-r--r--MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs11
-rw-r--r--MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs11
-rw-r--r--MediaBrowser.LocalMetadata/Providers/MusicVideoXmlProvider.cs11
-rw-r--r--MediaBrowser.LocalMetadata/Providers/PersonXmlProvider.cs32
-rw-r--r--MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs11
-rw-r--r--MediaBrowser.LocalMetadata/Providers/SeriesXmlProvider.cs11
-rw-r--r--MediaBrowser.LocalMetadata/Providers/VideoXmlProvider.cs11
-rw-r--r--MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs752
-rw-r--r--MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs66
-rw-r--r--MediaBrowser.LocalMetadata/Savers/FolderXmlSaver.cs61
-rw-r--r--MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs77
-rw-r--r--MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs92
-rw-r--r--MediaBrowser.LocalMetadata/Savers/PersonXmlSaver.cs133
-rw-r--r--MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs86
-rw-r--r--MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs647
-rw-r--r--MediaBrowser.LocalMetadata/packages.config5
-rw-r--r--MediaBrowser.LocalMetadata/project.json17
-rw-r--r--MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs13
-rw-r--r--MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs6
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs5
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs44
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs54
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs1
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs7
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs14
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs317
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs5
-rw-r--r--MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj47
-rw-r--r--MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.nuget.targets6
-rw-r--r--MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs173
-rw-r--r--MediaBrowser.MediaEncoding/Probing/whitelist.txt1
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/ConfigurationExtension.cs (renamed from MediaBrowser.Providers/Subtitles/ConfigurationExtension.cs)6
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs (renamed from MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs)67
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs190
-rw-r--r--MediaBrowser.MediaEncoding/packages.config4
-rw-r--r--MediaBrowser.MediaEncoding/project.json17
-rw-r--r--MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj1227
-rw-r--r--MediaBrowser.Model.Portable/app.config15
-rw-r--r--MediaBrowser.Model.net35/FodyWeavers.xml3
-rw-r--r--MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj1181
-rw-r--r--MediaBrowser.Model.net35/MediaBrowser.Model.snkbin596 -> 0 bytes
-rw-r--r--MediaBrowser.Model/Activity/IActivityManager.cs (renamed from MediaBrowser.Controller/Activity/IActivityManager.cs)7
-rw-r--r--MediaBrowser.Model/Activity/IActivityRepository.cs (renamed from MediaBrowser.Controller/Activity/IActivityRepository.cs)7
-rw-r--r--MediaBrowser.Model/Configuration/LibraryOptions.cs3
-rw-r--r--MediaBrowser.Model/Configuration/PeopleMetadataOptions.cs19
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs27
-rw-r--r--MediaBrowser.Model/Configuration/UserConfiguration.cs1
-rw-r--r--MediaBrowser.Model/Cryptography/ICryptoProvider.cs13
-rw-r--r--MediaBrowser.Model/Devices/DeviceQuery.cs5
-rw-r--r--MediaBrowser.Model/Diagnostics/IProcess.cs19
-rw-r--r--MediaBrowser.Model/Diagnostics/IProcessFactory.cs24
-rw-r--r--MediaBrowser.Model/Dlna/CodecProfile.cs1
-rw-r--r--MediaBrowser.Model/Dlna/ConditionProcessor.cs31
-rw-r--r--MediaBrowser.Model/Dlna/ContainerProfile.cs9
-rw-r--r--MediaBrowser.Model/Dlna/DeviceProfile.cs51
-rw-r--r--MediaBrowser.Model/Dlna/DirectPlayProfile.cs1
-rw-r--r--MediaBrowser.Model/Dlna/HttpHeaderInfo.cs1
-rw-r--r--MediaBrowser.Model/Dlna/IDeviceDiscovery.cs11
-rw-r--r--MediaBrowser.Model/Dlna/ProfileCondition.cs1
-rw-r--r--MediaBrowser.Model/Dlna/ResolutionNormalizer.cs3
-rw-r--r--MediaBrowser.Model/Dlna/ResponseProfile.cs1
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs51
-rw-r--r--MediaBrowser.Model/Dlna/StreamInfo.cs3
-rw-r--r--MediaBrowser.Model/Dlna/SubtitleProfile.cs1
-rw-r--r--MediaBrowser.Model/Dlna/TranscodingProfile.cs1
-rw-r--r--MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs14
-rw-r--r--MediaBrowser.Model/Drawing/ImageSize.cs6
-rw-r--r--MediaBrowser.Model/Dto/BaseItemDto.cs4
-rw-r--r--MediaBrowser.Model/Dto/BaseItemPerson.cs2
-rw-r--r--MediaBrowser.Model/Dto/ChapterInfoDto.cs2
-rw-r--r--MediaBrowser.Model/Dto/MediaSourceInfo.cs2
-rw-r--r--MediaBrowser.Model/Dto/StudioDto.cs2
-rw-r--r--MediaBrowser.Model/Dto/UserDto.cs2
-rw-r--r--MediaBrowser.Model/Entities/BaseItemInfo.cs4
-rw-r--r--MediaBrowser.Model/Entities/MediaStream.cs14
-rw-r--r--MediaBrowser.Model/Extensions/BoolHelper.cs16
-rw-r--r--MediaBrowser.Model/Extensions/DoubleHelper.cs21
-rw-r--r--MediaBrowser.Model/Extensions/FloatHelper.cs18
-rw-r--r--MediaBrowser.Model/Extensions/IntHelper.cs21
-rw-r--r--MediaBrowser.Model/Extensions/LinqExtensions.cs84
-rw-r--r--MediaBrowser.Model/Globalization/ILocalizationManager.cs (renamed from MediaBrowser.Controller/Localization/ILocalizationManager.cs)11
-rw-r--r--MediaBrowser.Model/Health/IHealthMonitor.cs (renamed from MediaBrowser.Controller/Health/IHealthMonitor.cs)2
-rw-r--r--MediaBrowser.Model/IO/FileSystemMetadata.cs56
-rw-r--r--MediaBrowser.Model/IO/IFileSystem.cs421
-rw-r--r--MediaBrowser.Model/IO/IMemoryStreamFactory.cs (renamed from MediaBrowser.Common/IO/IMemoryStreamProvider.cs)5
-rw-r--r--MediaBrowser.Model/IO/IShortcutHandler.cs25
-rw-r--r--MediaBrowser.Model/IO/StreamDefaults.cs (renamed from MediaBrowser.Common/IO/StreamDefaults.cs)2
-rw-r--r--MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs13
-rw-r--r--MediaBrowser.Model/LiveTv/ChannelInfoDto.cs2
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvOptions.cs8
-rw-r--r--MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs2
-rw-r--r--MediaBrowser.Model/LiveTv/TimerQuery.cs2
-rw-r--r--MediaBrowser.Model/Logging/LogHelper.cs (renamed from MediaBrowser.Common.Implementations/Logging/LogHelper.cs)6
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj128
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.nuget.targets6
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.snkbin596 -> 0 bytes
-rw-r--r--MediaBrowser.Model/Net/ISocket.cs28
-rw-r--r--MediaBrowser.Model/Net/ISocketFactory.cs43
-rw-r--r--MediaBrowser.Model/Net/IUdpSocket.cs27
-rw-r--r--MediaBrowser.Model/Net/IpAddressInfo.cs42
-rw-r--r--MediaBrowser.Model/Net/IpEndPointInfo.cs30
-rw-r--r--MediaBrowser.Model/Net/SocketReceiveResult.cs25
-rw-r--r--MediaBrowser.Model/News/INewsService.cs (renamed from MediaBrowser.Controller/News/INewsService.cs)5
-rw-r--r--MediaBrowser.Model/Plugins/IHasWebPages.cs9
-rw-r--r--MediaBrowser.Model/Plugins/PluginPageInfo.cs9
-rw-r--r--MediaBrowser.Model/PropertyChanged.xml54
-rw-r--r--MediaBrowser.Model/Querying/ItemFields.cs4
-rw-r--r--MediaBrowser.Model/Reflection/IAssemblyInfo.cs14
-rw-r--r--MediaBrowser.Model/Serialization/IXmlSerializer.cs2
-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.cs46
-rw-r--r--MediaBrowser.Model/Services/IHttpResponse.cs25
-rw-r--r--MediaBrowser.Model/Services/IHttpResult.cs42
-rw-r--r--MediaBrowser.Model/Services/IRequest.cs156
-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.cs195
-rw-r--r--MediaBrowser.Model/Services/RouteAttribute.cs144
-rw-r--r--MediaBrowser.Model/Session/ClientCapabilities.cs2
-rw-r--r--MediaBrowser.Model/Social/ISharingManager.cs (renamed from MediaBrowser.Controller/Social/ISharingManager.cs)5
-rw-r--r--MediaBrowser.Model/Social/ISharingRepository.cs15
-rw-r--r--MediaBrowser.Model/System/Architecture.cs3
-rw-r--r--MediaBrowser.Model/System/IEnvironmentInfo.cs22
-rw-r--r--MediaBrowser.Model/System/IPowerManagement.cs9
-rw-r--r--MediaBrowser.Model/System/ISystemEvents.cs12
-rw-r--r--MediaBrowser.Model/System/SystemInfo.cs6
-rw-r--r--MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs (renamed from MediaBrowser.Common/ScheduledTasks/IConfigurableScheduledTask.cs)7
-rw-r--r--MediaBrowser.Model/Tasks/IScheduledTask.cs (renamed from MediaBrowser.Common/ScheduledTasks/IScheduledTask.cs)6
-rw-r--r--MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs (renamed from MediaBrowser.Common/ScheduledTasks/IScheduledTaskWorker.cs)12
-rw-r--r--MediaBrowser.Model/Tasks/ITaskManager.cs (renamed from MediaBrowser.Common/ScheduledTasks/ITaskManager.cs)8
-rw-r--r--MediaBrowser.Model/Tasks/ITaskTrigger.cs (renamed from MediaBrowser.Common/ScheduledTasks/ITaskTrigger.cs)9
-rw-r--r--MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs52
-rw-r--r--MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs (renamed from MediaBrowser.Common/ScheduledTasks/TaskCompletionEventArgs.cs)5
-rw-r--r--MediaBrowser.Model/Tasks/TaskExecutionOptions.cs (renamed from MediaBrowser.Common/ScheduledTasks/TaskExecutionOptions.cs)2
-rw-r--r--MediaBrowser.Model/Tasks/TaskTriggerInfo.cs6
-rw-r--r--MediaBrowser.Model/Text/ITextEncoding.cs10
-rw-r--r--MediaBrowser.Model/Threading/ITimer.cs10
-rw-r--r--MediaBrowser.Model/Threading/ITimerFactory.cs10
-rw-r--r--MediaBrowser.Model/Updates/PackageVersionInfo.cs2
-rw-r--r--MediaBrowser.Model/Xml/IXmlReaderSettingsFactory.cs9
-rw-r--r--MediaBrowser.Model/project.json17
-rw-r--r--MediaBrowser.Mono.sln281
-rw-r--r--MediaBrowser.Providers/Books/AudioBookMetadataService.cs41
-rw-r--r--MediaBrowser.Providers/Books/AudioPodcastMetadataService.cs41
-rw-r--r--MediaBrowser.Providers/Books/BookMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs3
-rw-r--r--MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs9
-rw-r--r--MediaBrowser.Providers/Channels/ChannelMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/Folders/FolderMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/Folders/UserViewMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/GameGenres/GameGenreImageProvider.cs144
-rw-r--r--MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/Games/GameMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/Games/GameSystemMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/Genres/GenreImageProvider.cs143
-rw-r--r--MediaBrowser.Providers/Genres/GenreMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/ImagesByName/ImageUtils.cs27
-rw-r--r--MediaBrowser.Providers/LiveTv/AudioRecordingService.cs4
-rw-r--r--MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/LiveTv/VideoRecordingService.cs4
-rw-r--r--MediaBrowser.Providers/Manager/ImageSaver.cs30
-rw-r--r--MediaBrowser.Providers/Manager/ItemImageProvider.cs20
-rw-r--r--MediaBrowser.Providers/Manager/MetadataService.cs57
-rw-r--r--MediaBrowser.Providers/Manager/ProviderManager.cs78
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.csproj64
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.nuget.targets6
-rw-r--r--MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs55
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs6
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs28
-rw-r--r--MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs2
-rw-r--r--MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs21
-rw-r--r--MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs1
-rw-r--r--MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs14
-rw-r--r--MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs18
-rw-r--r--MediaBrowser.Providers/Movies/MovieDbImageProvider.cs10
-rw-r--r--MediaBrowser.Providers/Movies/MovieDbProvider.cs10
-rw-r--r--MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs3
-rw-r--r--MediaBrowser.Providers/Movies/MovieMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/Music/AlbumMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/Music/ArtistMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs3
-rw-r--r--MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs7
-rw-r--r--MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs3
-rw-r--r--MediaBrowser.Providers/Music/AudioDbArtistProvider.cs9
-rw-r--r--MediaBrowser.Providers/Music/AudioMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/Music/FanArtAlbumProvider.cs9
-rw-r--r--MediaBrowser.Providers/Music/FanArtArtistProvider.cs14
-rw-r--r--MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs390
-rw-r--r--MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs220
-rw-r--r--MediaBrowser.Providers/Music/MusicVideoMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/Omdb/OmdbImageProvider.cs8
-rw-r--r--MediaBrowser.Providers/Omdb/OmdbItemProvider.cs10
-rw-r--r--MediaBrowser.Providers/Omdb/OmdbProvider.cs92
-rw-r--r--MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs3
-rw-r--r--MediaBrowser.Providers/People/MovieDbPersonProvider.cs11
-rw-r--r--MediaBrowser.Providers/People/PersonMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/People/TvdbPersonImageProvider.cs97
-rw-r--r--MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/Photos/PhotoHelper.cs98
-rw-r--r--MediaBrowser.Providers/Photos/PhotoMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/Studios/StudioMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/Studios/StudiosImageProvider.cs6
-rw-r--r--MediaBrowser.Providers/Subtitles/SubtitleManager.cs6
-rw-r--r--MediaBrowser.Providers/TV/DummySeasonProvider.cs6
-rw-r--r--MediaBrowser.Providers/TV/EpisodeMetadataService.cs60
-rw-r--r--MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs9
-rw-r--r--MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs14
-rw-r--r--MediaBrowser.Providers/TV/MissingEpisodeProvider.cs155
-rw-r--r--MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs6
-rw-r--r--MediaBrowser.Providers/TV/SeasonMetadataService.cs25
-rw-r--r--MediaBrowser.Providers/TV/SeriesMetadataService.cs6
-rw-r--r--MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeImageProvider.cs6
-rw-r--r--MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs24
-rw-r--r--MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs6
-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.cs9
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs132
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs1219
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs49
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs86
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs84
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs613
-rw-r--r--MediaBrowser.Providers/Users/UserMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/Videos/VideoMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/Years/YearMetadataService.cs4
-rw-r--r--MediaBrowser.Providers/packages.config8
-rw-r--r--MediaBrowser.Providers/project.json17
-rw-r--r--MediaBrowser.Server.Implementations/Activity/ActivityRepository.cs253
-rw-r--r--MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs31
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/AsyncStreamWriter.cs59
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/ContainerAdapter.cs53
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs676
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/Security/SessionAuthProvider.cs35
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs33
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/ServerLogFactory.cs46
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/ServerLogger.cs194
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/SocketSharp/Extensions.cs28
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs151
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs167
-rw-r--r--MediaBrowser.Server.Implementations/Logging/PatternsLogger.cs63
-rw-r--r--MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj721
-rw-r--r--MediaBrowser.Server.Implementations/News/NewsEntryPoint.cs168
-rw-r--r--MediaBrowser.Server.Implementations/Notifications/SqliteNotificationsRepository.cs470
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/BaseSqliteRepository.cs114
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/DataExtensions.cs181
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/IDbConnector.cs10
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs408
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs312
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs53
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/SqliteFileOrganizationRepository.cs408
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs453
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs237
-rw-r--r--MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs41
-rw-r--r--MediaBrowser.Server.Implementations/Security/AuthenticationRepository.cs317
-rw-r--r--MediaBrowser.Server.Implementations/Social/SharingRepository.cs158
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/VideoBitRateComparer.cs41
-rw-r--r--MediaBrowser.Server.Implementations/Sync/SyncRepository.cs976
-rw-r--r--MediaBrowser.Server.Implementations/Udp/UdpMessageReceivedEventArgs.cs21
-rw-r--r--MediaBrowser.Server.Implementations/app.config2
-rw-r--r--MediaBrowser.Server.Implementations/packages.config12
-rw-r--r--MediaBrowser.Server.Implementations/project.json17
-rw-r--r--MediaBrowser.Server.Mac.sln673
-rw-r--r--MediaBrowser.Server.Mac/AppController.cs2
-rw-r--r--MediaBrowser.Server.Mac/Emby.Server.Mac.csproj393
-rw-r--r--MediaBrowser.Server.Mac/MacAppHost.cs135
-rw-r--r--MediaBrowser.Server.Mac/Main.cs189
-rw-r--r--MediaBrowser.Server.Mac/MenuBarIcon.cs3
-rw-r--r--MediaBrowser.Server.Mac/Native/BaseMonoApp.cs272
-rw-r--r--MediaBrowser.Server.Mac/Native/DbConnector.cs24
-rw-r--r--MediaBrowser.Server.Mac/Native/MonoFileSystem.cs21
-rw-r--r--MediaBrowser.Server.Mac/Native/NativeApp.cs46
-rw-r--r--MediaBrowser.Server.Mac/Native/NetworkManager.cs50
-rw-r--r--MediaBrowser.Server.Mac/Native/PowerManagement.cs15
-rw-r--r--MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj141
-rw-r--r--MediaBrowser.Server.Mono/MonoAppHost.cs154
-rw-r--r--MediaBrowser.Server.Mono/Native/BaseMonoApp.cs281
-rw-r--r--MediaBrowser.Server.Mono/Native/DbConnector.cs22
-rw-r--r--MediaBrowser.Server.Mono/Native/MonoFileSystem.cs22
-rw-r--r--MediaBrowser.Server.Mono/Native/NativeApp.cs45
-rw-r--r--MediaBrowser.Server.Mono/Native/PowerManagement.cs15
-rw-r--r--MediaBrowser.Server.Mono/Networking/NetworkManager.cs49
-rw-r--r--MediaBrowser.Server.Mono/Program.cs173
-rw-r--r--MediaBrowser.Server.Mono/SQLitePCLRaw.provider.sqlite3.dll.config3
-rw-r--r--MediaBrowser.Server.Mono/app.config18
-rw-r--r--MediaBrowser.Server.Mono/packages.config8
-rw-r--r--MediaBrowser.Server.Startup.Common/Cryptography/ASN1.cs (renamed from MediaBrowser.Server.Mono/Security/ASN1.cs)2
-rw-r--r--MediaBrowser.Server.Startup.Common/Cryptography/ASN1Convert.cs (renamed from MediaBrowser.Server.Mono/Security/ASN1Convert.cs)9
-rw-r--r--MediaBrowser.Server.Startup.Common/Cryptography/BitConverterLE.cs (renamed from MediaBrowser.Server.Mono/Security/BitConverterLE.cs)4
-rw-r--r--MediaBrowser.Server.Startup.Common/Cryptography/CertificateGenerator.cs (renamed from MediaBrowser.Server.Mono/Networking/CertificateGenerator.cs)7
-rw-r--r--MediaBrowser.Server.Startup.Common/Cryptography/CryptoConvert.cs (renamed from MediaBrowser.Server.Mono/Security/CryptoConvert.cs)3
-rw-r--r--MediaBrowser.Server.Startup.Common/Cryptography/PKCS1.cs (renamed from MediaBrowser.Server.Mono/Security/PKCS1.cs)11
-rw-r--r--MediaBrowser.Server.Startup.Common/Cryptography/PKCS12.cs (renamed from MediaBrowser.Server.Mono/Security/PKCS12.cs)3
-rw-r--r--MediaBrowser.Server.Startup.Common/Cryptography/PKCS7.cs (renamed from MediaBrowser.Server.Mono/Security/PKCS7.cs)3
-rw-r--r--MediaBrowser.Server.Startup.Common/Cryptography/PKCS8.cs (renamed from MediaBrowser.Server.Mono/Security/PKCS8.cs)3
-rw-r--r--MediaBrowser.Server.Startup.Common/Cryptography/PfxGenerator.cs75
-rw-r--r--MediaBrowser.Server.Startup.Common/Cryptography/X501Name.cs (renamed from MediaBrowser.Server.Mono/Security/X501Name.cs)15
-rw-r--r--MediaBrowser.Server.Startup.Common/Cryptography/X509Builder.cs (renamed from MediaBrowser.Server.Mono/Security/X509Builder.cs)5
-rw-r--r--MediaBrowser.Server.Startup.Common/Cryptography/X509Certificate.cs (renamed from MediaBrowser.Server.Mono/Security/X509Certificate.cs)3
-rw-r--r--MediaBrowser.Server.Startup.Common/Cryptography/X509CertificateBuilder.cs (renamed from MediaBrowser.Server.Mono/Security/X509CertificateBuilder.cs)9
-rw-r--r--MediaBrowser.Server.Startup.Common/Cryptography/X509CertificateCollection.cs (renamed from MediaBrowser.Server.Mono/Security/X509CertificateCollection.cs)5
-rw-r--r--MediaBrowser.Server.Startup.Common/Cryptography/X509Extension.cs (renamed from MediaBrowser.Server.Mono/Security/X509Extension.cs)5
-rw-r--r--MediaBrowser.Server.Startup.Common/Cryptography/X509Extensions.cs (renamed from MediaBrowser.Server.Mono/Security/X509Extensions.cs)5
-rw-r--r--MediaBrowser.Server.Startup.Common/Cryptography/X520Attributes.cs (renamed from MediaBrowser.Server.Mono/Security/X520Attributes.cs)27
-rw-r--r--MediaBrowser.Server.Startup.Common/EntryPoints/KeepServerAwake.cs60
-rw-r--r--MediaBrowser.Server.Startup.Common/INativeApp.cs106
-rw-r--r--MediaBrowser.Server.Startup.Common/IO/MemoryStreamProvider.cs (renamed from MediaBrowser.Common.Implementations/IO/MemoryStreamProvider.cs)20
-rw-r--r--MediaBrowser.Server.Startup.Common/ImageEncoderHelper.cs48
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ChannelScan.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ChannelScan.cs)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/ReportBlock.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/ReportBlock.cs)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/RtcpAppPacket.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpAppPacket.cs)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/RtcpByePacket.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpByePacket.cs)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/RtcpListener.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpListener.cs)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/RtcpPacket.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpPacket.cs)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/RtcpReceiverReportPacket.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpReceiverReportPacket.cs)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSenderReportPacket.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSenderReportPacket.cs)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSourceDescriptionPacket.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSourceDescriptionPacket.cs)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionBlock.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionBlock.cs)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionItem.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionItem.cs)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtp/RtpListener.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtp/RtpListener.cs)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtp/RtpPacket.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtp/RtpPacket.cs)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtsp/RtspMethod.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspMethod.cs)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtsp/RtspRequest.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspRequest.cs)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtsp/RtspResponse.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspResponse.cs)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtsp/RtspSession.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspSession.cs)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtsp/RtspStatusCode.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspStatusCode.cs)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs)1
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/SatIpHost.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs)6
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/TransmissionMode.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/TransmissionMode.cs)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Utils.cs (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Utils.cs)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0030.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0030.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0049.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0049.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0070.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0070.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0090.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0090.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0100.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0100.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0130.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0130.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0160.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0160.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0170.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0170.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0192.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0192.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0200.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0200.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0215.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0215.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0235.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0235.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0255.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0255.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0260.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0260.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0282.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0282.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0305.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0305.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0308.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0308.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0310.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0310.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0315.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0315.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0330.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0330.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0360.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0360.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0380.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0380.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0390.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0390.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0400.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0400.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0420.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0420.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0435.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0435.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0450.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0450.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0460.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0460.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0475.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0475.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0480.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0480.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0490.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0490.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0505.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0505.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0510.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0510.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0520.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0520.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0525.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0525.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0530.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0530.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0549.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0549.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0560.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0560.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0570.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0570.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0600.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0600.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0620.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0620.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0642.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0642.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0650.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0650.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0660.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0660.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0685.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0685.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0705.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0705.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0721.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0721.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0740.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0740.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0750.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0750.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0765.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0765.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0785.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0785.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0830.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0830.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0851.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0851.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0865.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0865.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0875.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0875.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0880.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0880.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0900.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0900.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0915.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0915.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0922.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0922.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0935.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0935.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0950.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0950.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0965.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0965.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1005.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1005.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1030.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1030.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1055.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1055.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1082.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1082.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1100.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1100.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1105.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1105.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1130.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1130.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1155.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1155.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1160.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1160.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1180.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1180.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1195.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1195.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1222.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1222.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1240.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1240.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1250.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1250.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1280.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1280.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1320.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1320.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1340.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1340.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1380.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1380.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1400.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1400.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1440.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1440.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1500.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1500.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1520.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1520.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1540.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1540.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1560.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1560.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1590.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1590.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1600 OPTUS D1 FTA (160.0E).ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1600 OPTUS D1 FTA (160.0E).ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1600.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1600.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1620.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1620.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1640.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1640.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1660.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1660.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1690.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1690.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1720.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1720.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1800.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1800.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1830.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1830.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2210.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2210.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2230.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2230.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2250.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2250.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2270.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2270.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2290.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2290.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2310.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2310.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2330.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2330.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2350.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2350.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2370.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2370.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2390.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2390.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2410.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2410.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2432.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2432.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2451.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2451.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2470.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2470.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2489.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2489.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2500.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2500.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2527.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2527.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2550.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2550.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2570.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2570.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2590.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2590.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2608.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2608.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2630.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2630.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2650.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2650.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2669.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2669.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2690.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2690.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2710.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2710.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2728.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2728.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2730.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2730.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2750.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2750.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2760.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2760.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2770.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2770.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2780.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2780.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2812.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2812.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2820.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2820.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2830.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2830.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2850.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2850.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2873.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2873.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2880.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2880.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2881.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2881.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2882.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2882.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2900.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2900.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2930.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2930.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2950.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2950.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2970.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2970.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2985.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2985.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2990.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2990.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3020.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3020.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3045.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3045.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3070.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3070.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3100.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3100.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3125.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3125.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3150.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3150.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3169.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3169.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3195.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3195.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3225.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3225.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3255.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3255.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3285.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3285.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3300.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3300.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3325.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3325.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3355.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3355.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3380.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3380.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3400.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3400.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3420.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3420.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3450.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3450.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3460.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3460.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3475.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3475.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3490.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3490.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3520.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3520.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3527.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3527.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3550.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3550.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3560.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3560.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3592.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3592.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3594.ini (renamed from MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3594.ini)0
-rw-r--r--MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj305
-rw-r--r--MediaBrowser.Server.Startup.Common/Migrations/DbMigration.cs62
-rw-r--r--MediaBrowser.Server.Startup.Common/Migrations/MovieDbEpisodeProviderMigration.cs44
-rw-r--r--MediaBrowser.Server.Startup.Common/NativeEnvironment.cs19
-rw-r--r--MediaBrowser.Server.Startup.Common/SystemEvents.cs50
-rw-r--r--MediaBrowser.Server.Startup.Common/app.config11
-rw-r--r--MediaBrowser.Server.Startup.Common/packages.config5
-rw-r--r--MediaBrowser.ServerApplication/App.config64
-rw-r--r--MediaBrowser.ServerApplication/MainForm.Designer.cs58
-rw-r--r--MediaBrowser.ServerApplication/MainStartup.cs121
-rw-r--r--MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj117
-rw-r--r--MediaBrowser.ServerApplication/Native/DbConnector.cs22
-rw-r--r--MediaBrowser.ServerApplication/Native/LnkShortcutHandler.cs4
-rw-r--r--MediaBrowser.ServerApplication/Native/PowerManagement.cs17
-rw-r--r--MediaBrowser.ServerApplication/Native/RegisterServer.bat3
-rw-r--r--MediaBrowser.ServerApplication/Native/WindowsApp.cs262
-rw-r--r--MediaBrowser.ServerApplication/Networking/CertificateGenerator.cs244
-rw-r--r--MediaBrowser.ServerApplication/Networking/NetworkManager.cs20
-rw-r--r--MediaBrowser.ServerApplication/ServerNotifyIcon.cs4
-rw-r--r--MediaBrowser.ServerApplication/Updates/ApplicationUpdater.cs19
-rw-r--r--MediaBrowser.ServerApplication/WindowsAppHost.cs215
-rw-r--r--MediaBrowser.ServerApplication/packages.config9
-rw-r--r--MediaBrowser.Tests/MediaBrowser.Tests.csproj5
-rw-r--r--MediaBrowser.Tests/app.config8
-rw-r--r--MediaBrowser.WebDashboard/Api/ConfigurationPageInfo.cs12
-rw-r--r--MediaBrowser.WebDashboard/Api/DashboardService.cs125
-rw-r--r--MediaBrowser.WebDashboard/Api/PackageCreator.cs193
-rw-r--r--MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj52
-rw-r--r--MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.nuget.targets6
-rw-r--r--MediaBrowser.WebDashboard/ServerEntryPoint.cs5
-rw-r--r--MediaBrowser.WebDashboard/packages.config3
-rw-r--r--MediaBrowser.WebDashboard/project.json17
-rw-r--r--MediaBrowser.XbmcMetadata/EntryPoint.cs2
-rw-r--r--MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj24
-rw-r--r--MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.nuget.targets6
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs292
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs10
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs11
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs10
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs10
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs11
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs11
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs6
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs11
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs11
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs13
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs11
-rw-r--r--MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs13
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs13
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs13
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs131
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs13
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs15
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs13
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs15
-rw-r--r--MediaBrowser.XbmcMetadata/packages.config2
-rw-r--r--MediaBrowser.XbmcMetadata/project.json17
-rw-r--r--MediaBrowser.sln846
-rw-r--r--Mono.Nat/AbstractNatDevice.cs45
-rw-r--r--Mono.Nat/Exceptions/MappingException.cs19
-rw-r--r--Mono.Nat/IMapper.cs50
-rw-r--r--Mono.Nat/INatDevice.cs21
-rw-r--r--Mono.Nat/Mono.Nat.xproj23
-rw-r--r--Mono.Nat/NatUtility.cs113
-rw-r--r--Mono.Nat/Pmp/Mappers/PmpMapper.cs83
-rw-r--r--Mono.Nat/Pmp/Pmp.cs118
-rw-r--r--Mono.Nat/Pmp/PmpNatDevice.cs405
-rw-r--r--Mono.Nat/Pmp/Searchers/PmpSearcher.cs92
-rw-r--r--Mono.Nat/Properties/AssemblyInfo.cs21
-rw-r--r--Mono.Nat/Upnp/AsyncResults/GetAllMappingsAsyncResult.cs56
-rw-r--r--Mono.Nat/Upnp/AsyncResults/PortMapAsyncResult.cs75
-rw-r--r--Mono.Nat/Upnp/Mappers/UpnpMapper.cs110
-rw-r--r--Mono.Nat/Upnp/Messages/ErrorMessage.cs4
-rw-r--r--Mono.Nat/Upnp/Messages/GetServicesMessage.cs29
-rw-r--r--Mono.Nat/Upnp/Messages/Requests/CreatePortMappingMessage.cs6
-rw-r--r--Mono.Nat/Upnp/Messages/Requests/DeletePortMappingMessage.cs57
-rw-r--r--Mono.Nat/Upnp/Messages/Requests/GetExternalIPAddressMessage.cs51
-rw-r--r--Mono.Nat/Upnp/Messages/Requests/GetGenericPortMappingEntry.cs55
-rw-r--r--Mono.Nat/Upnp/Messages/Requests/GetSpecificPortMappingEntryMessage.cs60
-rw-r--r--Mono.Nat/Upnp/Messages/Responses/CreatePortMappingResponseMessage.cs4
-rw-r--r--Mono.Nat/Upnp/Messages/Responses/DeletePortMappingResponseMessage.cs44
-rw-r--r--Mono.Nat/Upnp/Messages/Responses/GetExternalIPAddressResponseMessage.cs53
-rw-r--r--Mono.Nat/Upnp/Messages/Responses/GetGenericPortMappingEntryResponseMessage.cs108
-rw-r--r--Mono.Nat/Upnp/Messages/UpnpMessage.cs61
-rw-r--r--Mono.Nat/Upnp/Searchers/UpnpSearcher.cs198
-rw-r--r--Mono.Nat/Upnp/Upnp.cs17
-rw-r--r--Mono.Nat/Upnp/UpnpNatDevice.cs644
-rw-r--r--Mono.Nat/project.json41
-rw-r--r--Nuget/MediaBrowser.Common.Internal.nuspec8
-rw-r--r--Nuget/MediaBrowser.Common.nuspec12
-rw-r--r--Nuget/MediaBrowser.Server.Core.nuspec10
-rw-r--r--OpenSubtitlesHandler/Interfaces/IMethodResponse.cs30
-rw-r--r--OpenSubtitlesHandler/MovieHasher.cs42
-rw-r--r--OpenSubtitlesHandler/OpenSubtitlesHandler.csproj23
-rw-r--r--OpenSubtitlesHandler/OpenSubtitlesHandler.nuget.targets6
-rw-r--r--OpenSubtitlesHandler/Utilities.cs78
-rw-r--r--OpenSubtitlesHandler/XML-RPC/XmlRpcGenerator.cs126
-rw-r--r--OpenSubtitlesHandler/project.json17
-rw-r--r--RSSDP/CustomHttpHeaders.cs295
-rw-r--r--RSSDP/DeviceAvailableEventArgs.cs61
-rw-r--r--RSSDP/DeviceEventArgs.cs49
-rw-r--r--RSSDP/DeviceUnavailableEventArgs.cs60
-rw-r--r--RSSDP/DiscoveredSsdpDevice.cs171
-rw-r--r--RSSDP/DisposableManagedObjectBase.cs76
-rw-r--r--RSSDP/GlobalSuppressions.csbin0 -> 7686 bytes
-rw-r--r--RSSDP/HttpParserBase.cs244
-rw-r--r--RSSDP/HttpRequestParser.cs91
-rw-r--r--RSSDP/HttpResponseParser.cs93
-rw-r--r--RSSDP/IEnumerableExtensions.cs28
-rw-r--r--RSSDP/ISsdpCommunicationsServer.cs72
-rw-r--r--RSSDP/ISsdpDeviceLocator.cs146
-rw-r--r--RSSDP/ISsdpDevicePublisher.cs37
-rw-r--r--RSSDP/IUPnPDeviceValidator.cs27
-rw-r--r--RSSDP/Properties/AssemblyInfo.cs30
-rw-r--r--RSSDP/RSSDP.csproj89
-rw-r--r--RSSDP/RSSDP.nuget.targets6
-rw-r--r--RSSDP/RequestReceivedEventArgs.cs62
-rw-r--r--RSSDP/ResponseReceivedEventArgs.cs59
-rw-r--r--RSSDP/SsdpCommunicationsServer.cs499
-rw-r--r--RSSDP/SsdpConstants.cs68
-rw-r--r--RSSDP/SsdpDevice.cs783
-rw-r--r--RSSDP/SsdpDeviceExtensions.cs37
-rw-r--r--RSSDP/SsdpDeviceIcon.cs51
-rw-r--r--RSSDP/SsdpDeviceLocator.cs36
-rw-r--r--RSSDP/SsdpDeviceLocatorBase.cs716
-rw-r--r--RSSDP/SsdpDeviceProperties.cs206
-rw-r--r--RSSDP/SsdpDeviceProperty.cs36
-rw-r--r--RSSDP/SsdpDevicePublisher.cs38
-rw-r--r--RSSDP/SsdpDevicePublisherBase.cs708
-rw-r--r--RSSDP/SsdpEmbeddedDevice.cs69
-rw-r--r--RSSDP/SsdpHelper.cs88
-rw-r--r--RSSDP/SsdpRootDevice.cs176
-rw-r--r--RSSDP/UPnP10DeviceValidator.cs194
-rw-r--r--RSSDP/project.json17
-rw-r--r--ServiceStack/FilterAttributeCache.cs27
-rw-r--r--ServiceStack/Host/ActionContext.cs27
-rw-r--r--ServiceStack/Host/ContentTypes.cs63
-rw-r--r--ServiceStack/Host/RestHandler.cs176
-rw-r--r--ServiceStack/Host/RestPath.cs448
-rw-r--r--ServiceStack/Host/ServiceController.cs217
-rw-r--r--ServiceStack/Host/ServiceExec.cs156
-rw-r--r--ServiceStack/Host/ServiceMetadata.cs27
-rw-r--r--ServiceStack/HttpHandlerFactory.cs32
-rw-r--r--ServiceStack/HttpRequestExtensions.cs127
-rw-r--r--ServiceStack/HttpResponseExtensionsInternal.cs190
-rw-r--r--ServiceStack/HttpResult.cs61
-rw-r--r--ServiceStack/HttpUtils.cs34
-rw-r--r--ServiceStack/Properties/AssemblyInfo.cs25
-rw-r--r--ServiceStack/ReflectionExtensions.cs270
-rw-r--r--ServiceStack/ServiceStack.csproj130
-rw-r--r--ServiceStack/ServiceStack.nuget.targets6
-rw-r--r--ServiceStack/ServiceStackHost.Runtime.cs57
-rw-r--r--ServiceStack/ServiceStackHost.cs104
-rw-r--r--ServiceStack/StringMapTypeDeserializer.cs126
-rw-r--r--ServiceStack/UrlExtensions.cs33
-rw-r--r--ServiceStack/packages.config (renamed from MediaBrowser.Model.Portable/FodyWeavers.xml)4
-rw-r--r--ServiceStack/project.json17
-rw-r--r--SocketHttpListener.Portable/ByteOrder.cs17
-rw-r--r--SocketHttpListener.Portable/CloseEventArgs.cs90
-rw-r--r--SocketHttpListener.Portable/CloseStatusCode.cs94
-rw-r--r--SocketHttpListener.Portable/CompressionMethod.cs23
-rw-r--r--SocketHttpListener.Portable/ErrorEventArgs.cs46
-rw-r--r--SocketHttpListener.Portable/Ext.cs1083
-rw-r--r--SocketHttpListener.Portable/Fin.cs8
-rw-r--r--SocketHttpListener.Portable/HttpBase.cs104
-rw-r--r--SocketHttpListener.Portable/HttpResponse.cs161
-rw-r--r--SocketHttpListener.Portable/Mask.cs8
-rw-r--r--SocketHttpListener.Portable/MessageEventArgs.cs96
-rw-r--r--SocketHttpListener.Portable/Net/AuthenticationSchemeSelector.cs6
-rw-r--r--SocketHttpListener.Portable/Net/ChunkStream.cs371
-rw-r--r--SocketHttpListener.Portable/Net/ChunkedInputStream.cs160
-rw-r--r--SocketHttpListener.Portable/Net/CookieHelper.cs144
-rw-r--r--SocketHttpListener.Portable/Net/EndPointListener.cs383
-rw-r--r--SocketHttpListener.Portable/Net/EndPointManager.cs165
-rw-r--r--SocketHttpListener.Portable/Net/HttpConnection.cs551
-rw-r--r--SocketHttpListener.Portable/Net/HttpListener.cs288
-rw-r--r--SocketHttpListener.Portable/Net/HttpListenerBasicIdentity.cs70
-rw-r--r--SocketHttpListener.Portable/Net/HttpListenerContext.cs200
-rw-r--r--SocketHttpListener.Portable/Net/HttpListenerPrefixCollection.cs97
-rw-r--r--SocketHttpListener.Portable/Net/HttpListenerRequest.cs654
-rw-r--r--SocketHttpListener.Portable/Net/HttpListenerResponse.cs512
-rw-r--r--SocketHttpListener.Portable/Net/HttpStatusCode.cs321
-rw-r--r--SocketHttpListener.Portable/Net/HttpStreamAsyncResult.cs77
-rw-r--r--SocketHttpListener.Portable/Net/HttpVersion.cs16
-rw-r--r--SocketHttpListener.Portable/Net/ListenerPrefix.cs148
-rw-r--r--SocketHttpListener.Portable/Net/RequestStream.cs231
-rw-r--r--SocketHttpListener.Portable/Net/ResponseStream.cs293
-rw-r--r--SocketHttpListener.Portable/Net/WebHeaderCollection.cs391
-rw-r--r--SocketHttpListener.Portable/Net/WebSockets/HttpListenerWebSocketContext.cs348
-rw-r--r--SocketHttpListener.Portable/Net/WebSockets/WebSocketContext.cs183
-rw-r--r--SocketHttpListener.Portable/Opcode.cs43
-rw-r--r--SocketHttpListener.Portable/PayloadData.cs149
-rw-r--r--SocketHttpListener.Portable/Primitives/HttpListenerException.cs17
-rw-r--r--SocketHttpListener.Portable/Primitives/ICertificate.cs12
-rw-r--r--SocketHttpListener.Portable/Primitives/IStreamFactory.cs18
-rw-r--r--SocketHttpListener.Portable/Primitives/ITextEncoding.cs17
-rw-r--r--SocketHttpListener.Portable/Properties/AssemblyInfo.cs30
-rw-r--r--SocketHttpListener.Portable/Rsv.cs8
-rw-r--r--SocketHttpListener.Portable/SocketHttpListener.Portable.csproj108
-rw-r--r--SocketHttpListener.Portable/SocketHttpListener.Portable.nuget.targets6
-rw-r--r--SocketHttpListener.Portable/WebSocket.cs887
-rw-r--r--SocketHttpListener.Portable/WebSocketException.cs60
-rw-r--r--SocketHttpListener.Portable/WebSocketFrame.cs578
-rw-r--r--SocketHttpListener.Portable/WebSocketState.cs35
-rw-r--r--SocketHttpListener.Portable/packages.config5
-rw-r--r--SocketHttpListener.Portable/project.json17
-rw-r--r--global.json6
-rw-r--r--src/Emby.Server/ApplicationPathHelper.cs40
-rw-r--r--src/Emby.Server/CoreAppHost.cs133
-rw-r--r--src/Emby.Server/CoreSystemEvents.cs13
-rw-r--r--src/Emby.Server/Emby.Server.xproj46
-rw-r--r--src/Emby.Server/IO/MemoryStreamFactory.cs33
-rw-r--r--src/Emby.Server/PowerManagement.cs15
-rw-r--r--src/Emby.Server/Program.cs346
-rw-r--r--src/Emby.Server/Properties/AssemblyInfo.cs19
-rw-r--r--src/Emby.Server/project.json125
1520 files changed, 60405 insertions, 27875 deletions
diff --git a/.gitignore b/.gitignore
index 1598a7ac36..c74a54d71e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,8 @@ tmp/
*.bak
*.swp
*~.nib
+project.fragment.lock.json
+project.lock.json
local.properties
.classpath
.settings/
diff --git a/BDInfo/BDInfo.csproj b/BDInfo/BDInfo.csproj
new file mode 100644
index 0000000000..e7013f341b
--- /dev/null
+++ b/BDInfo/BDInfo.csproj
@@ -0,0 +1,77 @@
+<?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>
+ <ItemGroup>
+ <None Include="project.json" />
+ <!-- A reference to the entire .NET Framework is automatically included -->
+ </ItemGroup>
+ <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" />
+ </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
diff --git a/BDInfo/BDInfo.nuget.targets b/BDInfo/BDInfo.nuget.targets
new file mode 100644
index 0000000000..e69ce0e64f
--- /dev/null
+++ b/BDInfo/BDInfo.nuget.targets
@@ -0,0 +1,6 @@
+<?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/BDInfoSettings.cs b/BDInfo/BDInfoSettings.cs
new file mode 100644
index 0000000000..7abb67499f
--- /dev/null
+++ b/BDInfo/BDInfoSettings.cs
@@ -0,0 +1,105 @@
+
+namespace BDInfo
+{
+ class BDInfoSettings
+ {
+ public static bool GenerateStreamDiagnostics
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ public static bool EnableSSIF
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ public static bool AutosaveReport
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public static bool GenerateFrameDataFile
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public static bool FilterLoopingPlaylists
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ public static bool FilterShortPlaylists
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public static int FilterShortPlaylistsValue
+ {
+ get
+ {
+ return 0;
+ }
+ }
+
+ public static bool UseImagePrefix
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public static string UseImagePrefixValue
+ {
+ get
+ {
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Setting this to false throws an IComparer error on some discs.
+ /// </summary>
+ public static bool KeepStreamOrder
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ public static bool GenerateTextSummary
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public static string LastPath
+ {
+ get
+ {
+ return string.Empty;
+ }
+ }
+ }
+}
diff --git a/BDInfo/BDROM.cs b/BDInfo/BDROM.cs
new file mode 100644
index 0000000000..97dbfbf3bd
--- /dev/null
+++ b/BDInfo/BDROM.cs
@@ -0,0 +1,437 @@
+//============================================================================
+// BDInfo - Blu-ray Video and Audio Analysis Tool
+// Copyright © 2010 Cinema Squid
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//=============================================================================
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Text;
+
+namespace BDInfo
+{
+ public class BDROM
+ {
+ public FileSystemMetadata DirectoryRoot = null;
+ public FileSystemMetadata DirectoryBDMV = null;
+ public FileSystemMetadata DirectoryBDJO = null;
+ public FileSystemMetadata DirectoryCLIPINF = null;
+ public FileSystemMetadata DirectoryPLAYLIST = null;
+ public FileSystemMetadata DirectorySNP = null;
+ public FileSystemMetadata DirectorySSIF = null;
+ public FileSystemMetadata DirectorySTREAM = null;
+
+ public string VolumeLabel = null;
+ public ulong Size = 0;
+ public bool IsBDPlus = false;
+ public bool IsBDJava = false;
+ public bool IsDBOX = false;
+ public bool IsPSP = false;
+ public bool Is3D = false;
+ public bool Is50Hz = false;
+
+ private readonly IFileSystem _fileSystem;
+
+ public Dictionary<string, TSPlaylistFile> PlaylistFiles =
+ new Dictionary<string, TSPlaylistFile>();
+ public Dictionary<string, TSStreamClipFile> StreamClipFiles =
+ new Dictionary<string, TSStreamClipFile>();
+ public Dictionary<string, TSStreamFile> StreamFiles =
+ new Dictionary<string, TSStreamFile>();
+ 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);
+
+ public event OnStreamClipFileScanError StreamClipFileScanError;
+
+ public delegate bool OnStreamFileScanError(
+ TSStreamFile streamClipFile, Exception ex);
+
+ public event OnStreamFileScanError StreamFileScanError;
+
+ public delegate bool OnPlaylistFileScanError(
+ TSPlaylistFile playlistFile, Exception ex);
+
+ public event OnPlaylistFileScanError PlaylistFileScanError;
+
+ public BDROM(
+ string path, IFileSystem fileSystem, ITextEncoding textEncoding)
+ {
+ _fileSystem = fileSystem;
+ //
+ // Locate BDMV directories.
+ //
+
+ DirectoryBDMV =
+ GetDirectoryBDMV(path);
+
+ if (DirectoryBDMV == null)
+ {
+ throw new Exception("Unable to locate BD structure.");
+ }
+
+ DirectoryRoot =
+ _fileSystem.GetDirectoryInfo(Path.GetDirectoryName(DirectoryBDMV.FullName));
+ DirectoryBDJO =
+ GetDirectory("BDJO", DirectoryBDMV, 0);
+ DirectoryCLIPINF =
+ GetDirectory("CLIPINF", DirectoryBDMV, 0);
+ DirectoryPLAYLIST =
+ GetDirectory("PLAYLIST", DirectoryBDMV, 0);
+ DirectorySNP =
+ GetDirectory("SNP", DirectoryRoot, 0);
+ DirectorySTREAM =
+ GetDirectory("STREAM", DirectoryBDMV, 0);
+ DirectorySSIF =
+ GetDirectory("SSIF", DirectorySTREAM, 0);
+
+ if (DirectoryCLIPINF == null
+ || DirectoryPLAYLIST == null)
+ {
+ throw new Exception("Unable to locate BD structure.");
+ }
+
+ //
+ // Initialize basic disc properties.
+ //
+
+ VolumeLabel = GetVolumeLabel(DirectoryRoot);
+ Size = (ulong)GetDirectorySize(DirectoryRoot);
+
+ if (null != GetDirectory("BDSVM", DirectoryRoot, 0))
+ {
+ IsBDPlus = true;
+ }
+ if (null != GetDirectory("SLYVM", DirectoryRoot, 0))
+ {
+ IsBDPlus = true;
+ }
+ if (null != GetDirectory("ANYVM", DirectoryRoot, 0))
+ {
+ IsBDPlus = true;
+ }
+
+ if (DirectoryBDJO != null &&
+ _fileSystem.GetFiles(DirectoryBDJO.FullName).Any())
+ {
+ IsBDJava = true;
+ }
+
+ if (DirectorySNP != null &&
+ GetFiles(DirectorySNP.FullName, ".mnv").Any())
+ {
+ IsPSP = true;
+ }
+
+ if (DirectorySSIF != null &&
+ _fileSystem.GetFiles(DirectorySSIF.FullName).Any())
+ {
+ Is3D = true;
+ }
+
+ if (_fileSystem.FileExists(Path.Combine(DirectoryRoot.FullName, "FilmIndex.xml")))
+ {
+ IsDBOX = true;
+ }
+
+ //
+ // Initialize file lists.
+ //
+
+ if (DirectoryPLAYLIST != null)
+ {
+ FileSystemMetadata[] files = GetFiles(DirectoryPLAYLIST.FullName, ".mpls").ToArray();
+ foreach (FileSystemMetadata file in files)
+ {
+ PlaylistFiles.Add(
+ file.Name.ToUpper(), new TSPlaylistFile(this, file, _fileSystem, textEncoding));
+ }
+ }
+
+ if (DirectorySTREAM != null)
+ {
+ FileSystemMetadata[] files = GetFiles(DirectorySTREAM.FullName, ".m2ts").ToArray();
+ foreach (FileSystemMetadata file in files)
+ {
+ StreamFiles.Add(
+ file.Name.ToUpper(), new TSStreamFile(file, _fileSystem));
+ }
+ }
+
+ if (DirectoryCLIPINF != null)
+ {
+ FileSystemMetadata[] files = GetFiles(DirectoryCLIPINF.FullName, ".clpi").ToArray();
+ foreach (FileSystemMetadata file in files)
+ {
+ StreamClipFiles.Add(
+ file.Name.ToUpper(), new TSStreamClipFile(file, _fileSystem, textEncoding));
+ }
+ }
+
+ if (DirectorySSIF != null)
+ {
+ FileSystemMetadata[] files = GetFiles(DirectorySSIF.FullName, ".ssif").ToArray();
+ foreach (FileSystemMetadata file in files)
+ {
+ InterleavedFiles.Add(
+ file.Name.ToUpper(), new TSInterleavedFile(file));
+ }
+ }
+ }
+
+ private IEnumerable<FileSystemMetadata> GetFiles(string path, string extension)
+ {
+ return _fileSystem.GetFiles(path).Where(i => string.Equals(i.Extension, extension, StringComparison.OrdinalIgnoreCase));
+ }
+
+ public void Scan()
+ {
+ List<TSStreamClipFile> errorStreamClipFiles = new List<TSStreamClipFile>();
+ foreach (TSStreamClipFile streamClipFile in StreamClipFiles.Values)
+ {
+ try
+ {
+ streamClipFile.Scan();
+ }
+ catch (Exception ex)
+ {
+ errorStreamClipFiles.Add(streamClipFile);
+ if (StreamClipFileScanError != null)
+ {
+ if (StreamClipFileScanError(streamClipFile, ex))
+ {
+ continue;
+ }
+ else
+ {
+ break;
+ }
+ }
+ else throw ex;
+ }
+ }
+
+ foreach (TSStreamFile streamFile in StreamFiles.Values)
+ {
+ string ssifName = Path.GetFileNameWithoutExtension(streamFile.Name) + ".SSIF";
+ if (InterleavedFiles.ContainsKey(ssifName))
+ {
+ streamFile.InterleavedFile = InterleavedFiles[ssifName];
+ }
+ }
+
+ TSStreamFile[] streamFiles = new TSStreamFile[StreamFiles.Count];
+ StreamFiles.Values.CopyTo(streamFiles, 0);
+ Array.Sort(streamFiles, CompareStreamFiles);
+
+ List<TSPlaylistFile> errorPlaylistFiles = new List<TSPlaylistFile>();
+ foreach (TSPlaylistFile playlistFile in PlaylistFiles.Values)
+ {
+ try
+ {
+ playlistFile.Scan(StreamFiles, StreamClipFiles);
+ }
+ catch (Exception ex)
+ {
+ errorPlaylistFiles.Add(playlistFile);
+ if (PlaylistFileScanError != null)
+ {
+ if (PlaylistFileScanError(playlistFile, ex))
+ {
+ continue;
+ }
+ else
+ {
+ break;
+ }
+ }
+ else throw ex;
+ }
+ }
+
+ List<TSStreamFile> errorStreamFiles = new List<TSStreamFile>();
+ foreach (TSStreamFile streamFile in streamFiles)
+ {
+ try
+ {
+ List<TSPlaylistFile> playlists = new List<TSPlaylistFile>();
+ foreach (TSPlaylistFile playlist in PlaylistFiles.Values)
+ {
+ foreach (TSStreamClip streamClip in playlist.StreamClips)
+ {
+ if (streamClip.Name == streamFile.Name)
+ {
+ playlists.Add(playlist);
+ break;
+ }
+ }
+ }
+ streamFile.Scan(playlists, false);
+ }
+ catch (Exception ex)
+ {
+ errorStreamFiles.Add(streamFile);
+ if (StreamFileScanError != null)
+ {
+ if (StreamFileScanError(streamFile, ex))
+ {
+ continue;
+ }
+ else
+ {
+ break;
+ }
+ }
+ else throw ex;
+ }
+ }
+
+ foreach (TSPlaylistFile playlistFile in PlaylistFiles.Values)
+ {
+ playlistFile.Initialize();
+ if (!Is50Hz)
+ {
+ foreach (TSVideoStream videoStream in playlistFile.VideoStreams)
+ {
+ if (videoStream.FrameRate == TSFrameRate.FRAMERATE_25 ||
+ videoStream.FrameRate == TSFrameRate.FRAMERATE_50)
+ {
+ Is50Hz = true;
+ }
+ }
+ }
+ }
+ }
+
+ private FileSystemMetadata GetDirectoryBDMV(
+ string path)
+ {
+ FileSystemMetadata dir = _fileSystem.GetDirectoryInfo(path);
+
+ while (dir != null)
+ {
+ if (dir.Name == "BDMV")
+ {
+ return dir;
+ }
+ dir = _fileSystem.GetDirectoryInfo(Path.GetDirectoryName(dir.FullName));
+ }
+
+ return GetDirectory("BDMV", _fileSystem.GetDirectoryInfo(path), 0);
+ }
+
+ private FileSystemMetadata GetDirectory(
+ string name,
+ FileSystemMetadata dir,
+ int searchDepth)
+ {
+ if (dir != null)
+ {
+ FileSystemMetadata[] children = _fileSystem.GetDirectories(dir.FullName).ToArray();
+ foreach (FileSystemMetadata child in children)
+ {
+ if (child.Name == name)
+ {
+ return child;
+ }
+ }
+ if (searchDepth > 0)
+ {
+ foreach (FileSystemMetadata child in children)
+ {
+ GetDirectory(
+ name, child, searchDepth - 1);
+ }
+ }
+ }
+ return null;
+ }
+
+ private long GetDirectorySize(FileSystemMetadata directoryInfo)
+ {
+ long size = 0;
+
+ //if (!ExcludeDirs.Contains(directoryInfo.Name.ToUpper())) // TODO: Keep?
+ {
+ FileSystemMetadata[] pathFiles = _fileSystem.GetFiles(directoryInfo.FullName).ToArray();
+ foreach (FileSystemMetadata pathFile in pathFiles)
+ {
+ if (pathFile.Extension.ToUpper() == ".SSIF")
+ {
+ continue;
+ }
+ size += pathFile.Length;
+ }
+
+ FileSystemMetadata[] pathChildren = _fileSystem.GetDirectories(directoryInfo.FullName).ToArray();
+ foreach (FileSystemMetadata pathChild in pathChildren)
+ {
+ size += GetDirectorySize(pathChild);
+ }
+ }
+
+ return size;
+ }
+
+ private string GetVolumeLabel(FileSystemMetadata dir)
+ {
+ return dir.Name;
+ }
+
+ public static int CompareStreamFiles(
+ TSStreamFile x,
+ TSStreamFile y)
+ {
+ // TODO: Use interleaved file sizes
+
+ if ((x == null || x.FileInfo == null) && (y == null || y.FileInfo == null))
+ {
+ return 0;
+ }
+ else if ((x == null || x.FileInfo == null) && (y != null && y.FileInfo != null))
+ {
+ return 1;
+ }
+ else if ((x != null || x.FileInfo != null) && (y == null || y.FileInfo == null))
+ {
+ return -1;
+ }
+ else
+ {
+ if (x.FileInfo.Length > y.FileInfo.Length)
+ {
+ return 1;
+ }
+ else if (y.FileInfo.Length > x.FileInfo.Length)
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ }
+
+ }
+}
diff --git a/BDInfo/BitVector32.cs b/BDInfo/BitVector32.cs
new file mode 100644
index 0000000000..1ac94bfbeb
--- /dev/null
+++ b/BDInfo/BitVector32.cs
@@ -0,0 +1,308 @@
+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/LanguageCodes.cs b/BDInfo/LanguageCodes.cs
new file mode 100644
index 0000000000..90d0bccc43
--- /dev/null
+++ b/BDInfo/LanguageCodes.cs
@@ -0,0 +1,493 @@
+//============================================================================
+// BDInfo - Blu-ray Video and Audio Analysis Tool
+// Copyright © 2010 Cinema Squid
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//=============================================================================
+
+
+namespace BDInfo
+{
+ public abstract class LanguageCodes
+ {
+ public static string GetName(string code)
+ {
+ switch (code)
+ {
+ case "abk": return "Abkhazian";
+ case "ace": return "Achinese";
+ case "ach": return "Acoli";
+ case "ada": return "Adangme";
+ case "aar": return "Afar";
+ case "afh": return "Afrihili";
+ case "afr": return "Afrikaans";
+ case "afa": return "Afro-Asiatic (Other)";
+ case "aka": return "Akan";
+ case "akk": return "Akkadian";
+ case "alb": return "Albanian";
+ case "sqi": return "Albanian";
+ case "ale": return "Aleut";
+ case "alg": return "Algonquian languages";
+ case "tut": return "Altaic (Other)";
+ case "amh": return "Amharic";
+ case "apa": return "Apache languages";
+ case "ara": return "Arabic";
+ case "arc": return "Aramaic";
+ case "arp": return "Arapaho";
+ case "arn": return "Araucanian";
+ case "arw": return "Arawak";
+ case "arm": return "Armenian";
+ case "hye": return "Armenian";
+ case "art": return "Artificial (Other)";
+ case "asm": return "Assamese";
+ case "ath": return "Athapascan languages";
+ case "aus": return "Australian languages";
+ case "map": return "Austronesian (Other)";
+ case "ava": return "Avaric";
+ case "ave": return "Avestan";
+ case "awa": return "Awadhi";
+ case "aym": return "Aymara";
+ case "aze": return "Azerbaijani";
+ case "ban": return "Balinese";
+ case "bat": return "Baltic (Other)";
+ case "bal": return "Baluchi";
+ case "bam": return "Bambara";
+ case "bai": return "Bamileke languages";
+ case "bad": return "Banda";
+ case "bnt": return "Bantu (Other)";
+ case "bas": return "Basa";
+ case "bak": return "Bashkir";
+ case "baq": return "Basque";
+ case "eus": return "Basque";
+ case "btk": return "Batak (Indonesia)";
+ case "bej": return "Beja";
+ case "bel": return "Belarusian";
+ case "bem": return "Bemba";
+ case "ben": return "Bengali";
+ case "ber": return "Berber (Other)";
+ case "bho": return "Bhojpuri";
+ case "bih": return "Bihari";
+ case "bik": return "Bikol";
+ case "bin": return "Bini";
+ case "bis": return "Bislama";
+ case "bos": return "Bosnian";
+ case "bra": return "Braj";
+ case "bre": return "Breton";
+ case "bug": return "Buginese";
+ case "bul": return "Bulgarian";
+ case "bua": return "Buriat";
+ case "bur": return "Burmese";
+ case "mya": return "Burmese";
+ case "cad": return "Caddo";
+ case "car": return "Carib";
+ case "cat": return "Catalan";
+ case "cau": return "Caucasian (Other)";
+ case "ceb": return "Cebuano";
+ case "cel": return "Celtic (Other)";
+ case "cai": return "Central American Indian (Other)";
+ case "chg": return "Chagatai";
+ case "cmc": return "Chamic languages";
+ case "cha": return "Chamorro";
+ case "che": return "Chechen";
+ case "chr": return "Cherokee";
+ case "chy": return "Cheyenne";
+ case "chb": return "Chibcha";
+ case "chi": return "Chinese";
+ case "zho": return "Chinese";
+ case "chn": return "Chinook jargon";
+ case "chp": return "Chipewyan";
+ case "cho": return "Choctaw";
+ case "chu": return "Church Slavic";
+ case "chk": return "Chuukese";
+ case "chv": return "Chuvash";
+ case "cop": return "Coptic";
+ case "cor": return "Cornish";
+ case "cos": return "Corsican";
+ case "cre": return "Cree";
+ case "mus": return "Creek";
+ case "crp": return "Creoles and pidgins (Other)";
+ case "cpe": return "Creoles and pidgins,";
+ case "cpf": return "Creoles and pidgins,";
+ case "cpp": return "Creoles and pidgins,";
+ case "scr": return "Croatian";
+ case "hrv": return "Croatian";
+ case "cus": return "Cushitic (Other)";
+ case "cze": return "Czech";
+ case "ces": return "Czech";
+ case "dak": return "Dakota";
+ case "dan": return "Danish";
+ case "day": return "Dayak";
+ case "del": return "Delaware";
+ case "din": return "Dinka";
+ case "div": return "Divehi";
+ case "doi": return "Dogri";
+ case "dgr": return "Dogrib";
+ case "dra": return "Dravidian (Other)";
+ case "dua": return "Duala";
+ case "dut": return "Dutch";
+ case "nld": return "Dutch";
+ case "dum": return "Dutch, Middle (ca. 1050-1350)";
+ case "dyu": return "Dyula";
+ case "dzo": return "Dzongkha";
+ case "efi": return "Efik";
+ case "egy": return "Egyptian (Ancient)";
+ case "eka": return "Ekajuk";
+ case "elx": return "Elamite";
+ case "eng": return "English";
+ case "enm": return "English, Middle (1100-1500)";
+ case "ang": return "English, Old (ca.450-1100)";
+ case "epo": return "Esperanto";
+ case "est": return "Estonian";
+ case "ewe": return "Ewe";
+ case "ewo": return "Ewondo";
+ case "fan": return "Fang";
+ case "fat": return "Fanti";
+ case "fao": return "Faroese";
+ case "fij": return "Fijian";
+ case "fin": return "Finnish";
+ case "fiu": return "Finno-Ugrian (Other)";
+ case "fon": return "Fon";
+ case "fre": return "French";
+ case "fra": return "French";
+ case "frm": return "French, Middle (ca.1400-1600)";
+ case "fro": return "French, Old (842-ca.1400)";
+ case "fry": return "Frisian";
+ case "fur": return "Friulian";
+ case "ful": return "Fulah";
+ case "gaa": return "Ga";
+ case "glg": return "Gallegan";
+ case "lug": return "Ganda";
+ case "gay": return "Gayo";
+ case "gba": return "Gbaya";
+ case "gez": return "Geez";
+ case "geo": return "Georgian";
+ case "kat": return "Georgian";
+ case "ger": return "German";
+ case "deu": return "German";
+ case "nds": return "Saxon";
+ case "gmh": return "German, Middle High (ca.1050-1500)";
+ case "goh": return "German, Old High (ca.750-1050)";
+ case "gem": return "Germanic (Other)";
+ case "gil": return "Gilbertese";
+ case "gon": return "Gondi";
+ case "gor": return "Gorontalo";
+ case "got": return "Gothic";
+ case "grb": return "Grebo";
+ case "grc": return "Greek, Ancient (to 1453)";
+ case "gre": return "Greek";
+ case "ell": return "Greek";
+ case "grn": return "Guarani";
+ case "guj": return "Gujarati";
+ case "gwi": return "Gwich´in";
+ case "hai": return "Haida";
+ case "hau": return "Hausa";
+ case "haw": return "Hawaiian";
+ case "heb": return "Hebrew";
+ case "her": return "Herero";
+ case "hil": return "Hiligaynon";
+ case "him": return "Himachali";
+ case "hin": return "Hindi";
+ case "hmo": return "Hiri Motu";
+ case "hit": return "Hittite";
+ case "hmn": return "Hmong";
+ case "hun": return "Hungarian";
+ case "hup": return "Hupa";
+ case "iba": return "Iban";
+ case "ice": return "Icelandic";
+ case "isl": return "Icelandic";
+ case "ibo": return "Igbo";
+ case "ijo": return "Ijo";
+ case "ilo": return "Iloko";
+ case "inc": return "Indic (Other)";
+ case "ine": return "Indo-European (Other)";
+ case "ind": return "Indonesian";
+ case "ina": return "Interlingua (International";
+ case "ile": return "Interlingue";
+ case "iku": return "Inuktitut";
+ case "ipk": return "Inupiaq";
+ case "ira": return "Iranian (Other)";
+ case "gle": return "Irish";
+ case "mga": return "Irish, Middle (900-1200)";
+ case "sga": return "Irish, Old (to 900)";
+ case "iro": return "Iroquoian languages";
+ case "ita": return "Italian";
+ case "jpn": return "Japanese";
+ case "jav": return "Javanese";
+ case "jrb": return "Judeo-Arabic";
+ case "jpr": return "Judeo-Persian";
+ case "kab": return "Kabyle";
+ case "kac": return "Kachin";
+ case "kal": return "Kalaallisut";
+ case "kam": return "Kamba";
+ case "kan": return "Kannada";
+ case "kau": return "Kanuri";
+ case "kaa": return "Kara-Kalpak";
+ case "kar": return "Karen";
+ case "kas": return "Kashmiri";
+ case "kaw": return "Kawi";
+ case "kaz": return "Kazakh";
+ case "kha": return "Khasi";
+ case "khm": return "Khmer";
+ case "khi": return "Khoisan (Other)";
+ case "kho": return "Khotanese";
+ case "kik": return "Kikuyu";
+ case "kmb": return "Kimbundu";
+ case "kin": return "Kinyarwanda";
+ case "kir": return "Kirghiz";
+ case "kom": return "Komi";
+ case "kon": return "Kongo";
+ case "kok": return "Konkani";
+ case "kor": return "Korean";
+ case "kos": return "Kosraean";
+ case "kpe": return "Kpelle";
+ case "kro": return "Kru";
+ case "kua": return "Kuanyama";
+ case "kum": return "Kumyk";
+ case "kur": return "Kurdish";
+ case "kru": return "Kurukh";
+ case "kut": return "Kutenai";
+ case "lad": return "Ladino";
+ case "lah": return "Lahnda";
+ case "lam": return "Lamba";
+ case "lao": return "Lao";
+ case "lat": return "Latin";
+ case "lav": return "Latvian";
+ case "ltz": return "Letzeburgesch";
+ case "lez": return "Lezghian";
+ case "lin": return "Lingala";
+ case "lit": return "Lithuanian";
+ case "loz": return "Lozi";
+ case "lub": return "Luba-Katanga";
+ case "lua": return "Luba-Lulua";
+ case "lui": return "Luiseno";
+ case "lun": return "Lunda";
+ case "luo": return "Luo (Kenya and Tanzania)";
+ case "lus": return "Lushai";
+ case "mac": return "Macedonian";
+ case "mkd": return "Macedonian";
+ case "mad": return "Madurese";
+ case "mag": return "Magahi";
+ case "mai": return "Maithili";
+ case "mak": return "Makasar";
+ case "mlg": return "Malagasy";
+ case "may": return "Malay";
+ case "msa": return "Malay";
+ case "mal": return "Malayalam";
+ case "mlt": return "Maltese";
+ case "mnc": return "Manchu";
+ case "mdr": return "Mandar";
+ case "man": return "Mandingo";
+ case "mni": return "Manipuri";
+ case "mno": return "Manobo languages";
+ case "glv": return "Manx";
+ case "mao": return "Maori";
+ case "mri": return "Maori";
+ case "mar": return "Marathi";
+ case "chm": return "Mari";
+ case "mah": return "Marshall";
+ case "mwr": return "Marwari";
+ case "mas": return "Masai";
+ case "myn": return "Mayan languages";
+ case "men": return "Mende";
+ case "mic": return "Micmac";
+ case "min": return "Minangkabau";
+ case "mis": return "Miscellaneous languages";
+ case "moh": return "Mohawk";
+ case "mol": return "Moldavian";
+ case "mkh": return "Mon-Khmer (Other)";
+ case "lol": return "Mongo";
+ case "mon": return "Mongolian";
+ case "mos": return "Mossi";
+ case "mul": return "Multiple languages";
+ case "mun": return "Munda languages";
+ case "nah": return "Nahuatl";
+ case "nau": return "Nauru";
+ case "nav": return "Navajo";
+ case "nde": return "Ndebele, North";
+ case "nbl": return "Ndebele, South";
+ case "ndo": return "Ndonga";
+ case "nep": return "Nepali";
+ case "new": return "Newari";
+ case "nia": return "Nias";
+ case "nic": return "Niger-Kordofanian (Other)";
+ case "ssa": return "Nilo-Saharan (Other)";
+ case "niu": return "Niuean";
+ case "non": return "Norse, Old";
+ case "nai": return "North American Indian (Other)";
+ case "sme": return "Northern Sami";
+ case "nor": return "Norwegian";
+ case "nob": return "Norwegian Bokmål";
+ case "nno": return "Norwegian Nynorsk";
+ case "nub": return "Nubian languages";
+ case "nym": return "Nyamwezi";
+ case "nya": return "Nyanja";
+ case "nyn": return "Nyankole";
+ case "nyo": return "Nyoro";
+ case "nzi": return "Nzima";
+ case "oci": return "Occitan";
+ case "oji": return "Ojibwa";
+ case "ori": return "Oriya";
+ case "orm": return "Oromo";
+ case "osa": return "Osage";
+ case "oss": return "Ossetian";
+ case "oto": return "Otomian languages";
+ case "pal": return "Pahlavi";
+ case "pau": return "Palauan";
+ case "pli": return "Pali";
+ case "pam": return "Pampanga";
+ case "pag": return "Pangasinan";
+ case "pan": return "Panjabi";
+ case "pap": return "Papiamento";
+ case "paa": return "Papuan (Other)";
+ case "per": return "Persian";
+ case "fas": return "Persian";
+ case "peo": return "Persian, Old (ca.600-400 B.C.)";
+ case "phi": return "Philippine (Other)";
+ case "phn": return "Phoenician";
+ case "pon": return "Pohnpeian";
+ case "pol": return "Polish";
+ case "por": return "Portuguese";
+ case "pra": return "Prakrit languages";
+ case "pro": return "Provençal";
+ case "pus": return "Pushto";
+ case "que": return "Quechua";
+ case "roh": return "Raeto-Romance";
+ case "raj": return "Rajasthani";
+ case "rap": return "Rapanui";
+ case "rar": return "Rarotongan";
+ case "roa": return "Romance (Other)";
+ case "rum": return "Romanian";
+ case "ron": return "Romanian";
+ case "rom": return "Romany";
+ case "run": return "Rundi";
+ case "rus": return "Russian";
+ case "sal": return "Salishan languages";
+ case "sam": return "Samaritan Aramaic";
+ case "smi": return "Sami languages (Other)";
+ case "smo": return "Samoan";
+ case "sad": return "Sandawe";
+ case "sag": return "Sango";
+ case "san": return "Sanskrit";
+ case "sat": return "Santali";
+ case "srd": return "Sardinian";
+ case "sas": return "Sasak";
+ case "sco": return "Scots";
+ case "gla": return "Gaelic";
+ case "sel": return "Selkup";
+ case "sem": return "Semitic (Other)";
+ case "scc": return "Serbian";
+ case "srp": return "Serbian";
+ case "srr": return "Serer";
+ case "shn": return "Shan";
+ case "sna": return "Shona";
+ case "sid": return "Sidamo";
+ case "sgn": return "Sign languages";
+ case "bla": return "Siksika";
+ case "snd": return "Sindhi";
+ case "sin": return "Sinhalese";
+ case "sit": return "Sino-Tibetan (Other)";
+ case "sio": return "Siouan languages";
+ case "den": return "Slave (Athapascan)";
+ case "sla": return "Slavic (Other)";
+ case "slo": return "Slovak";
+ case "slk": return "Slovak";
+ case "slv": return "Slovenian";
+ case "sog": return "Sogdian";
+ case "som": return "Somali";
+ case "son": return "Songhai";
+ case "snk": return "Soninke";
+ case "wen": return "Sorbian languages";
+ case "nso": return "Sotho, Northern";
+ case "sot": return "Sotho, Southern";
+ case "sai": return "South American Indian (Other)";
+ case "spa": return "Spanish";
+ case "suk": return "Sukuma";
+ case "sux": return "Sumerian";
+ case "sun": return "Sundanese";
+ case "sus": return "Susu";
+ case "swa": return "Swahili";
+ case "ssw": return "Swati";
+ case "swe": return "Swedish";
+ case "syr": return "Syriac";
+ case "tgl": return "Tagalog";
+ case "tah": return "Tahitian";
+ case "tai": return "Tai (Other)";
+ case "tgk": return "Tajik";
+ case "tmh": return "Tamashek";
+ case "tam": return "Tamil";
+ case "tat": return "Tatar";
+ case "tel": return "Telugu";
+ case "ter": return "Tereno";
+ case "tet": return "Tetum";
+ case "tha": return "Thai";
+ case "tib": return "Tibetan";
+ case "bod": return "Tibetan";
+ case "tig": return "Tigre";
+ case "tir": return "Tigrinya";
+ case "tem": return "Timne";
+ case "tiv": return "Tiv";
+ case "tli": return "Tlingit";
+ case "tpi": return "Tok Pisin";
+ case "tkl": return "Tokelau";
+ case "tog": return "Tonga (Nyasa)";
+ case "ton": return "Tonga (Tonga Islands)";
+ case "tsi": return "Tsimshian";
+ case "tso": return "Tsonga";
+ case "tsn": return "Tswana";
+ case "tum": return "Tumbuka";
+ case "tur": return "Turkish";
+ case "ota": return "Turkish, Ottoman (1500-1928)";
+ case "tuk": return "Turkmen";
+ case "tvl": return "Tuvalu";
+ case "tyv": return "Tuvinian";
+ case "twi": return "Twi";
+ case "uga": return "Ugaritic";
+ case "uig": return "Uighur";
+ case "ukr": return "Ukrainian";
+ case "umb": return "Umbundu";
+ case "und": return "Undetermined";
+ case "urd": return "Urdu";
+ case "uzb": return "Uzbek";
+ case "vai": return "Vai";
+ case "ven": return "Venda";
+ case "vie": return "Vietnamese";
+ case "vol": return "Volapük";
+ case "vot": return "Votic";
+ case "wak": return "Wakashan languages";
+ case "wal": return "Walamo";
+ case "war": return "Waray";
+ case "was": return "Washo";
+ case "wel": return "Welsh";
+ case "cym": return "Welsh";
+ case "wol": return "Wolof";
+ case "xho": return "Xhosa";
+ case "sah": return "Yakut";
+ case "yao": return "Yao";
+ case "yap": return "Yapese";
+ case "yid": return "Yiddish";
+ case "yor": return "Yoruba";
+ case "ypk": return "Yupik languages";
+ case "znd": return "Zande";
+ case "zap": return "Zapotec";
+ case "zen": return "Zenaga";
+ case "zha": return "Zhuang";
+ case "zul": return "Zulu";
+ case "zun": return "Zuni";
+
+ default: return code;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Model.Portable/Properties/AssemblyInfo.cs b/BDInfo/Properties/AssemblyInfo.cs
index a24dd014e4..aa44da1909 100644
--- a/MediaBrowser.Model.Portable/Properties/AssemblyInfo.cs
+++ b/BDInfo/Properties/AssemblyInfo.cs
@@ -1,15 +1,17 @@
using System.Resources;
using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
-[assembly: AssemblyTitle("MediaBrowser.Model.Portable")]
+[assembly: AssemblyTitle("BDInfo")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("MediaBrowser.Model.Portable")]
-[assembly: AssemblyCopyright("Copyright © 2013")]
+[assembly: AssemblyProduct("BDInfo")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")]
@@ -20,4 +22,8 @@ using System.Reflection;
// 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.1")] \ No newline at end of file
diff --git a/BDInfo/ReadMe.txt b/BDInfo/ReadMe.txt
new file mode 100644
index 0000000000..68326d560a
--- /dev/null
+++ b/BDInfo/ReadMe.txt
@@ -0,0 +1,5 @@
+The source is taken from the BDRom folder of this project:
+
+http://www.cinemasquid.com/blu-ray/tools/bdinfo
+
+BDInfoSettings was taken from the FormSettings class, and changed so that the settings all return defaults. \ No newline at end of file
diff --git a/BDInfo/TSCodecAC3.cs b/BDInfo/TSCodecAC3.cs
new file mode 100644
index 0000000000..144141c302
--- /dev/null
+++ b/BDInfo/TSCodecAC3.cs
@@ -0,0 +1,309 @@
+//============================================================================
+// BDInfo - Blu-ray Video and Audio Analysis Tool
+// Copyright © 2010 Cinema Squid
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//=============================================================================
+
+#undef DEBUG
+using System.IO;
+
+namespace BDInfo
+{
+ public abstract class TSCodecAC3
+ {
+ private static byte[] eac3_blocks = new byte[] { 1, 2, 3, 6 };
+
+ public static void Scan(
+ TSAudioStream stream,
+ TSStreamBuffer buffer,
+ ref string tag)
+ {
+ if (stream.IsInitialized) return;
+
+ byte[] sync = buffer.ReadBytes(2);
+ if (sync == null ||
+ sync[0] != 0x0B ||
+ sync[1] != 0x77)
+ {
+ return;
+ }
+
+ int sr_code = 0;
+ int frame_size = 0;
+ int frame_size_code = 0;
+ int channel_mode = 0;
+ int lfe_on = 0;
+ int dial_norm = 0;
+ int num_blocks = 0;
+
+ byte[] hdr = buffer.ReadBytes(4);
+ int bsid = (hdr[3] & 0xF8) >> 3;
+ buffer.Seek(-4, SeekOrigin.Current);
+ if (bsid <= 10)
+ {
+ byte[] crc = buffer.ReadBytes(2);
+ sr_code = buffer.ReadBits(2);
+ frame_size_code = buffer.ReadBits(6);
+ bsid = buffer.ReadBits(5);
+ int bsmod = buffer.ReadBits(3);
+
+ channel_mode = buffer.ReadBits(3);
+ int cmixlev = 0;
+ if (((channel_mode & 0x1) > 0) && (channel_mode != 0x1))
+ {
+ cmixlev = buffer.ReadBits(2);
+ }
+ int surmixlev = 0;
+ if ((channel_mode & 0x4) > 0)
+ {
+ surmixlev = buffer.ReadBits(2);
+ }
+ int dsurmod = 0;
+ if (channel_mode == 0x2)
+ {
+ dsurmod = buffer.ReadBits(2);
+ if (dsurmod == 0x2)
+ {
+ stream.AudioMode = TSAudioMode.Surround;
+ }
+ }
+ lfe_on = buffer.ReadBits(1);
+ dial_norm = buffer.ReadBits(5);
+ int compr = 0;
+ if (1 == buffer.ReadBits(1))
+ {
+ compr = buffer.ReadBits(8);
+ }
+ int langcod = 0;
+ if (1 == buffer.ReadBits(1))
+ {
+ langcod = buffer.ReadBits(8);
+ }
+ int mixlevel = 0;
+ int roomtyp = 0;
+ if (1 == buffer.ReadBits(1))
+ {
+ mixlevel = buffer.ReadBits(5);
+ roomtyp = buffer.ReadBits(2);
+ }
+ if (channel_mode == 0)
+ {
+ int dialnorm2 = buffer.ReadBits(5);
+ int compr2 = 0;
+ if (1 == buffer.ReadBits(1))
+ {
+ compr2 = buffer.ReadBits(8);
+ }
+ int langcod2 = 0;
+ if (1 == buffer.ReadBits(1))
+ {
+ langcod2 = buffer.ReadBits(8);
+ }
+ int mixlevel2 = 0;
+ int roomtyp2 = 0;
+ if (1 == buffer.ReadBits(1))
+ {
+ mixlevel2 = buffer.ReadBits(5);
+ roomtyp2 = buffer.ReadBits(2);
+ }
+ }
+ int copyrightb = buffer.ReadBits(1);
+ int origbs = buffer.ReadBits(1);
+ if (bsid == 6)
+ {
+ if (1 == buffer.ReadBits(1))
+ {
+ int dmixmod = buffer.ReadBits(2);
+ int ltrtcmixlev = buffer.ReadBits(3);
+ int ltrtsurmixlev = buffer.ReadBits(3);
+ int lorocmixlev = buffer.ReadBits(3);
+ int lorosurmixlev = buffer.ReadBits(3);
+ }
+ if (1 == buffer.ReadBits(1))
+ {
+ int dsurexmod = buffer.ReadBits(2);
+ int dheadphonmod = buffer.ReadBits(2);
+ if (dheadphonmod == 0x2)
+ {
+ // TODO
+ }
+ int adconvtyp = buffer.ReadBits(1);
+ int xbsi2 = buffer.ReadBits(8);
+ int encinfo = buffer.ReadBits(1);
+ if (dsurexmod == 2)
+ {
+ stream.AudioMode = TSAudioMode.Extended;
+ }
+ }
+ }
+ }
+ else
+ {
+ int frame_type = buffer.ReadBits(2);
+ int substreamid = buffer.ReadBits(3);
+ frame_size = (buffer.ReadBits(11) + 1) << 1;
+
+ sr_code = buffer.ReadBits(2);
+ if (sr_code == 3)
+ {
+ sr_code = buffer.ReadBits(2);
+ }
+ else
+ {
+ num_blocks = buffer.ReadBits(2);
+ }
+ channel_mode = buffer.ReadBits(3);
+ lfe_on = buffer.ReadBits(1);
+ }
+
+ switch (channel_mode)
+ {
+ case 0: // 1+1
+ stream.ChannelCount = 2;
+ if (stream.AudioMode == TSAudioMode.Unknown)
+ {
+ stream.AudioMode = TSAudioMode.DualMono;
+ }
+ break;
+ case 1: // 1/0
+ stream.ChannelCount = 1;
+ break;
+ case 2: // 2/0
+ stream.ChannelCount = 2;
+ if (stream.AudioMode == TSAudioMode.Unknown)
+ {
+ stream.AudioMode = TSAudioMode.Stereo;
+ }
+ break;
+ case 3: // 3/0
+ stream.ChannelCount = 3;
+ break;
+ case 4: // 2/1
+ stream.ChannelCount = 3;
+ break;
+ case 5: // 3/1
+ stream.ChannelCount = 4;
+ break;
+ case 6: // 2/2
+ stream.ChannelCount = 4;
+ break;
+ case 7: // 3/2
+ stream.ChannelCount = 5;
+ break;
+ default:
+ stream.ChannelCount = 0;
+ break;
+ }
+
+ switch (sr_code)
+ {
+ case 0:
+ stream.SampleRate = 48000;
+ break;
+ case 1:
+ stream.SampleRate = 44100;
+ break;
+ case 2:
+ stream.SampleRate = 32000;
+ break;
+ default:
+ stream.SampleRate = 0;
+ break;
+ }
+
+ if (bsid <= 10)
+ {
+ switch (frame_size_code >> 1)
+ {
+ case 18:
+ stream.BitRate = 640000;
+ break;
+ case 17:
+ stream.BitRate = 576000;
+ break;
+ case 16:
+ stream.BitRate = 512000;
+ break;
+ case 15:
+ stream.BitRate = 448000;
+ break;
+ case 14:
+ stream.BitRate = 384000;
+ break;
+ case 13:
+ stream.BitRate = 320000;
+ break;
+ case 12:
+ stream.BitRate = 256000;
+ break;
+ case 11:
+ stream.BitRate = 224000;
+ break;
+ case 10:
+ stream.BitRate = 192000;
+ break;
+ case 9:
+ stream.BitRate = 160000;
+ break;
+ case 8:
+ stream.BitRate = 128000;
+ break;
+ case 7:
+ stream.BitRate = 112000;
+ break;
+ case 6:
+ stream.BitRate = 96000;
+ break;
+ case 5:
+ stream.BitRate = 80000;
+ break;
+ case 4:
+ stream.BitRate = 64000;
+ break;
+ case 3:
+ stream.BitRate = 56000;
+ break;
+ case 2:
+ stream.BitRate = 48000;
+ break;
+ case 1:
+ stream.BitRate = 40000;
+ break;
+ case 0:
+ stream.BitRate = 32000;
+ break;
+ default:
+ stream.BitRate = 0;
+ break;
+ }
+ }
+ else
+ {
+ stream.BitRate = (long)
+ (4.0 * frame_size * stream.SampleRate / (num_blocks * 256));
+ }
+
+ stream.LFE = lfe_on;
+ if (stream.StreamType != TSStreamType.AC3_PLUS_AUDIO &&
+ stream.StreamType != TSStreamType.AC3_PLUS_SECONDARY_AUDIO)
+ {
+ stream.DialNorm = dial_norm - 31;
+ }
+ stream.IsVBR = false;
+ stream.IsInitialized = true;
+ }
+ }
+}
diff --git a/BDInfo/TSCodecAVC.cs b/BDInfo/TSCodecAVC.cs
new file mode 100644
index 0000000000..43c6d6f857
--- /dev/null
+++ b/BDInfo/TSCodecAVC.cs
@@ -0,0 +1,148 @@
+//============================================================================
+// BDInfo - Blu-ray Video and Audio Analysis Tool
+// Copyright © 2010 Cinema Squid
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//=============================================================================
+
+
+namespace BDInfo
+{
+ public abstract class TSCodecAVC
+ {
+ public static void Scan(
+ TSVideoStream stream,
+ TSStreamBuffer buffer,
+ ref string tag)
+ {
+ uint parse = 0;
+ byte accessUnitDelimiterParse = 0;
+ byte sequenceParameterSetParse = 0;
+ string profile = null;
+ string level = null;
+ byte constraintSet0Flag = 0;
+ byte constraintSet1Flag = 0;
+ byte constraintSet2Flag = 0;
+ byte constraintSet3Flag = 0;
+
+ for (int i = 0; i < buffer.Length; i++)
+ {
+ parse = (parse << 8) + buffer.ReadByte();
+
+ if (parse == 0x00000109)
+ {
+ accessUnitDelimiterParse = 1;
+ }
+ else if (accessUnitDelimiterParse > 0)
+ {
+ --accessUnitDelimiterParse;
+ if (accessUnitDelimiterParse == 0)
+ {
+ switch ((parse & 0xFF) >> 5)
+ {
+ case 0: // I
+ case 3: // SI
+ case 5: // I, SI
+ tag = "I";
+ break;
+
+ case 1: // I, P
+ case 4: // SI, SP
+ case 6: // I, SI, P, SP
+ tag = "P";
+ break;
+
+ case 2: // I, P, B
+ case 7: // I, SI, P, SP, B
+ tag = "B";
+ break;
+ }
+ if (stream.IsInitialized) return;
+ }
+ }
+ else if (parse == 0x00000127 || parse == 0x00000167)
+ {
+ sequenceParameterSetParse = 3;
+ }
+ else if (sequenceParameterSetParse > 0)
+ {
+ --sequenceParameterSetParse;
+ switch (sequenceParameterSetParse)
+ {
+ case 2:
+ switch (parse & 0xFF)
+ {
+ case 66:
+ profile = "Baseline Profile";
+ break;
+ case 77:
+ profile = "Main Profile";
+ break;
+ case 88:
+ profile = "Extended Profile";
+ break;
+ case 100:
+ profile = "High Profile";
+ break;
+ case 110:
+ profile = "High 10 Profile";
+ break;
+ case 122:
+ profile = "High 4:2:2 Profile";
+ break;
+ case 144:
+ profile = "High 4:4:4 Profile";
+ break;
+ default:
+ profile = "Unknown Profile";
+ break;
+ }
+ break;
+
+ case 1:
+ constraintSet0Flag = (byte)
+ ((parse & 0x80) >> 7);
+ constraintSet1Flag = (byte)
+ ((parse & 0x40) >> 6);
+ constraintSet2Flag = (byte)
+ ((parse & 0x20) >> 5);
+ constraintSet3Flag = (byte)
+ ((parse & 0x10) >> 4);
+ break;
+
+ case 0:
+ byte b = (byte)(parse & 0xFF);
+ if (b == 11 && constraintSet3Flag == 1)
+ {
+ level = "1b";
+ }
+ else
+ {
+ level = string.Format(
+ "{0:D}.{1:D}",
+ b / 10, (b - ((b / 10) * 10)));
+ }
+ stream.EncodingProfile = string.Format(
+ "{0} {1}", profile, level);
+ stream.IsVBR = true;
+ stream.IsInitialized = true;
+ break;
+ }
+ }
+ }
+ return;
+ }
+ }
+}
diff --git a/BDInfo/TSCodecDTS.cs b/BDInfo/TSCodecDTS.cs
new file mode 100644
index 0000000000..58eb60fc59
--- /dev/null
+++ b/BDInfo/TSCodecDTS.cs
@@ -0,0 +1,159 @@
+//============================================================================
+// BDInfo - Blu-ray Video and Audio Analysis Tool
+// Copyright © 2010 Cinema Squid
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//=============================================================================
+
+
+namespace BDInfo
+{
+ public abstract class TSCodecDTS
+ {
+ private static int[] dca_sample_rates =
+ {
+ 0, 8000, 16000, 32000, 0, 0, 11025, 22050, 44100, 0, 0,
+ 12000, 24000, 48000, 96000, 192000
+ };
+
+ private static int[] dca_bit_rates =
+ {
+ 32000, 56000, 64000, 96000, 112000, 128000,
+ 192000, 224000, 256000, 320000, 384000,
+ 448000, 512000, 576000, 640000, 768000,
+ 896000, 1024000, 1152000, 1280000, 1344000,
+ 1408000, 1411200, 1472000, 1509000, 1920000,
+ 2048000, 3072000, 3840000, 1/*open*/, 2/*variable*/, 3/*lossless*/
+ };
+
+ private static int[] dca_channels =
+ {
+ 1, 2, 2, 2, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7, 8, 8
+ };
+
+ private static int[] dca_bits_per_sample =
+ {
+ 16, 16, 20, 20, 0, 24, 24
+ };
+
+ public static void Scan(
+ TSAudioStream stream,
+ TSStreamBuffer buffer,
+ long bitrate,
+ ref string tag)
+ {
+ if (stream.IsInitialized) return;
+
+ bool syncFound = false;
+ uint sync = 0;
+ for (int i = 0; i < buffer.Length; i++)
+ {
+ sync = (sync << 8) + buffer.ReadByte();
+ if (sync == 0x7FFE8001)
+ {
+ syncFound = true;
+ break;
+ }
+ }
+ if (!syncFound) return;
+
+ int frame_type = buffer.ReadBits(1);
+ int samples_deficit = buffer.ReadBits(5);
+ int crc_present = buffer.ReadBits(1);
+ int sample_blocks = buffer.ReadBits(7);
+ int frame_size = buffer.ReadBits(14);
+ if (frame_size < 95)
+ {
+ return;
+ }
+ int amode = buffer.ReadBits(6);
+ int sample_rate = buffer.ReadBits(4);
+ if (sample_rate < 0 || sample_rate >= dca_sample_rates.Length)
+ {
+ return;
+ }
+ int bit_rate = buffer.ReadBits(5);
+ if (bit_rate < 0 || bit_rate >= dca_bit_rates.Length)
+ {
+ return;
+ }
+ int downmix = buffer.ReadBits(1);
+ int dynrange = buffer.ReadBits(1);
+ int timestamp = buffer.ReadBits(1);
+ int aux_data = buffer.ReadBits(1);
+ int hdcd = buffer.ReadBits(1);
+ int ext_descr = buffer.ReadBits(3);
+ int ext_coding = buffer.ReadBits(1);
+ int aspf = buffer.ReadBits(1);
+ int lfe = buffer.ReadBits(2);
+ int predictor_history = buffer.ReadBits(1);
+ if (crc_present == 1)
+ {
+ int crc = buffer.ReadBits(16);
+ }
+ int multirate_inter = buffer.ReadBits(1);
+ int version = buffer.ReadBits(4);
+ int copy_history = buffer.ReadBits(2);
+ int source_pcm_res = buffer.ReadBits(3);
+ int front_sum = buffer.ReadBits(1);
+ int surround_sum = buffer.ReadBits(1);
+ int dialog_norm = buffer.ReadBits(4);
+ if (source_pcm_res < 0 || source_pcm_res >= dca_bits_per_sample.Length)
+ {
+ return;
+ }
+ int subframes = buffer.ReadBits(4);
+ int total_channels = buffer.ReadBits(3) + 1 + ext_coding;
+
+ stream.SampleRate = dca_sample_rates[sample_rate];
+ stream.ChannelCount = total_channels;
+ stream.LFE = (lfe > 0 ? 1 : 0);
+ stream.BitDepth = dca_bits_per_sample[source_pcm_res];
+ stream.DialNorm = -dialog_norm;
+ if ((source_pcm_res & 0x1) == 0x1)
+ {
+ stream.AudioMode = TSAudioMode.Extended;
+ }
+
+ stream.BitRate = (uint)dca_bit_rates[bit_rate];
+ switch (stream.BitRate)
+ {
+ case 1:
+ if (bitrate > 0)
+ {
+ stream.BitRate = bitrate;
+ stream.IsVBR = false;
+ stream.IsInitialized = true;
+ }
+ else
+ {
+ stream.BitRate = 0;
+ }
+ break;
+
+ case 2:
+ case 3:
+ stream.IsVBR = true;
+ stream.IsInitialized = true;
+ break;
+
+ default:
+ stream.IsVBR = false;
+ stream.IsInitialized = true;
+ break;
+ }
+ }
+ }
+}
diff --git a/BDInfo/TSCodecDTSHD.cs b/BDInfo/TSCodecDTSHD.cs
new file mode 100644
index 0000000000..169a8077f2
--- /dev/null
+++ b/BDInfo/TSCodecDTSHD.cs
@@ -0,0 +1,246 @@
+//============================================================================
+// BDInfo - Blu-ray Video and Audio Analysis Tool
+// Copyright © 2010 Cinema Squid
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//=============================================================================
+
+
+namespace BDInfo
+{
+ public abstract class TSCodecDTSHD
+ {
+ private static int[] SampleRates = new int[]
+ { 0x1F40, 0x3E80, 0x7D00, 0x0FA00, 0x1F400, 0x5622, 0x0AC44, 0x15888, 0x2B110, 0x56220, 0x2EE0, 0x5DC0, 0x0BB80, 0x17700, 0x2EE00, 0x5DC00 };
+
+ public static void Scan(
+ TSAudioStream stream,
+ TSStreamBuffer buffer,
+ long bitrate,
+ ref string tag)
+ {
+ if (stream.IsInitialized &&
+ (stream.StreamType == TSStreamType.DTS_HD_SECONDARY_AUDIO ||
+ (stream.CoreStream != null &&
+ stream.CoreStream.IsInitialized))) return;
+
+ bool syncFound = false;
+ uint sync = 0;
+ for (int i = 0; i < buffer.Length; i++)
+ {
+ sync = (sync << 8) + buffer.ReadByte();
+ if (sync == 0x64582025)
+ {
+ syncFound = true;
+ break;
+ }
+ }
+
+ if (!syncFound)
+ {
+ tag = "CORE";
+ if (stream.CoreStream == null)
+ {
+ stream.CoreStream = new TSAudioStream();
+ stream.CoreStream.StreamType = TSStreamType.DTS_AUDIO;
+ }
+ if (!stream.CoreStream.IsInitialized)
+ {
+ buffer.BeginRead();
+ TSCodecDTS.Scan(stream.CoreStream, buffer, bitrate, ref tag);
+ }
+ return;
+ }
+
+ tag = "HD";
+ int temp1 = buffer.ReadBits(8);
+ int nuSubStreamIndex = buffer.ReadBits(2);
+ int nuExtSSHeaderSize = 0;
+ int nuExtSSFSize = 0;
+ int bBlownUpHeader = buffer.ReadBits(1);
+ if (1 == bBlownUpHeader)
+ {
+ nuExtSSHeaderSize = buffer.ReadBits(12) + 1;
+ nuExtSSFSize = buffer.ReadBits(20) + 1;
+ }
+ else
+ {
+ nuExtSSHeaderSize = buffer.ReadBits(8) + 1;
+ nuExtSSFSize = buffer.ReadBits(16) + 1;
+ }
+ int nuNumAudioPresent = 1;
+ int nuNumAssets = 1;
+ int bStaticFieldsPresent = buffer.ReadBits(1);
+ if (1 == bStaticFieldsPresent)
+ {
+ int nuRefClockCode = buffer.ReadBits(2);
+ int nuExSSFrameDurationCode = buffer.ReadBits(3) + 1;
+ long nuTimeStamp = 0;
+ if (1 == buffer.ReadBits(1))
+ {
+ nuTimeStamp = (buffer.ReadBits(18) << 18) + buffer.ReadBits(18);
+ }
+ nuNumAudioPresent = buffer.ReadBits(3) + 1;
+ nuNumAssets = buffer.ReadBits(3) + 1;
+ int[] nuActiveExSSMask = new int[nuNumAudioPresent];
+ for (int i = 0; i < nuNumAudioPresent; i++)
+ {
+ nuActiveExSSMask[i] = buffer.ReadBits(nuSubStreamIndex + 1); //?
+ }
+ for (int i = 0; i < nuNumAudioPresent; i++)
+ {
+ for (int j = 0; j < nuSubStreamIndex + 1; j++)
+ {
+ if (((j + 1) % 2) == 1)
+ {
+ int mask = buffer.ReadBits(8);
+ }
+ }
+ }
+ if (1 == buffer.ReadBits(1))
+ {
+ int nuMixMetadataAdjLevel = buffer.ReadBits(2);
+ int nuBits4MixOutMask = buffer.ReadBits(2) * 4 + 4;
+ int nuNumMixOutConfigs = buffer.ReadBits(2) + 1;
+ int[] nuMixOutChMask = new int[nuNumMixOutConfigs];
+ for (int i = 0; i < nuNumMixOutConfigs; i++)
+ {
+ nuMixOutChMask[i] = buffer.ReadBits(nuBits4MixOutMask);
+ }
+ }
+ }
+ int[] AssetSizes = new int[nuNumAssets];
+ for (int i = 0; i < nuNumAssets; i++)
+ {
+ if (1 == bBlownUpHeader)
+ {
+ AssetSizes[i] = buffer.ReadBits(20) + 1;
+ }
+ else
+ {
+ AssetSizes[i] = buffer.ReadBits(16) + 1;
+ }
+ }
+ for (int i = 0; i < nuNumAssets; i++)
+ {
+ long bufferPosition = buffer.Position;
+ int nuAssetDescriptorFSIZE = buffer.ReadBits(9) + 1;
+ int DescriptorDataForAssetIndex = buffer.ReadBits(3);
+ if (1 == bStaticFieldsPresent)
+ {
+ int AssetTypeDescrPresent = buffer.ReadBits(1);
+ if (1 == AssetTypeDescrPresent)
+ {
+ int AssetTypeDescriptor = buffer.ReadBits(4);
+ }
+ int LanguageDescrPresent = buffer.ReadBits(1);
+ if (1 == LanguageDescrPresent)
+ {
+ int LanguageDescriptor = buffer.ReadBits(24);
+ }
+ int bInfoTextPresent = buffer.ReadBits(1);
+ if (1 == bInfoTextPresent)
+ {
+ int nuInfoTextByteSize = buffer.ReadBits(10) + 1;
+ int[] InfoText = new int[nuInfoTextByteSize];
+ for (int j = 0; j < nuInfoTextByteSize; j++)
+ {
+ InfoText[j] = buffer.ReadBits(8);
+ }
+ }
+ int nuBitResolution = buffer.ReadBits(5) + 1;
+ int nuMaxSampleRate = buffer.ReadBits(4);
+ int nuTotalNumChs = buffer.ReadBits(8) + 1;
+ int bOne2OneMapChannels2Speakers = buffer.ReadBits(1);
+ int nuSpkrActivityMask = 0;
+ if (1 == bOne2OneMapChannels2Speakers)
+ {
+ int bEmbeddedStereoFlag = 0;
+ if (nuTotalNumChs > 2)
+ {
+ bEmbeddedStereoFlag = buffer.ReadBits(1);
+ }
+ int bEmbeddedSixChFlag = 0;
+ if (nuTotalNumChs > 6)
+ {
+ bEmbeddedSixChFlag = buffer.ReadBits(1);
+ }
+ int bSpkrMaskEnabled = buffer.ReadBits(1);
+ int nuNumBits4SAMask = 0;
+ if (1 == bSpkrMaskEnabled)
+ {
+ nuNumBits4SAMask = buffer.ReadBits(2);
+ nuNumBits4SAMask = nuNumBits4SAMask * 4 + 4;
+ nuSpkrActivityMask = buffer.ReadBits(nuNumBits4SAMask);
+ }
+ // TODO...
+ }
+ stream.SampleRate = SampleRates[nuMaxSampleRate];
+ stream.BitDepth = nuBitResolution;
+
+ stream.LFE = 0;
+ if ((nuSpkrActivityMask & 0x8) == 0x8)
+ {
+ ++stream.LFE;
+ }
+ if ((nuSpkrActivityMask & 0x1000) == 0x1000)
+ {
+ ++stream.LFE;
+ }
+ stream.ChannelCount = nuTotalNumChs - stream.LFE;
+ }
+ if (nuNumAssets > 1)
+ {
+ // TODO...
+ break;
+ }
+ }
+
+ // TODO
+ if (stream.CoreStream != null)
+ {
+ TSAudioStream coreStream = (TSAudioStream)stream.CoreStream;
+ if (coreStream.AudioMode == TSAudioMode.Extended &&
+ stream.ChannelCount == 5)
+ {
+ stream.AudioMode = TSAudioMode.Extended;
+ }
+ /*
+ if (coreStream.DialNorm != 0)
+ {
+ stream.DialNorm = coreStream.DialNorm;
+ }
+ */
+ }
+
+ if (stream.StreamType == TSStreamType.DTS_HD_MASTER_AUDIO)
+ {
+ stream.IsVBR = true;
+ stream.IsInitialized = true;
+ }
+ else if (bitrate > 0)
+ {
+ stream.IsVBR = false;
+ stream.BitRate = bitrate;
+ if (stream.CoreStream != null)
+ {
+ stream.BitRate += stream.CoreStream.BitRate;
+ stream.IsInitialized = true;
+ }
+ stream.IsInitialized = (stream.BitRate > 0 ? true : false);
+ }
+ }
+ }
+}
diff --git a/BDInfo/TSCodecLPCM.cs b/BDInfo/TSCodecLPCM.cs
new file mode 100644
index 0000000000..d12674f0e8
--- /dev/null
+++ b/BDInfo/TSCodecLPCM.cs
@@ -0,0 +1,123 @@
+//============================================================================
+// BDInfo - Blu-ray Video and Audio Analysis Tool
+// Copyright © 2010 Cinema Squid
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//=============================================================================
+
+
+namespace BDInfo
+{
+ public abstract class TSCodecLPCM
+ {
+ public static void Scan(
+ TSAudioStream stream,
+ TSStreamBuffer buffer,
+ ref string tag)
+ {
+ if (stream.IsInitialized) return;
+
+ byte[] header = buffer.ReadBytes(4);
+ int flags = (header[2] << 8) + header[3];
+
+ switch ((flags & 0xF000) >> 12)
+ {
+ case 1: // 1/0/0
+ stream.ChannelCount = 1;
+ stream.LFE = 0;
+ break;
+ case 3: // 2/0/0
+ stream.ChannelCount = 2;
+ stream.LFE = 0;
+ break;
+ case 4: // 3/0/0
+ stream.ChannelCount = 3;
+ stream.LFE = 0;
+ break;
+ case 5: // 2/1/0
+ stream.ChannelCount = 3;
+ stream.LFE = 0;
+ break;
+ case 6: // 3/1/0
+ stream.ChannelCount = 4;
+ stream.LFE = 0;
+ break;
+ case 7: // 2/2/0
+ stream.ChannelCount = 4;
+ stream.LFE = 0;
+ break;
+ case 8: // 3/2/0
+ stream.ChannelCount = 5;
+ stream.LFE = 0;
+ break;
+ case 9: // 3/2/1
+ stream.ChannelCount = 5;
+ stream.LFE = 1;
+ break;
+ case 10: // 3/4/0
+ stream.ChannelCount = 7;
+ stream.LFE = 0;
+ break;
+ case 11: // 3/4/1
+ stream.ChannelCount = 7;
+ stream.LFE = 1;
+ break;
+ default:
+ stream.ChannelCount = 0;
+ stream.LFE = 0;
+ break;
+ }
+
+ switch ((flags & 0xC0) >> 6)
+ {
+ case 1:
+ stream.BitDepth = 16;
+ break;
+ case 2:
+ stream.BitDepth = 20;
+ break;
+ case 3:
+ stream.BitDepth = 24;
+ break;
+ default:
+ stream.BitDepth = 0;
+ break;
+ }
+
+ switch ((flags & 0xF00) >> 8)
+ {
+ case 1:
+ stream.SampleRate = 48000;
+ break;
+ case 4:
+ stream.SampleRate = 96000;
+ break;
+ case 5:
+ stream.SampleRate = 192000;
+ break;
+ default:
+ stream.SampleRate = 0;
+ break;
+ }
+
+ stream.BitRate = (uint)
+ (stream.SampleRate * stream.BitDepth *
+ (stream.ChannelCount + stream.LFE));
+
+ stream.IsVBR = false;
+ stream.IsInitialized = true;
+ }
+ }
+}
diff --git a/BDInfo/TSCodecMPEG2.cs b/BDInfo/TSCodecMPEG2.cs
new file mode 100644
index 0000000000..3413a06e92
--- /dev/null
+++ b/BDInfo/TSCodecMPEG2.cs
@@ -0,0 +1,208 @@
+//============================================================================
+// BDInfo - Blu-ray Video and Audio Analysis Tool
+// Copyright © 2010 Cinema Squid
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//=============================================================================
+
+#undef DEBUG
+
+
+namespace BDInfo
+{
+ public abstract class TSCodecMPEG2
+ {
+ public static void Scan(
+ TSVideoStream stream,
+ TSStreamBuffer buffer,
+ ref string tag)
+ {
+ int parse = 0;
+ int pictureParse = 0;
+ int sequenceHeaderParse = 0;
+ int extensionParse = 0;
+ int sequenceExtensionParse = 0;
+
+ for (int i = 0; i < buffer.Length; i++)
+ {
+ parse = (parse << 8) + buffer.ReadByte();
+
+ if (parse == 0x00000100)
+ {
+ pictureParse = 2;
+ }
+ else if (parse == 0x000001B3)
+ {
+ sequenceHeaderParse = 7;
+ }
+ else if (sequenceHeaderParse > 0)
+ {
+ --sequenceHeaderParse;
+ switch (sequenceHeaderParse)
+ {
+#if DEBUG
+ case 6:
+ break;
+
+ case 5:
+ break;
+
+ case 4:
+ stream.Width =
+ (int)((parse & 0xFFF000) >> 12);
+ stream.Height =
+ (int)(parse & 0xFFF);
+ break;
+
+ case 3:
+ stream.AspectRatio =
+ (TSAspectRatio)((parse & 0xF0) >> 4);
+
+ switch ((parse & 0xF0) >> 4)
+ {
+ case 0: // Forbidden
+ break;
+ case 1: // Square
+ break;
+ case 2: // 4:3
+ break;
+ case 3: // 16:9
+ break;
+ case 4: // 2.21:1
+ break;
+ default: // Reserved
+ break;
+ }
+
+ switch (parse & 0xF)
+ {
+ case 0: // Forbidden
+ break;
+ case 1: // 23.976
+ stream.FrameRateEnumerator = 24000;
+ stream.FrameRateDenominator = 1001;
+ break;
+ case 2: // 24
+ stream.FrameRateEnumerator = 24000;
+ stream.FrameRateDenominator = 1000;
+ break;
+ case 3: // 25
+ stream.FrameRateEnumerator = 25000;
+ stream.FrameRateDenominator = 1000;
+ break;
+ case 4: // 29.97
+ stream.FrameRateEnumerator = 30000;
+ stream.FrameRateDenominator = 1001;
+ break;
+ case 5: // 30
+ stream.FrameRateEnumerator = 30000;
+ stream.FrameRateDenominator = 1000;
+ break;
+ case 6: // 50
+ stream.FrameRateEnumerator = 50000;
+ stream.FrameRateDenominator = 1000;
+ break;
+ case 7: // 59.94
+ stream.FrameRateEnumerator = 60000;
+ stream.FrameRateDenominator = 1001;
+ break;
+ case 8: // 60
+ stream.FrameRateEnumerator = 60000;
+ stream.FrameRateDenominator = 1000;
+ break;
+ default: // Reserved
+ stream.FrameRateEnumerator = 0;
+ stream.FrameRateDenominator = 0;
+ break;
+ }
+ break;
+
+ case 2:
+ break;
+
+ case 1:
+ break;
+#endif
+
+ case 0:
+#if DEBUG
+ stream.BitRate =
+ (((parse & 0xFFFFC0) >> 6) * 200);
+#endif
+ stream.IsVBR = true;
+ stream.IsInitialized = true;
+ break;
+ }
+ }
+ else if (pictureParse > 0)
+ {
+ --pictureParse;
+ if (pictureParse == 0)
+ {
+ switch ((parse & 0x38) >> 3)
+ {
+ case 1:
+ tag = "I";
+ break;
+ case 2:
+ tag = "P";
+ break;
+ case 3:
+ tag = "B";
+ break;
+ default:
+ break;
+ }
+ if (stream.IsInitialized) return;
+ }
+ }
+ else if (parse == 0x000001B5)
+ {
+ extensionParse = 1;
+ }
+ else if (extensionParse > 0)
+ {
+ --extensionParse;
+ if (extensionParse == 0)
+ {
+ if ((parse & 0xF0) == 0x10)
+ {
+ sequenceExtensionParse = 1;
+ }
+ }
+ }
+ else if (sequenceExtensionParse > 0)
+ {
+ --sequenceExtensionParse;
+#if DEBUG
+ if (sequenceExtensionParse == 0)
+ {
+ uint sequenceExtension =
+ ((parse & 0x8) >> 3);
+ if (sequenceExtension == 0)
+ {
+ stream.IsInterlaced = true;
+ }
+ else
+ {
+ stream.IsInterlaced = false;
+ }
+ }
+#endif
+ }
+ }
+ }
+ }
+}
diff --git a/BDInfo/TSCodecMVC.cs b/BDInfo/TSCodecMVC.cs
new file mode 100644
index 0000000000..80fed3886c
--- /dev/null
+++ b/BDInfo/TSCodecMVC.cs
@@ -0,0 +1,36 @@
+//============================================================================
+// BDInfo - Blu-ray Video and Audio Analysis Tool
+// Copyright © 2010 Cinema Squid
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//=============================================================================
+
+
+namespace BDInfo
+{
+ // TODO: Do something more interesting here...
+
+ public abstract class TSCodecMVC
+ {
+ public static void Scan(
+ TSVideoStream stream,
+ TSStreamBuffer buffer,
+ ref string tag)
+ {
+ stream.IsVBR = true;
+ stream.IsInitialized = true;
+ }
+ }
+}
diff --git a/BDInfo/TSCodecTrueHD.cs b/BDInfo/TSCodecTrueHD.cs
new file mode 100644
index 0000000000..baf4fa3dfe
--- /dev/null
+++ b/BDInfo/TSCodecTrueHD.cs
@@ -0,0 +1,186 @@
+//============================================================================
+// BDInfo - Blu-ray Video and Audio Analysis Tool
+// Copyright © 2010 Cinema Squid
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//=============================================================================
+
+
+namespace BDInfo
+{
+ public abstract class TSCodecTrueHD
+ {
+ public static void Scan(
+ TSAudioStream stream,
+ TSStreamBuffer buffer,
+ ref string tag)
+ {
+ if (stream.IsInitialized &&
+ stream.CoreStream != null &&
+ stream.CoreStream.IsInitialized) return;
+
+ bool syncFound = false;
+ uint sync = 0;
+ for (int i = 0; i < buffer.Length; i++)
+ {
+ sync = (sync << 8) + buffer.ReadByte();
+ if (sync == 0xF8726FBA)
+ {
+ syncFound = true;
+ break;
+ }
+ }
+
+ if (!syncFound)
+ {
+ tag = "CORE";
+ if (stream.CoreStream == null)
+ {
+ stream.CoreStream = new TSAudioStream();
+ stream.CoreStream.StreamType = TSStreamType.AC3_AUDIO;
+ }
+ if (!stream.CoreStream.IsInitialized)
+ {
+ buffer.BeginRead();
+ TSCodecAC3.Scan(stream.CoreStream, buffer, ref tag);
+ }
+ return;
+ }
+
+ tag = "HD";
+ int ratebits = buffer.ReadBits(4);
+ if (ratebits != 0xF)
+ {
+ stream.SampleRate =
+ (((ratebits & 8) > 0 ? 44100 : 48000) << (ratebits & 7));
+ }
+ int temp1 = buffer.ReadBits(8);
+ int channels_thd_stream1 = buffer.ReadBits(5);
+ int temp2 = buffer.ReadBits(2);
+
+ stream.ChannelCount = 0;
+ stream.LFE = 0;
+ int c_LFE2 = buffer.ReadBits(1);
+ if (c_LFE2 == 1)
+ {
+ stream.LFE += 1;
+ }
+ int c_Cvh = buffer.ReadBits(1);
+ if (c_Cvh == 1)
+ {
+ stream.ChannelCount += 1;
+ }
+ int c_LRw = buffer.ReadBits(1);
+ if (c_LRw == 1)
+ {
+ stream.ChannelCount += 2;
+ }
+ int c_LRsd = buffer.ReadBits(1);
+ if (c_LRsd == 1)
+ {
+ stream.ChannelCount += 2;
+ }
+ int c_Ts = buffer.ReadBits(1);
+ if (c_Ts == 1)
+ {
+ stream.ChannelCount += 1;
+ }
+ int c_Cs = buffer.ReadBits(1);
+ if (c_Cs == 1)
+ {
+ stream.ChannelCount += 1;
+ }
+ int c_LRrs = buffer.ReadBits(1);
+ if (c_LRrs == 1)
+ {
+ stream.ChannelCount += 2;
+ }
+ int c_LRc = buffer.ReadBits(1);
+ if (c_LRc == 1)
+ {
+ stream.ChannelCount += 2;
+ }
+ int c_LRvh = buffer.ReadBits(1);
+ if (c_LRvh == 1)
+ {
+ stream.ChannelCount += 2;
+ }
+ int c_LRs = buffer.ReadBits(1);
+ if (c_LRs == 1)
+ {
+ stream.ChannelCount += 2;
+ }
+ int c_LFE = buffer.ReadBits(1);
+ if (c_LFE == 1)
+ {
+ stream.LFE += 1;
+ }
+ int c_C = buffer.ReadBits(1);
+ if (c_C == 1)
+ {
+ stream.ChannelCount += 1;
+ }
+ int c_LR = buffer.ReadBits(1);
+ if (c_LR == 1)
+ {
+ stream.ChannelCount += 2;
+ }
+
+ int access_unit_size = 40 << (ratebits & 7);
+ int access_unit_size_pow2 = 64 << (ratebits & 7);
+
+ int a1 = buffer.ReadBits(16);
+ int a2 = buffer.ReadBits(16);
+ int a3 = buffer.ReadBits(16);
+
+ int is_vbr = buffer.ReadBits(1);
+ int peak_bitrate = buffer.ReadBits(15);
+ peak_bitrate = (peak_bitrate * stream.SampleRate) >> 4;
+
+ double peak_bitdepth =
+ (double)peak_bitrate /
+ (stream.ChannelCount + stream.LFE) /
+ stream.SampleRate;
+ if (peak_bitdepth > 14)
+ {
+ stream.BitDepth = 24;
+ }
+ else
+ {
+ stream.BitDepth = 16;
+ }
+
+#if DEBUG
+ System.Diagnostics.Debug.WriteLine(string.Format(
+ "{0}\t{1}\t{2:F2}",
+ stream.PID, peak_bitrate, peak_bitdepth));
+#endif
+ /*
+ // TODO: Get THD dialnorm from metadata
+ if (stream.CoreStream != null)
+ {
+ TSAudioStream coreStream = (TSAudioStream)stream.CoreStream;
+ if (coreStream.DialNorm != 0)
+ {
+ stream.DialNorm = coreStream.DialNorm;
+ }
+ }
+ */
+
+ stream.IsVBR = true;
+ stream.IsInitialized = true;
+ }
+ }
+}
diff --git a/BDInfo/TSCodecVC1.cs b/BDInfo/TSCodecVC1.cs
new file mode 100644
index 0000000000..164ef8c47e
--- /dev/null
+++ b/BDInfo/TSCodecVC1.cs
@@ -0,0 +1,131 @@
+//============================================================================
+// BDInfo - Blu-ray Video and Audio Analysis Tool
+// Copyright © 2010 Cinema Squid
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//=============================================================================
+
+
+namespace BDInfo
+{
+ public abstract class TSCodecVC1
+ {
+ public static void Scan(
+ TSVideoStream stream,
+ TSStreamBuffer buffer,
+ ref string tag)
+ {
+ int parse = 0;
+ byte frameHeaderParse = 0;
+ byte sequenceHeaderParse = 0;
+ bool isInterlaced = false;
+
+ for (int i = 0; i < buffer.Length; i++)
+ {
+ parse = (parse << 8) + buffer.ReadByte();
+
+ if (parse == 0x0000010D)
+ {
+ frameHeaderParse = 4;
+ }
+ else if (frameHeaderParse > 0)
+ {
+ --frameHeaderParse;
+ if (frameHeaderParse == 0)
+ {
+ uint pictureType = 0;
+ if (isInterlaced)
+ {
+ if ((parse & 0x80000000) == 0)
+ {
+ pictureType =
+ (uint)((parse & 0x78000000) >> 13);
+ }
+ else
+ {
+ pictureType =
+ (uint)((parse & 0x3c000000) >> 12);
+ }
+ }
+ else
+ {
+ pictureType =
+ (uint)((parse & 0xf0000000) >> 14);
+ }
+
+ if ((pictureType & 0x20000) == 0)
+ {
+ tag = "P";
+ }
+ else if ((pictureType & 0x10000) == 0)
+ {
+ tag = "B";
+ }
+ else if ((pictureType & 0x8000) == 0)
+ {
+ tag = "I";
+ }
+ else if ((pictureType & 0x4000) == 0)
+ {
+ tag = "BI";
+ }
+ else
+ {
+ tag = null;
+ }
+ if (stream.IsInitialized) return;
+ }
+ }
+ else if (parse == 0x0000010F)
+ {
+ sequenceHeaderParse = 6;
+ }
+ else if (sequenceHeaderParse > 0)
+ {
+ --sequenceHeaderParse;
+ switch (sequenceHeaderParse)
+ {
+ case 5:
+ int profileLevel = ((parse & 0x38) >> 3);
+ if (((parse & 0xC0) >> 6) == 3)
+ {
+ stream.EncodingProfile = string.Format(
+ "Advanced Profile {0}", profileLevel);
+ }
+ else
+ {
+ stream.EncodingProfile = string.Format(
+ "Main Profile {0}", profileLevel);
+ }
+ break;
+
+ case 0:
+ if (((parse & 0x40) >> 6) > 0)
+ {
+ isInterlaced = true;
+ }
+ else
+ {
+ isInterlaced = false;
+ }
+ break;
+ }
+ stream.IsVBR = true;
+ stream.IsInitialized = true;
+ }
+ }
+ }
+ }
+}
diff --git a/BDInfo/TSInterleavedFile.cs b/BDInfo/TSInterleavedFile.cs
new file mode 100644
index 0000000000..44a16bbaa6
--- /dev/null
+++ b/BDInfo/TSInterleavedFile.cs
@@ -0,0 +1,38 @@
+//============================================================================
+// BDInfo - Blu-ray Video and Audio Analysis Tool
+// Copyright © 2010 Cinema Squid
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//=============================================================================
+
+using System.IO;
+using MediaBrowser.Model.IO;
+
+// TODO: Do more interesting things here...
+
+namespace BDInfo
+{
+ public class TSInterleavedFile
+ {
+ public FileSystemMetadata FileInfo = null;
+ public string Name = null;
+
+ public TSInterleavedFile(FileSystemMetadata fileInfo)
+ {
+ FileInfo = fileInfo;
+ Name = fileInfo.Name.ToUpper();
+ }
+ }
+}
diff --git a/BDInfo/TSPlaylistFile.cs b/BDInfo/TSPlaylistFile.cs
new file mode 100644
index 0000000000..46d66f513e
--- /dev/null
+++ b/BDInfo/TSPlaylistFile.cs
@@ -0,0 +1,1292 @@
+//============================================================================
+// BDInfo - Blu-ray Video and Audio Analysis Tool
+// Copyright © 2010 Cinema Squid
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//=============================================================================
+
+#undef DEBUG
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Text;
+
+namespace BDInfo
+{
+ public class TSPlaylistFile
+ {
+ private readonly IFileSystem _fileSystem;
+ private readonly ITextEncoding _textEncoding;
+ private FileSystemMetadata FileInfo = null;
+ public string FileType = null;
+ public bool IsInitialized = false;
+ public string Name = null;
+ public BDROM BDROM = null;
+ public bool HasHiddenTracks = false;
+ public bool HasLoops = false;
+ public bool IsCustom = false;
+
+ public List<double> Chapters = new List<double>();
+
+ public Dictionary<ushort, TSStream> Streams =
+ new Dictionary<ushort, TSStream>();
+ public Dictionary<ushort, TSStream> PlaylistStreams =
+ new Dictionary<ushort, TSStream>();
+ public List<TSStreamClip> StreamClips =
+ new List<TSStreamClip>();
+ public List<Dictionary<ushort, TSStream>> AngleStreams =
+ new List<Dictionary<ushort, TSStream>>();
+ public List<Dictionary<double, TSStreamClip>> AngleClips =
+ new List<Dictionary<double, TSStreamClip>>();
+ public int AngleCount = 0;
+
+ public List<TSStream> SortedStreams =
+ new List<TSStream>();
+ public List<TSVideoStream> VideoStreams =
+ new List<TSVideoStream>();
+ public List<TSAudioStream> AudioStreams =
+ new List<TSAudioStream>();
+ public List<TSTextStream> TextStreams =
+ new List<TSTextStream>();
+ public List<TSGraphicsStream> GraphicsStreams =
+ new List<TSGraphicsStream>();
+
+ public TSPlaylistFile(
+ BDROM bdrom,
+ FileSystemMetadata fileInfo, IFileSystem fileSystem, ITextEncoding textEncoding)
+ {
+ BDROM = bdrom;
+ FileInfo = fileInfo;
+ _fileSystem = fileSystem;
+ _textEncoding = textEncoding;
+ Name = fileInfo.Name.ToUpper();
+ }
+
+ public TSPlaylistFile(
+ BDROM bdrom,
+ string name,
+ List<TSStreamClip> clips, IFileSystem fileSystem, ITextEncoding textEncoding)
+ {
+ BDROM = bdrom;
+ Name = name;
+ _fileSystem = fileSystem;
+ _textEncoding = textEncoding;
+ IsCustom = true;
+ foreach (TSStreamClip clip in clips)
+ {
+ TSStreamClip newClip = new TSStreamClip(
+ clip.StreamFile, clip.StreamClipFile);
+
+ newClip.Name = clip.Name;
+ newClip.TimeIn = clip.TimeIn;
+ newClip.TimeOut = clip.TimeOut;
+ newClip.Length = newClip.TimeOut - newClip.TimeIn;
+ newClip.RelativeTimeIn = TotalLength;
+ newClip.RelativeTimeOut = newClip.RelativeTimeIn + newClip.Length;
+ newClip.AngleIndex = clip.AngleIndex;
+ newClip.Chapters.Add(clip.TimeIn);
+ StreamClips.Add(newClip);
+
+ if (newClip.AngleIndex > AngleCount)
+ {
+ AngleCount = newClip.AngleIndex;
+ }
+ if (newClip.AngleIndex == 0)
+ {
+ Chapters.Add(newClip.RelativeTimeIn);
+ }
+ }
+ LoadStreamClips();
+ IsInitialized = true;
+ }
+
+ public override string ToString()
+ {
+ return Name;
+ }
+
+ public ulong InterleavedFileSize
+ {
+ get
+ {
+ ulong size = 0;
+ foreach (TSStreamClip clip in StreamClips)
+ {
+ size += clip.InterleavedFileSize;
+ }
+ return size;
+ }
+ }
+ public ulong FileSize
+ {
+ get
+ {
+ ulong size = 0;
+ foreach (TSStreamClip clip in StreamClips)
+ {
+ size += clip.FileSize;
+ }
+ return size;
+ }
+ }
+ public double TotalLength
+ {
+ get
+ {
+ double length = 0;
+ foreach (TSStreamClip clip in StreamClips)
+ {
+ if (clip.AngleIndex == 0)
+ {
+ length += clip.Length;
+ }
+ }
+ return length;
+ }
+ }
+
+ public double TotalAngleLength
+ {
+ get
+ {
+ double length = 0;
+ foreach (TSStreamClip clip in StreamClips)
+ {
+ length += clip.Length;
+ }
+ return length;
+ }
+ }
+
+ public ulong TotalSize
+ {
+ get
+ {
+ ulong size = 0;
+ foreach (TSStreamClip clip in StreamClips)
+ {
+ if (clip.AngleIndex == 0)
+ {
+ size += clip.PacketSize;
+ }
+ }
+ return size;
+ }
+ }
+
+ public ulong TotalAngleSize
+ {
+ get
+ {
+ ulong size = 0;
+ foreach (TSStreamClip clip in StreamClips)
+ {
+ size += clip.PacketSize;
+ }
+ return size;
+ }
+ }
+
+ public ulong TotalBitRate
+ {
+ get
+ {
+ if (TotalLength > 0)
+ {
+ return (ulong)Math.Round(((TotalSize * 8.0) / TotalLength));
+ }
+ return 0;
+ }
+ }
+
+ public ulong TotalAngleBitRate
+ {
+ get
+ {
+ if (TotalAngleLength > 0)
+ {
+ return (ulong)Math.Round(((TotalAngleSize * 8.0) / TotalAngleLength));
+ }
+ return 0;
+ }
+ }
+
+ public void Scan(
+ Dictionary<string, TSStreamFile> streamFiles,
+ Dictionary<string, TSStreamClipFile> streamClipFiles)
+ {
+ Stream fileStream = null;
+ BinaryReader fileReader = null;
+
+ try
+ {
+ Streams.Clear();
+ StreamClips.Clear();
+
+ fileStream = _fileSystem.OpenRead(FileInfo.FullName);
+ fileReader = new BinaryReader(fileStream);
+
+ byte[] data = new byte[fileStream.Length];
+ int dataLength = fileReader.Read(data, 0, data.Length);
+
+ int pos = 0;
+
+ FileType = ReadString(data, 8, ref pos);
+ if (FileType != "MPLS0100" && FileType != "MPLS0200")
+ {
+ throw new Exception(string.Format(
+ "Playlist {0} has an unknown file type {1}.",
+ FileInfo.Name, FileType));
+ }
+
+ int playlistOffset = ReadInt32(data, ref pos);
+ int chaptersOffset = ReadInt32(data, ref pos);
+ int extensionsOffset = ReadInt32(data, ref pos);
+
+ pos = playlistOffset;
+
+ int playlistLength = ReadInt32(data, ref pos);
+ int playlistReserved = ReadInt16(data, ref pos);
+ int itemCount = ReadInt16(data, ref pos);
+ int subitemCount = ReadInt16(data, ref pos);
+
+ List<TSStreamClip> chapterClips = new List<TSStreamClip>();
+ for (int itemIndex = 0; itemIndex < itemCount; itemIndex++)
+ {
+ int itemStart = pos;
+ int itemLength = ReadInt16(data, ref pos);
+ string itemName = ReadString(data, 5, ref pos);
+ string itemType = ReadString(data, 4, ref pos);
+
+ TSStreamFile streamFile = null;
+ string streamFileName = string.Format(
+ "{0}.M2TS", itemName);
+ if (streamFiles.ContainsKey(streamFileName))
+ {
+ streamFile = streamFiles[streamFileName];
+ }
+ if (streamFile == null)
+ {
+ // Error condition
+ }
+
+ TSStreamClipFile streamClipFile = null;
+ string streamClipFileName = string.Format(
+ "{0}.CLPI", itemName);
+ if (streamClipFiles.ContainsKey(streamClipFileName))
+ {
+ streamClipFile = streamClipFiles[streamClipFileName];
+ }
+ if (streamClipFile == null)
+ {
+ throw new Exception(string.Format(
+ "Playlist {0} referenced missing file {1}.",
+ FileInfo.Name, streamFileName));
+ }
+
+ pos += 1;
+ int multiangle = (data[pos] >> 4) & 0x01;
+ int condition = data[pos] & 0x0F;
+ pos += 2;
+
+ int inTime = ReadInt32(data, ref pos);
+ if (inTime < 0) inTime &= 0x7FFFFFFF;
+ double timeIn = (double)inTime / 45000;
+
+ int outTime = ReadInt32(data, ref pos);
+ if (outTime < 0) outTime &= 0x7FFFFFFF;
+ double timeOut = (double)outTime / 45000;
+
+ TSStreamClip streamClip = new TSStreamClip(
+ streamFile, streamClipFile);
+
+ streamClip.Name = streamFileName; //TODO
+ streamClip.TimeIn = timeIn;
+ streamClip.TimeOut = timeOut;
+ streamClip.Length = streamClip.TimeOut - streamClip.TimeIn;
+ streamClip.RelativeTimeIn = TotalLength;
+ streamClip.RelativeTimeOut = streamClip.RelativeTimeIn + streamClip.Length;
+ StreamClips.Add(streamClip);
+ chapterClips.Add(streamClip);
+
+ pos += 12;
+ if (multiangle > 0)
+ {
+ int angles = data[pos];
+ pos += 2;
+ for (int angle = 0; angle < angles - 1; angle++)
+ {
+ string angleName = ReadString(data, 5, ref pos);
+ string angleType = ReadString(data, 4, ref pos);
+ pos += 1;
+
+ TSStreamFile angleFile = null;
+ string angleFileName = string.Format(
+ "{0}.M2TS", angleName);
+ if (streamFiles.ContainsKey(angleFileName))
+ {
+ angleFile = streamFiles[angleFileName];
+ }
+ if (angleFile == null)
+ {
+ throw new Exception(string.Format(
+ "Playlist {0} referenced missing angle file {1}.",
+ FileInfo.Name, angleFileName));
+ }
+
+ TSStreamClipFile angleClipFile = null;
+ string angleClipFileName = string.Format(
+ "{0}.CLPI", angleName);
+ if (streamClipFiles.ContainsKey(angleClipFileName))
+ {
+ angleClipFile = streamClipFiles[angleClipFileName];
+ }
+ if (angleClipFile == null)
+ {
+ throw new Exception(string.Format(
+ "Playlist {0} referenced missing angle file {1}.",
+ FileInfo.Name, angleClipFileName));
+ }
+
+ TSStreamClip angleClip =
+ new TSStreamClip(angleFile, angleClipFile);
+ angleClip.AngleIndex = angle + 1;
+ angleClip.TimeIn = streamClip.TimeIn;
+ angleClip.TimeOut = streamClip.TimeOut;
+ angleClip.RelativeTimeIn = streamClip.RelativeTimeIn;
+ angleClip.RelativeTimeOut = streamClip.RelativeTimeOut;
+ angleClip.Length = streamClip.Length;
+ StreamClips.Add(angleClip);
+ }
+ if (angles - 1 > AngleCount) AngleCount = angles - 1;
+ }
+
+ int streamInfoLength = ReadInt16(data, ref pos);
+ pos += 2;
+ int streamCountVideo = data[pos++];
+ int streamCountAudio = data[pos++];
+ int streamCountPG = data[pos++];
+ int streamCountIG = data[pos++];
+ int streamCountSecondaryAudio = data[pos++];
+ int streamCountSecondaryVideo = data[pos++];
+ int streamCountPIP = data[pos++];
+ pos += 5;
+
+#if DEBUG
+ Debug.WriteLine(string.Format(
+ "{0} : {1} -> V:{2} A:{3} PG:{4} IG:{5} 2A:{6} 2V:{7} PIP:{8}",
+ Name, streamFileName, streamCountVideo, streamCountAudio, streamCountPG, streamCountIG,
+ streamCountSecondaryAudio, streamCountSecondaryVideo, streamCountPIP));
+#endif
+
+ for (int i = 0; i < streamCountVideo; i++)
+ {
+ TSStream stream = CreatePlaylistStream(data, ref pos);
+ if (stream != null) PlaylistStreams[stream.PID] = stream;
+ }
+ for (int i = 0; i < streamCountAudio; i++)
+ {
+ TSStream stream = CreatePlaylistStream(data, ref pos);
+ if (stream != null) PlaylistStreams[stream.PID] = stream;
+ }
+ for (int i = 0; i < streamCountPG; i++)
+ {
+ TSStream stream = CreatePlaylistStream(data, ref pos);
+ if (stream != null) PlaylistStreams[stream.PID] = stream;
+ }
+ for (int i = 0; i < streamCountIG; i++)
+ {
+ TSStream stream = CreatePlaylistStream(data, ref pos);
+ if (stream != null) PlaylistStreams[stream.PID] = stream;
+ }
+ for (int i = 0; i < streamCountSecondaryAudio; i++)
+ {
+ TSStream stream = CreatePlaylistStream(data, ref pos);
+ if (stream != null) PlaylistStreams[stream.PID] = stream;
+ pos += 2;
+ }
+ for (int i = 0; i < streamCountSecondaryVideo; i++)
+ {
+ TSStream stream = CreatePlaylistStream(data, ref pos);
+ if (stream != null) PlaylistStreams[stream.PID] = stream;
+ pos += 6;
+ }
+ /*
+ * TODO
+ *
+ for (int i = 0; i < streamCountPIP; i++)
+ {
+ TSStream stream = CreatePlaylistStream(data, ref pos);
+ if (stream != null) PlaylistStreams[stream.PID] = stream;
+ }
+ */
+
+ pos += itemLength - (pos - itemStart) + 2;
+ }
+
+ pos = chaptersOffset + 4;
+
+ int chapterCount = ReadInt16(data, ref pos);
+
+ for (int chapterIndex = 0;
+ chapterIndex < chapterCount;
+ chapterIndex++)
+ {
+ int chapterType = data[pos+1];
+
+ if (chapterType == 1)
+ {
+ int streamFileIndex =
+ ((int)data[pos + 2] << 8) + data[pos + 3];
+
+ long chapterTime =
+ ((long)data[pos + 4] << 24) +
+ ((long)data[pos + 5] << 16) +
+ ((long)data[pos + 6] << 8) +
+ ((long)data[pos + 7]);
+
+ TSStreamClip streamClip = chapterClips[streamFileIndex];
+
+ double chapterSeconds = (double)chapterTime / 45000;
+
+ double relativeSeconds =
+ chapterSeconds -
+ streamClip.TimeIn +
+ streamClip.RelativeTimeIn;
+
+ // TODO: Ignore short last chapter?
+ if (TotalLength - relativeSeconds > 1.0)
+ {
+ streamClip.Chapters.Add(chapterSeconds);
+ this.Chapters.Add(relativeSeconds);
+ }
+ }
+ else
+ {
+ // TODO: Handle other chapter types?
+ }
+ pos += 14;
+ }
+ }
+ finally
+ {
+ if (fileReader != null)
+ {
+ fileReader.Dispose();
+ }
+ if (fileStream != null)
+ {
+ fileStream.Dispose();
+ }
+ }
+ }
+
+ public void Initialize()
+ {
+ LoadStreamClips();
+
+ Dictionary<string, List<double>> clipTimes = new Dictionary<string, List<double>>();
+ foreach (TSStreamClip clip in StreamClips)
+ {
+ if (clip.AngleIndex == 0)
+ {
+ if (clipTimes.ContainsKey(clip.Name))
+ {
+ if (clipTimes[clip.Name].Contains(clip.TimeIn))
+ {
+ HasLoops = true;
+ break;
+ }
+ else
+ {
+ clipTimes[clip.Name].Add(clip.TimeIn);
+ }
+ }
+ else
+ {
+ clipTimes[clip.Name] = new List<double> { clip.TimeIn };
+ }
+ }
+ }
+ ClearBitrates();
+ IsInitialized = true;
+ }
+
+ protected TSStream CreatePlaylistStream(byte[] data, ref int pos)
+ {
+ TSStream stream = null;
+
+ int start = pos;
+
+ int headerLength = data[pos++];
+ int headerPos = pos;
+ int headerType = data[pos++];
+
+ int pid = 0;
+ int subpathid = 0;
+ int subclipid = 0;
+
+ switch (headerType)
+ {
+ case 1:
+ pid = ReadInt16(data, ref pos);
+ break;
+ case 2:
+ subpathid = data[pos++];
+ subclipid = data[pos++];
+ pid = ReadInt16(data, ref pos);
+ break;
+ case 3:
+ subpathid = data[pos++];
+ pid = ReadInt16(data, ref pos);
+ break;
+ case 4:
+ subpathid = data[pos++];
+ subclipid = data[pos++];
+ pid = ReadInt16(data, ref pos);
+ break;
+ default:
+ break;
+ }
+
+ pos = headerPos + headerLength;
+
+ int streamLength = data[pos++];
+ int streamPos = pos;
+
+ TSStreamType streamType = (TSStreamType)data[pos++];
+ switch (streamType)
+ {
+ case TSStreamType.MVC_VIDEO:
+ // TODO
+ break;
+
+ case TSStreamType.AVC_VIDEO:
+ case TSStreamType.MPEG1_VIDEO:
+ case TSStreamType.MPEG2_VIDEO:
+ case TSStreamType.VC1_VIDEO:
+
+ TSVideoFormat videoFormat = (TSVideoFormat)
+ (data[pos] >> 4);
+ TSFrameRate frameRate = (TSFrameRate)
+ (data[pos] & 0xF);
+ TSAspectRatio aspectRatio = (TSAspectRatio)
+ (data[pos + 1] >> 4);
+
+ stream = new TSVideoStream();
+ ((TSVideoStream)stream).VideoFormat = videoFormat;
+ ((TSVideoStream)stream).AspectRatio = aspectRatio;
+ ((TSVideoStream)stream).FrameRate = frameRate;
+
+#if DEBUG
+ Debug.WriteLine(string.Format(
+ "\t{0} {1} {2} {3} {4}",
+ pid,
+ streamType,
+ videoFormat,
+ frameRate,
+ aspectRatio));
+#endif
+
+ break;
+
+ case TSStreamType.AC3_AUDIO:
+ case TSStreamType.AC3_PLUS_AUDIO:
+ case TSStreamType.AC3_PLUS_SECONDARY_AUDIO:
+ case TSStreamType.AC3_TRUE_HD_AUDIO:
+ case TSStreamType.DTS_AUDIO:
+ case TSStreamType.DTS_HD_AUDIO:
+ case TSStreamType.DTS_HD_MASTER_AUDIO:
+ case TSStreamType.DTS_HD_SECONDARY_AUDIO:
+ case TSStreamType.LPCM_AUDIO:
+ case TSStreamType.MPEG1_AUDIO:
+ case TSStreamType.MPEG2_AUDIO:
+
+ int audioFormat = ReadByte(data, ref pos);
+
+ TSChannelLayout channelLayout = (TSChannelLayout)
+ (audioFormat >> 4);
+ TSSampleRate sampleRate = (TSSampleRate)
+ (audioFormat & 0xF);
+
+ string audioLanguage = ReadString(data, 3, ref pos);
+
+ stream = new TSAudioStream();
+ ((TSAudioStream)stream).ChannelLayout = channelLayout;
+ ((TSAudioStream)stream).SampleRate = TSAudioStream.ConvertSampleRate(sampleRate);
+ ((TSAudioStream)stream).LanguageCode = audioLanguage;
+
+#if DEBUG
+ Debug.WriteLine(string.Format(
+ "\t{0} {1} {2} {3} {4}",
+ pid,
+ streamType,
+ audioLanguage,
+ channelLayout,
+ sampleRate));
+#endif
+
+ break;
+
+ case TSStreamType.INTERACTIVE_GRAPHICS:
+ case TSStreamType.PRESENTATION_GRAPHICS:
+
+ string graphicsLanguage = ReadString(data, 3, ref pos);
+
+ stream = new TSGraphicsStream();
+ ((TSGraphicsStream)stream).LanguageCode = graphicsLanguage;
+
+ if (data[pos] != 0)
+ {
+ }
+
+#if DEBUG
+ Debug.WriteLine(string.Format(
+ "\t{0} {1} {2}",
+ pid,
+ streamType,
+ graphicsLanguage));
+#endif
+
+ break;
+
+ case TSStreamType.SUBTITLE:
+
+ int code = ReadByte(data, ref pos); // TODO
+ string textLanguage = ReadString(data, 3, ref pos);
+
+ stream = new TSTextStream();
+ ((TSTextStream)stream).LanguageCode = textLanguage;
+
+#if DEBUG
+ Debug.WriteLine(string.Format(
+ "\t{0} {1} {2}",
+ pid,
+ streamType,
+ textLanguage));
+#endif
+
+ break;
+
+ default:
+ break;
+ }
+
+ pos = streamPos + streamLength;
+
+ if (stream != null)
+ {
+ stream.PID = (ushort)pid;
+ stream.StreamType = streamType;
+ }
+
+ return stream;
+ }
+
+ private void LoadStreamClips()
+ {
+ AngleClips.Clear();
+ if (AngleCount > 0)
+ {
+ for (int angleIndex = 0; angleIndex < AngleCount; angleIndex++)
+ {
+ AngleClips.Add(new Dictionary<double, TSStreamClip>());
+ }
+ }
+
+ TSStreamClip referenceClip = null;
+ if (StreamClips.Count > 0)
+ {
+ referenceClip = StreamClips[0];
+ }
+ foreach (TSStreamClip clip in StreamClips)
+ {
+ if (clip.StreamClipFile.Streams.Count > referenceClip.StreamClipFile.Streams.Count)
+ {
+ referenceClip = clip;
+ }
+ else if (clip.Length > referenceClip.Length)
+ {
+ referenceClip = clip;
+ }
+ if (AngleCount > 0)
+ {
+ if (clip.AngleIndex == 0)
+ {
+ for (int angleIndex = 0; angleIndex < AngleCount; angleIndex++)
+ {
+ AngleClips[angleIndex][clip.RelativeTimeIn] = clip;
+ }
+ }
+ else
+ {
+ AngleClips[clip.AngleIndex - 1][clip.RelativeTimeIn] = clip;
+ }
+ }
+ }
+
+ foreach (TSStream clipStream
+ in referenceClip.StreamClipFile.Streams.Values)
+ {
+ if (!Streams.ContainsKey(clipStream.PID))
+ {
+ TSStream stream = clipStream.Clone();
+ Streams[clipStream.PID] = stream;
+
+ if (!IsCustom && !PlaylistStreams.ContainsKey(stream.PID))
+ {
+ stream.IsHidden = true;
+ HasHiddenTracks = true;
+ }
+
+ if (stream.IsVideoStream)
+ {
+ VideoStreams.Add((TSVideoStream)stream);
+ }
+ else if (stream.IsAudioStream)
+ {
+ AudioStreams.Add((TSAudioStream)stream);
+ }
+ else if (stream.IsGraphicsStream)
+ {
+ GraphicsStreams.Add((TSGraphicsStream)stream);
+ }
+ else if (stream.IsTextStream)
+ {
+ TextStreams.Add((TSTextStream)stream);
+ }
+ }
+ }
+
+ if (referenceClip.StreamFile != null)
+ {
+ // TODO: Better way to add this in?
+ if (BDInfoSettings.EnableSSIF &&
+ referenceClip.StreamFile.InterleavedFile != null &&
+ referenceClip.StreamFile.Streams.ContainsKey(4114) &&
+ !Streams.ContainsKey(4114))
+ {
+ TSStream stream = referenceClip.StreamFile.Streams[4114].Clone();
+ Streams[4114] = stream;
+ if (stream.IsVideoStream)
+ {
+ VideoStreams.Add((TSVideoStream)stream);
+ }
+ }
+
+ foreach (TSStream clipStream
+ in referenceClip.StreamFile.Streams.Values)
+ {
+ if (Streams.ContainsKey(clipStream.PID))
+ {
+ TSStream stream = Streams[clipStream.PID];
+
+ if (stream.StreamType != clipStream.StreamType) continue;
+
+ if (clipStream.BitRate > stream.BitRate)
+ {
+ stream.BitRate = clipStream.BitRate;
+ }
+ stream.IsVBR = clipStream.IsVBR;
+
+ if (stream.IsVideoStream &&
+ clipStream.IsVideoStream)
+ {
+ ((TSVideoStream)stream).EncodingProfile =
+ ((TSVideoStream)clipStream).EncodingProfile;
+ }
+ else if (stream.IsAudioStream &&
+ clipStream.IsAudioStream)
+ {
+ TSAudioStream audioStream = (TSAudioStream)stream;
+ TSAudioStream clipAudioStream = (TSAudioStream)clipStream;
+
+ if (clipAudioStream.ChannelCount > audioStream.ChannelCount)
+ {
+ audioStream.ChannelCount = clipAudioStream.ChannelCount;
+ }
+ if (clipAudioStream.LFE > audioStream.LFE)
+ {
+ audioStream.LFE = clipAudioStream.LFE;
+ }
+ if (clipAudioStream.SampleRate > audioStream.SampleRate)
+ {
+ audioStream.SampleRate = clipAudioStream.SampleRate;
+ }
+ if (clipAudioStream.BitDepth > audioStream.BitDepth)
+ {
+ audioStream.BitDepth = clipAudioStream.BitDepth;
+ }
+ if (clipAudioStream.DialNorm < audioStream.DialNorm)
+ {
+ audioStream.DialNorm = clipAudioStream.DialNorm;
+ }
+ if (clipAudioStream.AudioMode != TSAudioMode.Unknown)
+ {
+ audioStream.AudioMode = clipAudioStream.AudioMode;
+ }
+ if (clipAudioStream.CoreStream != null &&
+ audioStream.CoreStream == null)
+ {
+ audioStream.CoreStream = (TSAudioStream)
+ clipAudioStream.CoreStream.Clone();
+ }
+ }
+ }
+ }
+ }
+
+ for (int i = 0; i < AngleCount; i++)
+ {
+ AngleStreams.Add(new Dictionary<ushort, TSStream>());
+ }
+
+ if (!BDInfoSettings.KeepStreamOrder)
+ {
+ VideoStreams.Sort(CompareVideoStreams);
+ }
+ foreach (TSStream stream in VideoStreams)
+ {
+ SortedStreams.Add(stream);
+ for (int i = 0; i < AngleCount; i++)
+ {
+ TSStream angleStream = stream.Clone();
+ angleStream.AngleIndex = i + 1;
+ AngleStreams[i][angleStream.PID] = angleStream;
+ SortedStreams.Add(angleStream);
+ }
+ }
+
+ if (!BDInfoSettings.KeepStreamOrder)
+ {
+ AudioStreams.Sort(CompareAudioStreams);
+ }
+ foreach (TSStream stream in AudioStreams)
+ {
+ SortedStreams.Add(stream);
+ }
+
+ if (!BDInfoSettings.KeepStreamOrder)
+ {
+ GraphicsStreams.Sort(CompareGraphicsStreams);
+ }
+ foreach (TSStream stream in GraphicsStreams)
+ {
+ SortedStreams.Add(stream);
+ }
+
+ if (!BDInfoSettings.KeepStreamOrder)
+ {
+ TextStreams.Sort(CompareTextStreams);
+ }
+ foreach (TSStream stream in TextStreams)
+ {
+ SortedStreams.Add(stream);
+ }
+ }
+
+ public void ClearBitrates()
+ {
+ foreach (TSStreamClip clip in StreamClips)
+ {
+ clip.PayloadBytes = 0;
+ clip.PacketCount = 0;
+ clip.PacketSeconds = 0;
+
+ if (clip.StreamFile != null)
+ {
+ foreach (TSStream stream in clip.StreamFile.Streams.Values)
+ {
+ stream.PayloadBytes = 0;
+ stream.PacketCount = 0;
+ stream.PacketSeconds = 0;
+ }
+
+ if (clip.StreamFile != null &&
+ clip.StreamFile.StreamDiagnostics != null)
+ {
+ clip.StreamFile.StreamDiagnostics.Clear();
+ }
+ }
+ }
+
+ foreach (TSStream stream in SortedStreams)
+ {
+ stream.PayloadBytes = 0;
+ stream.PacketCount = 0;
+ stream.PacketSeconds = 0;
+ }
+ }
+
+ public bool IsValid
+ {
+ get
+ {
+ if (!IsInitialized) return false;
+
+ if (BDInfoSettings.FilterShortPlaylists &&
+ TotalLength < BDInfoSettings.FilterShortPlaylistsValue)
+ {
+ return false;
+ }
+
+ if (HasLoops &&
+ BDInfoSettings.FilterLoopingPlaylists)
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ public static int CompareVideoStreams(
+ TSVideoStream x,
+ TSVideoStream y)
+ {
+ if (x == null && y == null)
+ {
+ return 0;
+ }
+ else if (x == null && y != null)
+ {
+ return 1;
+ }
+ else if (x != null && y == null)
+ {
+ return -1;
+ }
+ else
+ {
+ if (x.Height > y.Height)
+ {
+ return -1;
+ }
+ else if (y.Height > x.Height)
+ {
+ return 1;
+ }
+ else if (x.PID > y.PID)
+ {
+ return 1;
+ }
+ else if (y.PID > x.PID)
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ }
+
+ public static int CompareAudioStreams(
+ TSAudioStream x,
+ TSAudioStream y)
+ {
+ if (x == y)
+ {
+ return 0;
+ }
+ else if (x == null && y == null)
+ {
+ return 0;
+ }
+ else if (x == null && y != null)
+ {
+ return -1;
+ }
+ else if (x != null && y == null)
+ {
+ return 1;
+ }
+ else
+ {
+ if (x.ChannelCount > y.ChannelCount)
+ {
+ return -1;
+ }
+ else if (y.ChannelCount > x.ChannelCount)
+ {
+ return 1;
+ }
+ else
+ {
+ int sortX = GetStreamTypeSortIndex(x.StreamType);
+ int sortY = GetStreamTypeSortIndex(y.StreamType);
+
+ if (sortX > sortY)
+ {
+ return -1;
+ }
+ else if (sortY > sortX)
+ {
+ return 1;
+ }
+ else
+ {
+ if (x.LanguageCode == "eng")
+ {
+ return -1;
+ }
+ else if (y.LanguageCode == "eng")
+ {
+ return 1;
+ }
+ else if (x.LanguageCode != y.LanguageCode)
+ {
+ return string.Compare(
+ x.LanguageName, y.LanguageName);
+ }
+ else if (x.PID < y.PID)
+ {
+ return -1;
+ }
+ else if (y.PID < x.PID)
+ {
+ return 1;
+ }
+ return 0;
+ }
+ }
+ }
+ }
+
+ public static int CompareTextStreams(
+ TSTextStream x,
+ TSTextStream y)
+ {
+ if (x == y)
+ {
+ return 0;
+ }
+ else if (x == null && y == null)
+ {
+ return 0;
+ }
+ else if (x == null && y != null)
+ {
+ return -1;
+ }
+ else if (x != null && y == null)
+ {
+ return 1;
+ }
+ else
+ {
+ if (x.LanguageCode == "eng")
+ {
+ return -1;
+ }
+ else if (y.LanguageCode == "eng")
+ {
+ return 1;
+ }
+ else
+ {
+ if (x.LanguageCode == y.LanguageCode)
+ {
+ if (x.PID > y.PID)
+ {
+ return 1;
+ }
+ else if (y.PID > x.PID)
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ else
+ {
+ return string.Compare(
+ x.LanguageName, y.LanguageName);
+ }
+ }
+ }
+ }
+
+ private static int CompareGraphicsStreams(
+ TSGraphicsStream x,
+ TSGraphicsStream y)
+ {
+ if (x == y)
+ {
+ return 0;
+ }
+ else if (x == null && y == null)
+ {
+ return 0;
+ }
+ else if (x == null && y != null)
+ {
+ return -1;
+ }
+ else if (x != null && y == null)
+ {
+ return 1;
+ }
+ else
+ {
+ int sortX = GetStreamTypeSortIndex(x.StreamType);
+ int sortY = GetStreamTypeSortIndex(y.StreamType);
+
+ if (sortX > sortY)
+ {
+ return -1;
+ }
+ else if (sortY > sortX)
+ {
+ return 1;
+ }
+ else if (x.LanguageCode == "eng")
+ {
+ return -1;
+ }
+ else if (y.LanguageCode == "eng")
+ {
+ return 1;
+ }
+ else
+ {
+ if (x.LanguageCode == y.LanguageCode)
+ {
+ if (x.PID > y.PID)
+ {
+ return 1;
+ }
+ else if (y.PID > x.PID)
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ else
+ {
+ return string.Compare(x.LanguageName, y.LanguageName);
+ }
+ }
+ }
+ }
+
+ private static int GetStreamTypeSortIndex(TSStreamType streamType)
+ {
+ switch (streamType)
+ {
+ case TSStreamType.Unknown:
+ return 0;
+ case TSStreamType.MPEG1_VIDEO:
+ return 1;
+ case TSStreamType.MPEG2_VIDEO:
+ return 2;
+ case TSStreamType.AVC_VIDEO:
+ return 3;
+ case TSStreamType.VC1_VIDEO:
+ return 4;
+ case TSStreamType.MVC_VIDEO:
+ return 5;
+
+ case TSStreamType.MPEG1_AUDIO:
+ return 1;
+ case TSStreamType.MPEG2_AUDIO:
+ return 2;
+ case TSStreamType.AC3_PLUS_SECONDARY_AUDIO:
+ return 3;
+ case TSStreamType.DTS_HD_SECONDARY_AUDIO:
+ return 4;
+ case TSStreamType.AC3_AUDIO:
+ return 5;
+ case TSStreamType.DTS_AUDIO:
+ return 6;
+ case TSStreamType.AC3_PLUS_AUDIO:
+ return 7;
+ case TSStreamType.DTS_HD_AUDIO:
+ return 8;
+ case TSStreamType.AC3_TRUE_HD_AUDIO:
+ return 9;
+ case TSStreamType.DTS_HD_MASTER_AUDIO:
+ return 10;
+ case TSStreamType.LPCM_AUDIO:
+ return 11;
+
+ case TSStreamType.SUBTITLE:
+ return 1;
+ case TSStreamType.INTERACTIVE_GRAPHICS:
+ return 2;
+ case TSStreamType.PRESENTATION_GRAPHICS:
+ return 3;
+
+ default:
+ return 0;
+ }
+ }
+
+ protected string ReadString(
+ byte[] data,
+ int count,
+ ref int pos)
+ {
+ string val =
+ _textEncoding.GetASCIIEncoding().GetString(data, pos, count);
+
+ pos += count;
+
+ return val;
+ }
+
+ protected int ReadInt32(
+ byte[] data,
+ ref int pos)
+ {
+ int val =
+ ((int)data[pos] << 24) +
+ ((int)data[pos + 1] << 16) +
+ ((int)data[pos + 2] << 8) +
+ ((int)data[pos + 3]);
+
+ pos += 4;
+
+ return val;
+ }
+
+ protected int ReadInt16(
+ byte[] data,
+ ref int pos)
+ {
+ int val =
+ ((int)data[pos] << 8) +
+ ((int)data[pos + 1]);
+
+ pos += 2;
+
+ return val;
+ }
+
+ protected byte ReadByte(
+ byte[] data,
+ ref int pos)
+ {
+ return data[pos++];
+ }
+ }
+}
diff --git a/BDInfo/TSStream.cs b/BDInfo/TSStream.cs
new file mode 100644
index 0000000000..5afb81c5e4
--- /dev/null
+++ b/BDInfo/TSStream.cs
@@ -0,0 +1,801 @@
+//============================================================================
+// BDInfo - Blu-ray Video and Audio Analysis Tool
+// Copyright © 2010 Cinema Squid
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//=============================================================================
+
+using System;
+using System.Collections.Generic;
+
+namespace BDInfo
+{
+ public enum TSStreamType : byte
+ {
+ Unknown = 0,
+ MPEG1_VIDEO = 0x01,
+ MPEG2_VIDEO = 0x02,
+ AVC_VIDEO = 0x1b,
+ MVC_VIDEO = 0x20,
+ VC1_VIDEO = 0xea,
+ MPEG1_AUDIO = 0x03,
+ MPEG2_AUDIO = 0x04,
+ LPCM_AUDIO = 0x80,
+ AC3_AUDIO = 0x81,
+ AC3_PLUS_AUDIO = 0x84,
+ AC3_PLUS_SECONDARY_AUDIO = 0xA1,
+ AC3_TRUE_HD_AUDIO = 0x83,
+ DTS_AUDIO = 0x82,
+ DTS_HD_AUDIO = 0x85,
+ DTS_HD_SECONDARY_AUDIO = 0xA2,
+ DTS_HD_MASTER_AUDIO = 0x86,
+ PRESENTATION_GRAPHICS = 0x90,
+ INTERACTIVE_GRAPHICS = 0x91,
+ SUBTITLE = 0x92
+ }
+
+ public enum TSVideoFormat : byte
+ {
+ Unknown = 0,
+ VIDEOFORMAT_480i = 1,
+ VIDEOFORMAT_576i = 2,
+ VIDEOFORMAT_480p = 3,
+ VIDEOFORMAT_1080i = 4,
+ VIDEOFORMAT_720p = 5,
+ VIDEOFORMAT_1080p = 6,
+ VIDEOFORMAT_576p = 7,
+ }
+
+ public enum TSFrameRate : byte
+ {
+ Unknown = 0,
+ FRAMERATE_23_976 = 1,
+ FRAMERATE_24 = 2,
+ FRAMERATE_25 = 3,
+ FRAMERATE_29_97 = 4,
+ FRAMERATE_50 = 6,
+ FRAMERATE_59_94 = 7
+ }
+
+ public enum TSChannelLayout : byte
+ {
+ Unknown = 0,
+ CHANNELLAYOUT_MONO = 1,
+ CHANNELLAYOUT_STEREO = 3,
+ CHANNELLAYOUT_MULTI = 6,
+ CHANNELLAYOUT_COMBO = 12
+ }
+
+ public enum TSSampleRate : byte
+ {
+ Unknown = 0,
+ SAMPLERATE_48 = 1,
+ SAMPLERATE_96 = 4,
+ SAMPLERATE_192 = 5,
+ SAMPLERATE_48_192 = 12,
+ SAMPLERATE_48_96 = 14
+ }
+
+ public enum TSAspectRatio
+ {
+ Unknown = 0,
+ ASPECT_4_3 = 2,
+ ASPECT_16_9 = 3,
+ ASPECT_2_21 = 4
+ }
+
+ public class TSDescriptor
+ {
+ public byte Name;
+ public byte[] Value;
+
+ public TSDescriptor(byte name, byte length)
+ {
+ Name = name;
+ Value = new byte[length];
+ }
+
+ public TSDescriptor Clone()
+ {
+ TSDescriptor descriptor =
+ new TSDescriptor(Name, (byte)Value.Length);
+ Value.CopyTo(descriptor.Value, 0);
+ return descriptor;
+ }
+ }
+
+ public abstract class TSStream
+ {
+ public TSStream()
+ {
+ }
+
+ public override string ToString()
+ {
+ return string.Format("{0} ({1})", CodecShortName, PID);
+ }
+
+ public ushort PID;
+ public TSStreamType StreamType;
+ public List<TSDescriptor> Descriptors = null;
+ public long BitRate = 0;
+ public long ActiveBitRate = 0;
+ public bool IsVBR = false;
+ public bool IsInitialized = false;
+ public string LanguageName;
+ public bool IsHidden = false;
+
+ public ulong PayloadBytes = 0;
+ public ulong PacketCount = 0;
+ public double PacketSeconds = 0;
+ public int AngleIndex = 0;
+
+ public ulong PacketSize
+ {
+ get
+ {
+ return PacketCount * 192;
+ }
+ }
+
+ private string _LanguageCode;
+ public string LanguageCode
+ {
+ get
+ {
+ return _LanguageCode;
+ }
+ set
+ {
+ _LanguageCode = value;
+ LanguageName = LanguageCodes.GetName(value);
+ }
+ }
+
+ public bool IsVideoStream
+ {
+ get
+ {
+ switch (StreamType)
+ {
+ case TSStreamType.MPEG1_VIDEO:
+ case TSStreamType.MPEG2_VIDEO:
+ case TSStreamType.AVC_VIDEO:
+ case TSStreamType.MVC_VIDEO:
+ case TSStreamType.VC1_VIDEO:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+ }
+
+ public bool IsAudioStream
+ {
+ get
+ {
+ switch (StreamType)
+ {
+ case TSStreamType.MPEG1_AUDIO:
+ case TSStreamType.MPEG2_AUDIO:
+ case TSStreamType.LPCM_AUDIO:
+ case TSStreamType.AC3_AUDIO:
+ case TSStreamType.AC3_PLUS_AUDIO:
+ case TSStreamType.AC3_PLUS_SECONDARY_AUDIO:
+ case TSStreamType.AC3_TRUE_HD_AUDIO:
+ case TSStreamType.DTS_AUDIO:
+ case TSStreamType.DTS_HD_AUDIO:
+ case TSStreamType.DTS_HD_SECONDARY_AUDIO:
+ case TSStreamType.DTS_HD_MASTER_AUDIO:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+ }
+
+ public bool IsGraphicsStream
+ {
+ get
+ {
+ switch (StreamType)
+ {
+ case TSStreamType.PRESENTATION_GRAPHICS:
+ case TSStreamType.INTERACTIVE_GRAPHICS:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+ }
+
+ public bool IsTextStream
+ {
+ get
+ {
+ switch (StreamType)
+ {
+ case TSStreamType.SUBTITLE:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+ }
+
+ public string CodecName
+ {
+ get
+ {
+ switch (StreamType)
+ {
+ case TSStreamType.MPEG1_VIDEO:
+ return "MPEG-1 Video";
+ case TSStreamType.MPEG2_VIDEO:
+ return "MPEG-2 Video";
+ case TSStreamType.AVC_VIDEO:
+ return "MPEG-4 AVC Video";
+ case TSStreamType.MVC_VIDEO:
+ return "MPEG-4 MVC Video";
+ case TSStreamType.VC1_VIDEO:
+ return "VC-1 Video";
+ case TSStreamType.MPEG1_AUDIO:
+ return "MP1 Audio";
+ case TSStreamType.MPEG2_AUDIO:
+ return "MP2 Audio";
+ case TSStreamType.LPCM_AUDIO:
+ return "LPCM Audio";
+ case TSStreamType.AC3_AUDIO:
+ if (((TSAudioStream)this).AudioMode == TSAudioMode.Extended)
+ return "Dolby Digital EX Audio";
+ else
+ return "Dolby Digital Audio";
+ case TSStreamType.AC3_PLUS_AUDIO:
+ case TSStreamType.AC3_PLUS_SECONDARY_AUDIO:
+ return "Dolby Digital Plus Audio";
+ case TSStreamType.AC3_TRUE_HD_AUDIO:
+ return "Dolby TrueHD Audio";
+ case TSStreamType.DTS_AUDIO:
+ if (((TSAudioStream)this).AudioMode == TSAudioMode.Extended)
+ return "DTS-ES Audio";
+ else
+ return "DTS Audio";
+ case TSStreamType.DTS_HD_AUDIO:
+ return "DTS-HD High-Res Audio";
+ case TSStreamType.DTS_HD_SECONDARY_AUDIO:
+ return "DTS Express";
+ case TSStreamType.DTS_HD_MASTER_AUDIO:
+ return "DTS-HD Master Audio";
+ case TSStreamType.PRESENTATION_GRAPHICS:
+ return "Presentation Graphics";
+ case TSStreamType.INTERACTIVE_GRAPHICS:
+ return "Interactive Graphics";
+ case TSStreamType.SUBTITLE:
+ return "Subtitle";
+ default:
+ return "UNKNOWN";
+ }
+ }
+ }
+
+ public string CodecAltName
+ {
+ get
+ {
+ switch (StreamType)
+ {
+ case TSStreamType.MPEG1_VIDEO:
+ return "MPEG-1";
+ case TSStreamType.MPEG2_VIDEO:
+ return "MPEG-2";
+ case TSStreamType.AVC_VIDEO:
+ return "AVC";
+ case TSStreamType.MVC_VIDEO:
+ return "MVC";
+ case TSStreamType.VC1_VIDEO:
+ return "VC-1";
+ case TSStreamType.MPEG1_AUDIO:
+ return "MP1";
+ case TSStreamType.MPEG2_AUDIO:
+ return "MP2";
+ case TSStreamType.LPCM_AUDIO:
+ return "LPCM";
+ case TSStreamType.AC3_AUDIO:
+ return "DD AC3";
+ case TSStreamType.AC3_PLUS_AUDIO:
+ case TSStreamType.AC3_PLUS_SECONDARY_AUDIO:
+ return "DD AC3+";
+ case TSStreamType.AC3_TRUE_HD_AUDIO:
+ return "Dolby TrueHD";
+ case TSStreamType.DTS_AUDIO:
+ return "DTS";
+ case TSStreamType.DTS_HD_AUDIO:
+ return "DTS-HD Hi-Res";
+ case TSStreamType.DTS_HD_SECONDARY_AUDIO:
+ return "DTS Express";
+ case TSStreamType.DTS_HD_MASTER_AUDIO:
+ return "DTS-HD Master";
+ case TSStreamType.PRESENTATION_GRAPHICS:
+ return "PGS";
+ case TSStreamType.INTERACTIVE_GRAPHICS:
+ return "IGS";
+ case TSStreamType.SUBTITLE:
+ return "SUB";
+ default:
+ return "UNKNOWN";
+ }
+ }
+ }
+
+ public string CodecShortName
+ {
+ get
+ {
+ switch (StreamType)
+ {
+ case TSStreamType.MPEG1_VIDEO:
+ return "MPEG-1";
+ case TSStreamType.MPEG2_VIDEO:
+ return "MPEG-2";
+ case TSStreamType.AVC_VIDEO:
+ return "AVC";
+ case TSStreamType.MVC_VIDEO:
+ return "MVC";
+ case TSStreamType.VC1_VIDEO:
+ return "VC-1";
+ case TSStreamType.MPEG1_AUDIO:
+ return "MP1";
+ case TSStreamType.MPEG2_AUDIO:
+ return "MP2";
+ case TSStreamType.LPCM_AUDIO:
+ return "LPCM";
+ case TSStreamType.AC3_AUDIO:
+ if (((TSAudioStream)this).AudioMode == TSAudioMode.Extended)
+ return "AC3-EX";
+ else
+ return "AC3";
+ case TSStreamType.AC3_PLUS_AUDIO:
+ case TSStreamType.AC3_PLUS_SECONDARY_AUDIO:
+ return "AC3+";
+ case TSStreamType.AC3_TRUE_HD_AUDIO:
+ return "TrueHD";
+ case TSStreamType.DTS_AUDIO:
+ if (((TSAudioStream)this).AudioMode == TSAudioMode.Extended)
+ return "DTS-ES";
+ else
+ return "DTS";
+ case TSStreamType.DTS_HD_AUDIO:
+ return "DTS-HD HR";
+ case TSStreamType.DTS_HD_SECONDARY_AUDIO:
+ return "DTS Express";
+ case TSStreamType.DTS_HD_MASTER_AUDIO:
+ return "DTS-HD MA";
+ case TSStreamType.PRESENTATION_GRAPHICS:
+ return "PGS";
+ case TSStreamType.INTERACTIVE_GRAPHICS:
+ return "IGS";
+ case TSStreamType.SUBTITLE:
+ return "SUB";
+ default:
+ return "UNKNOWN";
+ }
+ }
+ }
+
+ public virtual string Description
+ {
+ get
+ {
+ return "";
+ }
+ }
+
+ public abstract TSStream Clone();
+
+ protected void CopyTo(TSStream stream)
+ {
+ stream.PID = PID;
+ stream.StreamType = StreamType;
+ stream.IsVBR = IsVBR;
+ stream.BitRate = BitRate;
+ stream.IsInitialized = IsInitialized;
+ stream.LanguageCode = _LanguageCode;
+ if (Descriptors != null)
+ {
+ stream.Descriptors = new List<TSDescriptor>();
+ foreach (TSDescriptor descriptor in Descriptors)
+ {
+ stream.Descriptors.Add(descriptor.Clone());
+ }
+ }
+ }
+ }
+
+ public class TSVideoStream : TSStream
+ {
+ public TSVideoStream()
+ {
+ }
+
+ public int Width;
+ public int Height;
+ public bool IsInterlaced;
+ public int FrameRateEnumerator;
+ public int FrameRateDenominator;
+ public TSAspectRatio AspectRatio;
+ public string EncodingProfile;
+
+ private TSVideoFormat _VideoFormat;
+ public TSVideoFormat VideoFormat
+ {
+ get
+ {
+ return _VideoFormat;
+ }
+ set
+ {
+ _VideoFormat = value;
+ switch (value)
+ {
+ case TSVideoFormat.VIDEOFORMAT_480i:
+ Height = 480;
+ IsInterlaced = true;
+ break;
+ case TSVideoFormat.VIDEOFORMAT_480p:
+ Height = 480;
+ IsInterlaced = false;
+ break;
+ case TSVideoFormat.VIDEOFORMAT_576i:
+ Height = 576;
+ IsInterlaced = true;
+ break;
+ case TSVideoFormat.VIDEOFORMAT_576p:
+ Height = 576;
+ IsInterlaced = false;
+ break;
+ case TSVideoFormat.VIDEOFORMAT_720p:
+ Height = 720;
+ IsInterlaced = false;
+ break;
+ case TSVideoFormat.VIDEOFORMAT_1080i:
+ Height = 1080;
+ IsInterlaced = true;
+ break;
+ case TSVideoFormat.VIDEOFORMAT_1080p:
+ Height = 1080;
+ IsInterlaced = false;
+ break;
+ }
+ }
+ }
+
+ private TSFrameRate _FrameRate;
+ public TSFrameRate FrameRate
+ {
+ get
+ {
+ return _FrameRate;
+ }
+ set
+ {
+ _FrameRate = value;
+ switch (value)
+ {
+ case TSFrameRate.FRAMERATE_23_976:
+ FrameRateEnumerator = 24000;
+ FrameRateDenominator = 1001;
+ break;
+ case TSFrameRate.FRAMERATE_24:
+ FrameRateEnumerator = 24000;
+ FrameRateDenominator = 1000;
+ break;
+ case TSFrameRate.FRAMERATE_25:
+ FrameRateEnumerator = 25000;
+ FrameRateDenominator = 1000;
+ break;
+ case TSFrameRate.FRAMERATE_29_97:
+ FrameRateEnumerator = 30000;
+ FrameRateDenominator = 1001;
+ break;
+ case TSFrameRate.FRAMERATE_50:
+ FrameRateEnumerator = 50000;
+ FrameRateDenominator = 1000;
+ break;
+ case TSFrameRate.FRAMERATE_59_94:
+ FrameRateEnumerator = 60000;
+ FrameRateDenominator = 1001;
+ break;
+ }
+ }
+ }
+
+ public override string Description
+ {
+ get
+ {
+ string description = "";
+
+ if (Height > 0)
+ {
+ description += string.Format("{0:D}{1} / ",
+ Height,
+ IsInterlaced ? "i" : "p");
+ }
+ if (FrameRateEnumerator > 0 &&
+ FrameRateDenominator > 0)
+ {
+ if (FrameRateEnumerator % FrameRateDenominator == 0)
+ {
+ description += string.Format("{0:D} fps / ",
+ FrameRateEnumerator / FrameRateDenominator);
+ }
+ else
+ {
+ description += string.Format("{0:F3} fps / ",
+ (double)FrameRateEnumerator / FrameRateDenominator);
+ }
+
+ }
+ if (AspectRatio == TSAspectRatio.ASPECT_4_3)
+ {
+ description += "4:3 / ";
+ }
+ else if (AspectRatio == TSAspectRatio.ASPECT_16_9)
+ {
+ description += "16:9 / ";
+ }
+ if (EncodingProfile != null)
+ {
+ description += EncodingProfile + " / ";
+ }
+ if (description.EndsWith(" / "))
+ {
+ description = description.Substring(0, description.Length - 3);
+ }
+ return description;
+ }
+ }
+
+ public override TSStream Clone()
+ {
+ TSVideoStream stream = new TSVideoStream();
+ CopyTo(stream);
+
+ stream.VideoFormat = _VideoFormat;
+ stream.FrameRate = _FrameRate;
+ stream.Width = Width;
+ stream.Height = Height;
+ stream.IsInterlaced = IsInterlaced;
+ stream.FrameRateEnumerator = FrameRateEnumerator;
+ stream.FrameRateDenominator = FrameRateDenominator;
+ stream.AspectRatio = AspectRatio;
+ stream.EncodingProfile = EncodingProfile;
+
+ return stream;
+ }
+ }
+
+ public enum TSAudioMode
+ {
+ Unknown,
+ DualMono,
+ Stereo,
+ Surround,
+ Extended
+ }
+
+ public class TSAudioStream : TSStream
+ {
+ public TSAudioStream()
+ {
+ }
+
+ public int SampleRate;
+ public int ChannelCount;
+ public int BitDepth;
+ public int LFE;
+ public int DialNorm;
+ public TSAudioMode AudioMode;
+ public TSAudioStream CoreStream;
+ public TSChannelLayout ChannelLayout;
+
+ public static int ConvertSampleRate(
+ TSSampleRate sampleRate)
+ {
+ switch (sampleRate)
+ {
+ case TSSampleRate.SAMPLERATE_48:
+ return 48000;
+
+ case TSSampleRate.SAMPLERATE_96:
+ case TSSampleRate.SAMPLERATE_48_96:
+ return 96000;
+
+ case TSSampleRate.SAMPLERATE_192:
+ case TSSampleRate.SAMPLERATE_48_192:
+ return 192000;
+ }
+ return 0;
+ }
+
+ public string ChannelDescription
+ {
+ get
+ {
+ if (ChannelLayout == TSChannelLayout.CHANNELLAYOUT_MONO &&
+ ChannelCount == 2)
+ {
+ }
+
+ string description = "";
+ if (ChannelCount > 0)
+ {
+ description += string.Format(
+ "{0:D}.{1:D}",
+ ChannelCount, LFE);
+ }
+ else
+ {
+ switch (ChannelLayout)
+ {
+ case TSChannelLayout.CHANNELLAYOUT_MONO:
+ description += "1.0";
+ break;
+ case TSChannelLayout.CHANNELLAYOUT_STEREO:
+ description += "2.0";
+ break;
+ case TSChannelLayout.CHANNELLAYOUT_MULTI:
+ description += "5.1";
+ break;
+ }
+ }
+ if (AudioMode == TSAudioMode.Extended)
+ {
+ if (StreamType == TSStreamType.AC3_AUDIO)
+ {
+ description += "-EX";
+ }
+ if (StreamType == TSStreamType.DTS_AUDIO ||
+ StreamType == TSStreamType.DTS_HD_AUDIO ||
+ StreamType == TSStreamType.DTS_HD_MASTER_AUDIO)
+ {
+ description += "-ES";
+ }
+ }
+ return description;
+ }
+ }
+
+ public override string Description
+ {
+ get
+ {
+ string description = ChannelDescription;
+
+ if (SampleRate > 0)
+ {
+ description += string.Format(
+ " / {0:D} kHz", SampleRate / 1000);
+ }
+ if (BitRate > 0)
+ {
+ description += string.Format(
+ " / {0:D} kbps", (uint)Math.Round((double)BitRate / 1000));
+ }
+ if (BitDepth > 0)
+ {
+ description += string.Format(
+ " / {0:D}-bit", BitDepth);
+ }
+ if (DialNorm != 0)
+ {
+ description += string.Format(
+ " / DN {0}dB", DialNorm);
+ }
+ if (ChannelCount == 2)
+ {
+ switch (AudioMode)
+ {
+ case TSAudioMode.DualMono:
+ description += " / Dual Mono";
+ break;
+
+ case TSAudioMode.Surround:
+ description += " / Dolby Surround";
+ break;
+ }
+ }
+ if (description.EndsWith(" / "))
+ {
+ description = description.Substring(0, description.Length - 3);
+ }
+ if (CoreStream != null)
+ {
+ string codec = "";
+ switch (CoreStream.StreamType)
+ {
+ case TSStreamType.AC3_AUDIO:
+ codec = "AC3 Embedded";
+ break;
+ case TSStreamType.DTS_AUDIO:
+ codec = "DTS Core";
+ break;
+ }
+ description += string.Format(
+ " ({0}: {1})",
+ codec,
+ CoreStream.Description);
+ }
+ return description;
+ }
+ }
+
+ public override TSStream Clone()
+ {
+ TSAudioStream stream = new TSAudioStream();
+ CopyTo(stream);
+
+ stream.SampleRate = SampleRate;
+ stream.ChannelLayout = ChannelLayout;
+ stream.ChannelCount = ChannelCount;
+ stream.BitDepth = BitDepth;
+ stream.LFE = LFE;
+ stream.DialNorm = DialNorm;
+ stream.AudioMode = AudioMode;
+ if (CoreStream != null)
+ {
+ stream.CoreStream = (TSAudioStream)CoreStream.Clone();
+ }
+
+ return stream;
+ }
+ }
+
+ public class TSGraphicsStream : TSStream
+ {
+ public TSGraphicsStream()
+ {
+ IsVBR = true;
+ IsInitialized = true;
+ }
+
+ public override TSStream Clone()
+ {
+ TSGraphicsStream stream = new TSGraphicsStream();
+ CopyTo(stream);
+ return stream;
+ }
+ }
+
+ public class TSTextStream : TSStream
+ {
+ public TSTextStream()
+ {
+ IsVBR = true;
+ IsInitialized = true;
+ }
+
+ public override TSStream Clone()
+ {
+ TSTextStream stream = new TSTextStream();
+ CopyTo(stream);
+ return stream;
+ }
+ }
+}
diff --git a/BDInfo/TSStreamBuffer.cs b/BDInfo/TSStreamBuffer.cs
new file mode 100644
index 0000000000..2f9094876e
--- /dev/null
+++ b/BDInfo/TSStreamBuffer.cs
@@ -0,0 +1,142 @@
+//============================================================================
+// BDInfo - Blu-ray Video and Audio Analysis Tool
+// Copyright © 2010 Cinema Squid
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//=============================================================================
+
+using System;
+using System.Collections.Specialized;
+using System.IO;
+
+namespace BDInfo
+{
+ public class TSStreamBuffer
+ {
+ private MemoryStream Stream = new MemoryStream();
+ private int SkipBits = 0;
+ private byte[] Buffer;
+ private int BufferLength = 0;
+ public int TransferLength = 0;
+
+ public TSStreamBuffer()
+ {
+ Buffer = new byte[4096];
+ Stream = new MemoryStream(Buffer);
+ }
+
+ public long Length
+ {
+ get
+ {
+ return (long)BufferLength;
+ }
+ }
+
+ public long Position
+ {
+ get
+ {
+ return Stream.Position;
+ }
+ }
+
+ public void Add(
+ byte[] buffer,
+ int offset,
+ int length)
+ {
+ TransferLength += length;
+
+ if (BufferLength + length >= Buffer.Length)
+ {
+ length = Buffer.Length - BufferLength;
+ }
+ if (length > 0)
+ {
+ Array.Copy(buffer, offset, Buffer, BufferLength, length);
+ BufferLength += length;
+ }
+ }
+
+ public void Seek(
+ long offset,
+ SeekOrigin loc)
+ {
+ Stream.Seek(offset, loc);
+ }
+
+ public void Reset()
+ {
+ BufferLength = 0;
+ TransferLength = 0;
+ }
+
+ public void BeginRead()
+ {
+ SkipBits = 0;
+ Stream.Seek(0, SeekOrigin.Begin);
+ }
+
+ public void EndRead()
+ {
+ }
+
+ public byte[] ReadBytes(int bytes)
+ {
+ if (Stream.Position + bytes >= BufferLength)
+ {
+ return null;
+ }
+
+ byte[] value = new byte[bytes];
+ Stream.Read(value, 0, bytes);
+ return value;
+ }
+
+ public byte ReadByte()
+ {
+ return (byte)Stream.ReadByte();
+ }
+
+ public int ReadBits(int bits)
+ {
+ long pos = Stream.Position;
+
+ int shift = 24;
+ int data = 0;
+ for (int i = 0; i < 4; i++)
+ {
+ if (pos + i >= BufferLength) break;
+ data += (Stream.ReadByte() << shift);
+ shift -= 8;
+ }
+ BitVector32 vector = new BitVector32(data);
+
+ int value = 0;
+ for (int i = SkipBits; i < SkipBits + bits; i++)
+ {
+ value <<= 1;
+ value += (vector[1 << (32 - i - 1)] ? 1 : 0);
+ }
+
+ SkipBits += bits;
+ Stream.Seek(pos + (SkipBits >> 3), SeekOrigin.Begin);
+ SkipBits = SkipBits % 8;
+
+ return value;
+ }
+ }
+}
diff --git a/BDInfo/TSStreamClip.cs b/BDInfo/TSStreamClip.cs
new file mode 100644
index 0000000000..d7592a71aa
--- /dev/null
+++ b/BDInfo/TSStreamClip.cs
@@ -0,0 +1,113 @@
+//============================================================================
+// BDInfo - Blu-ray Video and Audio Analysis Tool
+// Copyright © 2010 Cinema Squid
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//=============================================================================
+
+using System;
+using System.Collections.Generic;
+
+namespace BDInfo
+{
+ public class TSStreamClip
+ {
+ public int AngleIndex = 0;
+ public string Name;
+ public double TimeIn;
+ public double TimeOut;
+ public double RelativeTimeIn;
+ public double RelativeTimeOut;
+ public double Length;
+
+ public ulong FileSize = 0;
+ public ulong InterleavedFileSize = 0;
+ public ulong PayloadBytes = 0;
+ public ulong PacketCount = 0;
+ public double PacketSeconds = 0;
+
+ public List<double> Chapters = new List<double>();
+
+ public TSStreamFile StreamFile = null;
+ public TSStreamClipFile StreamClipFile = null;
+
+ public TSStreamClip(
+ TSStreamFile streamFile,
+ TSStreamClipFile streamClipFile)
+ {
+ if (streamFile != null)
+ {
+ Name = streamFile.Name;
+ StreamFile = streamFile;
+ FileSize = (ulong)StreamFile.FileInfo.Length;
+ if (StreamFile.InterleavedFile != null)
+ {
+ InterleavedFileSize = (ulong)StreamFile.InterleavedFile.FileInfo.Length;
+ }
+ }
+ StreamClipFile = streamClipFile;
+ }
+
+ public string DisplayName
+ {
+ get
+ {
+ if (StreamFile != null &&
+ StreamFile.InterleavedFile != null &&
+ BDInfoSettings.EnableSSIF)
+ {
+ return StreamFile.InterleavedFile.Name;
+ }
+ return Name;
+ }
+ }
+
+ public ulong PacketSize
+ {
+ get
+ {
+ return PacketCount * 192;
+ }
+ }
+
+ public ulong PacketBitRate
+ {
+ get
+ {
+ if (PacketSeconds > 0)
+ {
+ return (ulong)Math.Round(((PacketSize * 8.0) / PacketSeconds));
+ }
+ return 0;
+ }
+ }
+
+ public bool IsCompatible(TSStreamClip clip)
+ {
+ foreach (TSStream stream1 in StreamFile.Streams.Values)
+ {
+ if (clip.StreamFile.Streams.ContainsKey(stream1.PID))
+ {
+ TSStream stream2 = clip.StreamFile.Streams[stream1.PID];
+ if (stream1.StreamType != stream2.StreamType)
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ }
+}
diff --git a/BDInfo/TSStreamClipFile.cs b/BDInfo/TSStreamClipFile.cs
new file mode 100644
index 0000000000..f2accb88d8
--- /dev/null
+++ b/BDInfo/TSStreamClipFile.cs
@@ -0,0 +1,253 @@
+//============================================================================
+// BDInfo - Blu-ray Video and Audio Analysis Tool
+// Copyright © 2010 Cinema Squid
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//=============================================================================
+
+#undef DEBUG
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Text;
+
+namespace BDInfo
+{
+ public class TSStreamClipFile
+ {
+ private readonly IFileSystem _fileSystem;
+ private readonly ITextEncoding _textEncoding;
+ public FileSystemMetadata FileInfo = null;
+ public string FileType = null;
+ public bool IsValid = false;
+ public string Name = null;
+
+ public Dictionary<ushort, TSStream> Streams =
+ new Dictionary<ushort,TSStream>();
+
+ public TSStreamClipFile(
+ FileSystemMetadata fileInfo, IFileSystem fileSystem, ITextEncoding textEncoding)
+ {
+ FileInfo = fileInfo;
+ _fileSystem = fileSystem;
+ _textEncoding = textEncoding;
+ Name = fileInfo.Name.ToUpper();
+ }
+
+ public void Scan()
+ {
+ Stream fileStream = null;
+ BinaryReader fileReader = null;
+
+ try
+ {
+#if DEBUG
+ Debug.WriteLine(string.Format(
+ "Scanning {0}...", Name));
+#endif
+ Streams.Clear();
+
+ fileStream = _fileSystem.OpenRead(FileInfo.FullName);
+ fileReader = new BinaryReader(fileStream);
+
+ byte[] data = new byte[fileStream.Length];
+ fileReader.Read(data, 0, data.Length);
+
+ byte[] fileType = new byte[8];
+ Array.Copy(data, 0, fileType, 0, fileType.Length);
+
+ FileType = _textEncoding.GetASCIIEncoding().GetString(fileType, 0, fileType.Length);
+ if (FileType != "HDMV0100" &&
+ FileType != "HDMV0200")
+ {
+ throw new Exception(string.Format(
+ "Clip info file {0} has an unknown file type {1}.",
+ FileInfo.Name, FileType));
+ }
+#if DEBUG
+ Debug.WriteLine(string.Format(
+ "\tFileType: {0}", FileType));
+#endif
+ int clipIndex =
+ ((int)data[12] << 24) +
+ ((int)data[13] << 16) +
+ ((int)data[14] << 8) +
+ ((int)data[15]);
+
+ int clipLength =
+ ((int)data[clipIndex] << 24) +
+ ((int)data[clipIndex + 1] << 16) +
+ ((int)data[clipIndex + 2] << 8) +
+ ((int)data[clipIndex + 3]);
+
+ byte[] clipData = new byte[clipLength];
+ Array.Copy(data, clipIndex + 4, clipData, 0, clipData.Length);
+
+ int streamCount = clipData[8];
+#if DEBUG
+ Debug.WriteLine(string.Format(
+ "\tStreamCount: {0}", streamCount));
+#endif
+ int streamOffset = 10;
+ for (int streamIndex = 0;
+ streamIndex < streamCount;
+ streamIndex++)
+ {
+ TSStream stream = null;
+
+ ushort PID = (ushort)
+ ((clipData[streamOffset] << 8) +
+ clipData[streamOffset + 1]);
+
+ streamOffset += 2;
+
+ TSStreamType streamType = (TSStreamType)
+ clipData[streamOffset + 1];
+ switch (streamType)
+ {
+ case TSStreamType.MVC_VIDEO:
+ // TODO
+ break;
+
+ case TSStreamType.AVC_VIDEO:
+ case TSStreamType.MPEG1_VIDEO:
+ case TSStreamType.MPEG2_VIDEO:
+ case TSStreamType.VC1_VIDEO:
+ {
+ TSVideoFormat videoFormat = (TSVideoFormat)
+ (clipData[streamOffset + 2] >> 4);
+ TSFrameRate frameRate = (TSFrameRate)
+ (clipData[streamOffset + 2] & 0xF);
+ TSAspectRatio aspectRatio = (TSAspectRatio)
+ (clipData[streamOffset + 3] >> 4);
+
+ stream = new TSVideoStream();
+ ((TSVideoStream)stream).VideoFormat = videoFormat;
+ ((TSVideoStream)stream).AspectRatio = aspectRatio;
+ ((TSVideoStream)stream).FrameRate = frameRate;
+#if DEBUG
+ Debug.WriteLine(string.Format(
+ "\t{0} {1} {2} {3} {4}",
+ PID,
+ streamType,
+ videoFormat,
+ frameRate,
+ aspectRatio));
+#endif
+ }
+ break;
+
+ case TSStreamType.AC3_AUDIO:
+ case TSStreamType.AC3_PLUS_AUDIO:
+ case TSStreamType.AC3_PLUS_SECONDARY_AUDIO:
+ case TSStreamType.AC3_TRUE_HD_AUDIO:
+ case TSStreamType.DTS_AUDIO:
+ case TSStreamType.DTS_HD_AUDIO:
+ case TSStreamType.DTS_HD_MASTER_AUDIO:
+ case TSStreamType.DTS_HD_SECONDARY_AUDIO:
+ case TSStreamType.LPCM_AUDIO:
+ case TSStreamType.MPEG1_AUDIO:
+ case TSStreamType.MPEG2_AUDIO:
+ {
+ byte[] languageBytes = new byte[3];
+ Array.Copy(clipData, streamOffset + 3,
+ languageBytes, 0, languageBytes.Length);
+ string languageCode =
+ _textEncoding.GetASCIIEncoding().GetString(languageBytes, 0, languageBytes.Length);
+
+ TSChannelLayout channelLayout = (TSChannelLayout)
+ (clipData[streamOffset + 2] >> 4);
+ TSSampleRate sampleRate = (TSSampleRate)
+ (clipData[streamOffset + 2] & 0xF);
+
+ stream = new TSAudioStream();
+ ((TSAudioStream)stream).LanguageCode = languageCode;
+ ((TSAudioStream)stream).ChannelLayout = channelLayout;
+ ((TSAudioStream)stream).SampleRate = TSAudioStream.ConvertSampleRate(sampleRate);
+ ((TSAudioStream)stream).LanguageCode = languageCode;
+#if DEBUG
+ Debug.WriteLine(string.Format(
+ "\t{0} {1} {2} {3} {4}",
+ PID,
+ streamType,
+ languageCode,
+ channelLayout,
+ sampleRate));
+#endif
+ }
+ break;
+
+ case TSStreamType.INTERACTIVE_GRAPHICS:
+ case TSStreamType.PRESENTATION_GRAPHICS:
+ {
+ byte[] languageBytes = new byte[3];
+ Array.Copy(clipData, streamOffset + 2,
+ languageBytes, 0, languageBytes.Length);
+ string languageCode =
+ _textEncoding.GetASCIIEncoding().GetString(languageBytes, 0, languageBytes.Length);
+
+ stream = new TSGraphicsStream();
+ stream.LanguageCode = languageCode;
+#if DEBUG
+ Debug.WriteLine(string.Format(
+ "\t{0} {1} {2}",
+ PID,
+ streamType,
+ languageCode));
+#endif
+ }
+ break;
+
+ case TSStreamType.SUBTITLE:
+ {
+ byte[] languageBytes = new byte[3];
+ Array.Copy(clipData, streamOffset + 3,
+ languageBytes, 0, languageBytes.Length);
+ string languageCode =
+ _textEncoding.GetASCIIEncoding().GetString(languageBytes, 0, languageBytes.Length);
+#if DEBUG
+ Debug.WriteLine(string.Format(
+ "\t{0} {1} {2}",
+ PID,
+ streamType,
+ languageCode));
+#endif
+ stream = new TSTextStream();
+ stream.LanguageCode = languageCode;
+ }
+ break;
+ }
+
+ if (stream != null)
+ {
+ stream.PID = PID;
+ stream.StreamType = streamType;
+ Streams.Add(PID, stream);
+ }
+
+ streamOffset += clipData[streamOffset] + 1;
+ }
+ IsValid = true;
+ }
+ finally
+ {
+ if (fileReader != null) fileReader.Dispose();
+ if (fileStream != null) fileStream.Dispose();
+ }
+ }
+ }
+}
diff --git a/BDInfo/TSStreamFile.cs b/BDInfo/TSStreamFile.cs
new file mode 100644
index 0000000000..31020cbf40
--- /dev/null
+++ b/BDInfo/TSStreamFile.cs
@@ -0,0 +1,1553 @@
+//============================================================================
+// BDInfo - Blu-ray Video and Audio Analysis Tool
+// Copyright © 2010 Cinema Squid
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//=============================================================================
+
+#undef DEBUG
+using System;
+using System.Collections.Generic;
+using System.IO;
+using MediaBrowser.Model.IO;
+
+namespace BDInfo
+{
+ public class TSStreamState
+ {
+ public ulong TransferCount = 0;
+
+ public string StreamTag = null;
+
+ public ulong TotalPackets = 0;
+ public ulong WindowPackets = 0;
+
+ public ulong TotalBytes = 0;
+ public ulong WindowBytes = 0;
+
+ public long PeakTransferLength = 0;
+ public long PeakTransferRate = 0;
+
+ public double TransferMarker = 0;
+ public double TransferInterval = 0;
+
+ public TSStreamBuffer StreamBuffer = new TSStreamBuffer();
+
+ public uint Parse = 0;
+ public bool TransferState = false;
+ public int TransferLength = 0;
+ public int PacketLength = 0;
+ public byte PacketLengthParse = 0;
+ public byte PacketParse = 0;
+
+ public byte PTSParse = 0;
+ public ulong PTS = 0;
+ public ulong PTSTemp = 0;
+ public ulong PTSLast = 0;
+ public ulong PTSPrev = 0;
+ public ulong PTSDiff = 0;
+ public ulong PTSCount = 0;
+ public ulong PTSTransfer = 0;
+
+ public byte DTSParse = 0;
+ public ulong DTSTemp = 0;
+ public ulong DTSPrev = 0;
+
+ public byte PESHeaderLength = 0;
+ public byte PESHeaderFlags = 0;
+#if DEBUG
+ public byte PESHeaderIndex = 0;
+ public byte[] PESHeader = new byte[256 + 9];
+#endif
+ }
+
+ public class TSPacketParser
+ {
+ public bool SyncState = false;
+ public byte TimeCodeParse = 4;
+ public byte PacketLength = 0;
+ public byte HeaderParse = 0;
+
+ public uint TimeCode;
+ public byte TransportErrorIndicator;
+ public byte PayloadUnitStartIndicator;
+ public byte TransportPriority;
+ public ushort PID;
+ public byte TransportScramblingControl;
+ public byte AdaptionFieldControl;
+
+ public bool AdaptionFieldState = false;
+ public byte AdaptionFieldParse = 0;
+ public byte AdaptionFieldLength = 0;
+
+ public ushort PCRPID = 0xFFFF;
+ public byte PCRParse = 0;
+ public ulong PreviousPCR = 0;
+ public ulong PCR = 0;
+ public ulong PCRCount = 0;
+ public ulong PTSFirst = ulong.MaxValue;
+ public ulong PTSLast = ulong.MinValue;
+ public ulong PTSDiff = 0;
+
+ public byte[] PAT = new byte[1024];
+ public bool PATSectionStart = false;
+ public byte PATPointerField = 0;
+ public uint PATOffset = 0;
+ public byte PATSectionLengthParse = 0;
+ public ushort PATSectionLength = 0;
+ public uint PATSectionParse = 0;
+ public bool PATTransferState = false;
+ public byte PATSectionNumber = 0;
+ public byte PATLastSectionNumber = 0;
+
+ public ushort TransportStreamId = 0xFFFF;
+
+ public List<TSDescriptor> PMTProgramDescriptors = new List<TSDescriptor>();
+ public ushort PMTPID = 0xFFFF;
+ public Dictionary<ushort, byte[]> PMT = new Dictionary<ushort, byte[]>();
+ public bool PMTSectionStart = false;
+ public ushort PMTProgramInfoLength = 0;
+ public byte PMTProgramDescriptor = 0;
+ public byte PMTProgramDescriptorLengthParse = 0;
+ public byte PMTProgramDescriptorLength = 0;
+ public ushort PMTStreamInfoLength = 0;
+ public uint PMTStreamDescriptorLengthParse = 0;
+ public uint PMTStreamDescriptorLength = 0;
+ public byte PMTPointerField = 0;
+ public uint PMTOffset = 0;
+ public uint PMTSectionLengthParse = 0;
+ public ushort PMTSectionLength = 0;
+ public uint PMTSectionParse = 0;
+ public bool PMTTransferState = false;
+ public byte PMTSectionNumber = 0;
+ public byte PMTLastSectionNumber = 0;
+
+ public byte PMTTemp = 0;
+
+ public TSStream Stream = null;
+ public TSStreamState StreamState = null;
+
+ public ulong TotalPackets = 0;
+ }
+
+ public class TSStreamDiagnostics
+ {
+ public ulong Bytes = 0;
+ public ulong Packets = 0;
+ public double Marker = 0;
+ public double Interval = 0;
+ public string Tag = null;
+ }
+
+ public class TSStreamFile
+ {
+ public FileSystemMetadata FileInfo = null;
+ public string Name = null;
+ public long Size = 0;
+ public double Length = 0;
+
+ public TSInterleavedFile InterleavedFile = null;
+
+ private Dictionary<ushort, TSStreamState> StreamStates =
+ new Dictionary<ushort, TSStreamState>();
+
+ public Dictionary<ushort, TSStream> Streams =
+ new Dictionary<ushort, TSStream>();
+
+ public Dictionary<ushort, List<TSStreamDiagnostics>> StreamDiagnostics =
+ new Dictionary<ushort, List<TSStreamDiagnostics>>();
+
+ private List<TSPlaylistFile> Playlists = null;
+
+ private readonly IFileSystem _fileSystem;
+
+ public TSStreamFile(FileSystemMetadata fileInfo, IFileSystem fileSystem)
+ {
+ FileInfo = fileInfo;
+ _fileSystem = fileSystem;
+ Name = fileInfo.Name.ToUpper();
+ }
+
+ public string DisplayName
+ {
+ get
+ {
+ if (BDInfoSettings.EnableSSIF &&
+ InterleavedFile != null)
+ {
+ return InterleavedFile.Name;
+ }
+ return Name;
+ }
+ }
+
+ private bool ScanStream(
+ TSStream stream,
+ TSStreamState streamState,
+ TSStreamBuffer buffer)
+ {
+ streamState.StreamTag = null;
+
+ long bitrate = 0;
+ if (stream.IsAudioStream &&
+ streamState.PTSTransfer > 0)
+ {
+ bitrate = (long)Math.Round(
+ (buffer.TransferLength * 8.0) /
+ ((double)streamState.PTSTransfer / 90000));
+
+ if (bitrate > streamState.PeakTransferRate)
+ {
+ streamState.PeakTransferRate = bitrate;
+ }
+ }
+ if (buffer.TransferLength > streamState.PeakTransferLength)
+ {
+ streamState.PeakTransferLength = buffer.TransferLength;
+ }
+
+ buffer.BeginRead();
+ switch (stream.StreamType)
+ {
+ case TSStreamType.MPEG2_VIDEO:
+ TSCodecMPEG2.Scan(
+ (TSVideoStream)stream, buffer, ref streamState.StreamTag);
+ break;
+
+ case TSStreamType.AVC_VIDEO:
+ TSCodecAVC.Scan(
+ (TSVideoStream)stream, buffer, ref streamState.StreamTag);
+ break;
+
+ case TSStreamType.MVC_VIDEO:
+ TSCodecMVC.Scan(
+ (TSVideoStream)stream, buffer, ref streamState.StreamTag);
+ break;
+
+ case TSStreamType.VC1_VIDEO:
+ TSCodecVC1.Scan(
+ (TSVideoStream)stream, buffer, ref streamState.StreamTag);
+ break;
+
+ case TSStreamType.AC3_AUDIO:
+ TSCodecAC3.Scan(
+ (TSAudioStream)stream, buffer, ref streamState.StreamTag);
+ break;
+
+ case TSStreamType.AC3_PLUS_AUDIO:
+ case TSStreamType.AC3_PLUS_SECONDARY_AUDIO:
+ TSCodecAC3.Scan(
+ (TSAudioStream)stream, buffer, ref streamState.StreamTag);
+ break;
+
+ case TSStreamType.AC3_TRUE_HD_AUDIO:
+ TSCodecTrueHD.Scan(
+ (TSAudioStream)stream, buffer, ref streamState.StreamTag);
+ break;
+
+ case TSStreamType.LPCM_AUDIO:
+ TSCodecLPCM.Scan(
+ (TSAudioStream)stream, buffer, ref streamState.StreamTag);
+ break;
+
+ case TSStreamType.DTS_AUDIO:
+ TSCodecDTS.Scan(
+ (TSAudioStream)stream, buffer, bitrate, ref streamState.StreamTag);
+ break;
+
+ case TSStreamType.DTS_HD_AUDIO:
+ case TSStreamType.DTS_HD_MASTER_AUDIO:
+ case TSStreamType.DTS_HD_SECONDARY_AUDIO:
+ TSCodecDTSHD.Scan(
+ (TSAudioStream)stream, buffer, bitrate, ref streamState.StreamTag);
+ break;
+
+ default:
+ stream.IsInitialized = true;
+ break;
+ }
+ buffer.EndRead();
+ streamState.StreamBuffer.Reset();
+
+ bool isAVC = false;
+ bool isMVC = false;
+ foreach (TSStream finishedStream in Streams.Values)
+ {
+ if (!finishedStream.IsInitialized)
+ {
+ return false;
+ }
+ if (finishedStream.StreamType == TSStreamType.AVC_VIDEO)
+ {
+ isAVC = true;
+ }
+ if (finishedStream.StreamType == TSStreamType.MVC_VIDEO)
+ {
+ isMVC = true;
+ }
+ }
+ if (isMVC && !isAVC)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ private void UpdateStreamBitrates(
+ ushort PTSPID,
+ ulong PTS,
+ ulong PTSDiff)
+ {
+ if (Playlists == null) return;
+
+ foreach (ushort PID in StreamStates.Keys)
+ {
+ if (Streams.ContainsKey(PID) &&
+ Streams[PID].IsVideoStream &&
+ PID != PTSPID)
+ {
+ continue;
+ }
+ if (StreamStates[PID].WindowPackets == 0)
+ {
+ continue;
+ }
+ UpdateStreamBitrate(PID, PTSPID, PTS, PTSDiff);
+ }
+
+ foreach (TSPlaylistFile playlist in Playlists)
+ {
+ double packetSeconds = 0;
+ foreach (TSStreamClip clip in playlist.StreamClips)
+ {
+ if (clip.AngleIndex == 0)
+ {
+ packetSeconds += clip.PacketSeconds;
+ }
+ }
+ if (packetSeconds > 0)
+ {
+ foreach (TSStream playlistStream in playlist.SortedStreams)
+ {
+ if (playlistStream.IsVBR)
+ {
+ playlistStream.BitRate = (long)Math.Round(
+ ((playlistStream.PayloadBytes * 8.0) / packetSeconds));
+
+ if (playlistStream.StreamType == TSStreamType.AC3_TRUE_HD_AUDIO &&
+ ((TSAudioStream)playlistStream).CoreStream != null)
+ {
+ playlistStream.BitRate -=
+ ((TSAudioStream)playlistStream).CoreStream.BitRate;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void UpdateStreamBitrate(
+ ushort PID,
+ ushort PTSPID,
+ ulong PTS,
+ ulong PTSDiff)
+ {
+ if (Playlists == null) return;
+
+ TSStreamState streamState = StreamStates[PID];
+ double streamTime = (double)PTS / 90000;
+ double streamInterval = (double)PTSDiff / 90000;
+ double streamOffset = streamTime + streamInterval;
+
+ foreach (TSPlaylistFile playlist in Playlists)
+ {
+ foreach (TSStreamClip clip in playlist.StreamClips)
+ {
+ if (clip.Name != this.Name) continue;
+
+ if (streamTime == 0 ||
+ (streamTime >= clip.TimeIn &&
+ streamTime <= clip.TimeOut))
+ {
+ clip.PayloadBytes += streamState.WindowBytes;
+ clip.PacketCount += streamState.WindowPackets;
+
+ if (streamOffset > clip.TimeIn &&
+ streamOffset - clip.TimeIn > clip.PacketSeconds)
+ {
+ clip.PacketSeconds = streamOffset - clip.TimeIn;
+ }
+
+ Dictionary<ushort, TSStream> playlistStreams = playlist.Streams;
+ if (clip.AngleIndex > 0 &&
+ clip.AngleIndex < playlist.AngleStreams.Count + 1)
+ {
+ playlistStreams = playlist.AngleStreams[clip.AngleIndex - 1];
+ }
+ if (playlistStreams.ContainsKey(PID))
+ {
+ TSStream stream = playlistStreams[PID];
+
+ stream.PayloadBytes += streamState.WindowBytes;
+ stream.PacketCount += streamState.WindowPackets;
+
+ if (stream.IsVideoStream)
+ {
+ stream.PacketSeconds += streamInterval;
+
+ stream.ActiveBitRate = (long)Math.Round(
+ ((stream.PayloadBytes * 8.0) /
+ stream.PacketSeconds));
+ }
+
+ if (stream.StreamType == TSStreamType.AC3_TRUE_HD_AUDIO &&
+ ((TSAudioStream)stream).CoreStream != null)
+ {
+ stream.ActiveBitRate -=
+ ((TSAudioStream)stream).CoreStream.BitRate;
+ }
+ }
+ }
+ }
+ }
+
+ if (Streams.ContainsKey(PID))
+ {
+ TSStream stream = Streams[PID];
+ stream.PayloadBytes += streamState.WindowBytes;
+ stream.PacketCount += streamState.WindowPackets;
+
+ if (stream.IsVideoStream)
+ {
+ TSStreamDiagnostics diag = new TSStreamDiagnostics();
+ diag.Marker = (double)PTS / 90000;
+ diag.Interval = (double)PTSDiff / 90000;
+ diag.Bytes = streamState.WindowBytes;
+ diag.Packets = streamState.WindowPackets;
+ diag.Tag = streamState.StreamTag;
+ StreamDiagnostics[PID].Add(diag);
+
+ stream.PacketSeconds += streamInterval;
+ }
+ }
+ streamState.WindowPackets = 0;
+ streamState.WindowBytes = 0;
+ }
+
+ public void Scan(List<TSPlaylistFile> playlists, bool isFullScan)
+ {
+ if (playlists == null || playlists.Count == 0)
+ {
+ return;
+ }
+
+ Playlists = playlists;
+ int dataSize = 16384;
+ Stream fileStream = null;
+ try
+ {
+ string fileName;
+ if (BDInfoSettings.EnableSSIF &&
+ InterleavedFile != null)
+ {
+ fileName = InterleavedFile.FileInfo.FullName;
+ }
+ else
+ {
+ fileName = FileInfo.FullName;
+ }
+ fileStream = _fileSystem.GetFileStream(
+ fileName,
+ FileOpenMode.Open,
+ FileAccessMode.Read,
+ FileShareMode.Read,
+ false);
+
+ Size = 0;
+ Length = 0;
+
+ Streams.Clear();
+ StreamStates.Clear();
+ StreamDiagnostics.Clear();
+
+ TSPacketParser parser =
+ new TSPacketParser();
+
+ long fileLength = (uint)fileStream.Length;
+ byte[] buffer = new byte[dataSize];
+ int bufferLength = 0;
+ while ((bufferLength =
+ fileStream.Read(buffer, 0, buffer.Length)) > 0)
+ {
+ int offset = 0;
+ for (int i = 0; i < bufferLength; i++)
+ {
+ if (parser.SyncState == false)
+ {
+ if (parser.TimeCodeParse > 0)
+ {
+ parser.TimeCodeParse--;
+ switch (parser.TimeCodeParse)
+ {
+ case 3:
+ parser.TimeCode = 0;
+ parser.TimeCode |=
+ ((uint)buffer[i] & 0x3F) << 24;
+ break;
+ case 2:
+ parser.TimeCode |=
+ ((uint)buffer[i] & 0xFF) << 16;
+ break;
+ case 1:
+ parser.TimeCode |=
+ ((uint)buffer[i] & 0xFF) << 8;
+ break;
+ case 0:
+ parser.TimeCode |=
+ ((uint)buffer[i] & 0xFF);
+ break;
+ }
+ }
+ else if (buffer[i] == 0x47)
+ {
+ parser.SyncState = true;
+ parser.PacketLength = 187;
+ parser.TimeCodeParse = 4;
+ parser.HeaderParse = 3;
+ }
+ }
+ else if (parser.HeaderParse > 0)
+ {
+ parser.PacketLength--;
+ parser.HeaderParse--;
+
+ switch (parser.HeaderParse)
+ {
+ case 2:
+ {
+ parser.TransportErrorIndicator =
+ (byte)((buffer[i] >> 7) & 0x1);
+ parser.PayloadUnitStartIndicator =
+ (byte)((buffer[i] >> 6) & 0x1);
+ parser.TransportPriority =
+ (byte)((buffer[i] >> 5) & 0x1);
+ parser.PID =
+ (ushort)((buffer[i] & 0x1f) << 8);
+ }
+ break;
+
+ case 1:
+ {
+ parser.PID |= (ushort)buffer[i];
+ if (Streams.ContainsKey(parser.PID))
+ {
+ parser.Stream = Streams[parser.PID];
+ }
+ else
+ {
+ parser.Stream = null;
+ }
+ if (!StreamStates.ContainsKey(parser.PID))
+ {
+ StreamStates[parser.PID] = new TSStreamState();
+ }
+ parser.StreamState = StreamStates[parser.PID];
+ parser.StreamState.TotalPackets++;
+ parser.StreamState.WindowPackets++;
+ parser.TotalPackets++;
+ }
+ break;
+
+ case 0:
+ {
+ parser.TransportScramblingControl =
+ (byte)((buffer[i] >> 6) & 0x3);
+ parser.AdaptionFieldControl =
+ (byte)((buffer[i] >> 4) & 0x3);
+
+ if ((parser.AdaptionFieldControl & 0x2) == 0x2)
+ {
+ parser.AdaptionFieldState = true;
+ }
+ if (parser.PayloadUnitStartIndicator == 1)
+ {
+ if (parser.PID == 0)
+ {
+ parser.PATSectionStart = true;
+ }
+ else if (parser.PID == parser.PMTPID)
+ {
+ parser.PMTSectionStart = true;
+ }
+ else if (parser.StreamState != null &&
+ parser.StreamState.TransferState)
+ {
+ parser.StreamState.TransferState = false;
+ parser.StreamState.TransferCount++;
+
+ bool isFinished = ScanStream(
+ parser.Stream,
+ parser.StreamState,
+ parser.StreamState.StreamBuffer);
+
+ if (!isFullScan && isFinished)
+ {
+ return;
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ else if (parser.AdaptionFieldState)
+ {
+ parser.PacketLength--;
+ parser.AdaptionFieldParse = buffer[i];
+ parser.AdaptionFieldLength = buffer[i];
+ parser.AdaptionFieldState = false;
+ }
+ else if (parser.AdaptionFieldParse > 0)
+ {
+ parser.PacketLength--;
+ parser.AdaptionFieldParse--;
+ if ((parser.AdaptionFieldLength - parser.AdaptionFieldParse) == 1)
+ {
+ if ((buffer[i] & 0x10) == 0x10)
+ {
+ parser.PCRParse = 6;
+ parser.PCR = 0;
+ }
+ }
+ else if (parser.PCRParse > 0)
+ {
+ parser.PCRParse--;
+ parser.PCR = (parser.PCR << 8) + (ulong)buffer[i];
+ if (parser.PCRParse == 0)
+ {
+ parser.PreviousPCR = parser.PCR;
+ parser.PCR = (parser.PCR & 0x1FF) +
+ ((parser.PCR >> 15) * 300);
+ }
+ parser.PCRCount++;
+ }
+ if (parser.PacketLength == 0)
+ {
+ parser.SyncState = false;
+ }
+ }
+ else if (parser.PID == 0)
+ {
+ if (parser.PATTransferState)
+ {
+ if ((bufferLength - i) > parser.PATSectionLength)
+ {
+ offset = parser.PATSectionLength;
+ }
+ else
+ {
+ offset = (bufferLength - i);
+ }
+ if (parser.PacketLength <= offset)
+ {
+ offset = parser.PacketLength;
+ }
+
+ for (int k = 0; k < offset; k++)
+ {
+ parser.PAT[parser.PATOffset++] = buffer[i++];
+ parser.PATSectionLength--;
+ parser.PacketLength--;
+ } --i;
+
+ if (parser.PATSectionLength == 0)
+ {
+ parser.PATTransferState = false;
+ if (parser.PATSectionNumber == parser.PATLastSectionNumber)
+ {
+ for (int k = 0; k < (parser.PATOffset - 4); k += 4)
+ {
+ uint programNumber = (uint)
+ ((parser.PAT[k] << 8) +
+ parser.PAT[k + 1]);
+
+ ushort programPID = (ushort)
+ (((parser.PAT[k + 2] & 0x1F) << 8) +
+ parser.PAT[k + 3]);
+
+ if (programNumber == 1)
+ {
+ parser.PMTPID = programPID;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ --parser.PacketLength;
+ if (parser.PATSectionStart)
+ {
+ parser.PATPointerField = buffer[i];
+ if (parser.PATPointerField == 0)
+ {
+ parser.PATSectionLengthParse = 3;
+ }
+ parser.PATSectionStart = false;
+ }
+ else if (parser.PATPointerField > 0)
+ {
+ --parser.PATPointerField;
+ if (parser.PATPointerField == 0)
+ {
+ parser.PATSectionLengthParse = 3;
+ }
+ }
+ else if (parser.PATSectionLengthParse > 0)
+ {
+ --parser.PATSectionLengthParse;
+ switch (parser.PATSectionLengthParse)
+ {
+ case 2:
+ break;
+ case 1:
+ parser.PATSectionLength = (ushort)
+ ((buffer[i] & 0xF) << 8);
+ break;
+ case 0:
+ parser.PATSectionLength |= buffer[i];
+ if (parser.PATSectionLength > 1021)
+ {
+ parser.PATSectionLength = 0;
+ }
+ else
+ {
+ parser.PATSectionParse = 5;
+ }
+ break;
+ }
+ }
+ else if (parser.PATSectionParse > 0)
+ {
+ --parser.PATSectionLength;
+ --parser.PATSectionParse;
+
+ switch (parser.PATSectionParse)
+ {
+ case 4:
+ parser.TransportStreamId = (ushort)
+ (buffer[i] << 8);
+ break;
+ case 3:
+ parser.TransportStreamId |= buffer[i];
+ break;
+ case 2:
+ break;
+ case 1:
+ parser.PATSectionNumber = buffer[i];
+ if (parser.PATSectionNumber == 0)
+ {
+ parser.PATOffset = 0;
+ }
+ break;
+ case 0:
+ parser.PATLastSectionNumber = buffer[i];
+ parser.PATTransferState = true;
+ break;
+ }
+ }
+ }
+ if (parser.PacketLength == 0)
+ {
+ parser.SyncState = false;
+ }
+ }
+ else if (parser.PID == parser.PMTPID)
+ {
+ if (parser.PMTTransferState)
+ {
+ if ((bufferLength - i) >= parser.PMTSectionLength)
+ {
+ offset = parser.PMTSectionLength;
+ }
+ else
+ {
+ offset = (bufferLength - i);
+ }
+ if (parser.PacketLength <= offset)
+ {
+ offset = parser.PacketLength;
+ }
+ if (!parser.PMT.ContainsKey(parser.PID))
+ {
+ parser.PMT[parser.PID] = new byte[1024];
+ }
+
+ byte[] PMT = parser.PMT[parser.PID];
+ for (int k = 0; k < offset; k++)
+ {
+ PMT[parser.PMTOffset++] = buffer[i++];
+ --parser.PMTSectionLength;
+ --parser.PacketLength;
+ } --i;
+
+ if (parser.PMTSectionLength == 0)
+ {
+ parser.PMTTransferState = false;
+ if (parser.PMTSectionNumber == parser.PMTLastSectionNumber)
+ {
+ //Console.WriteLine("PMT Start: " + parser.PMTTemp);
+ try
+ {
+ for (int k = 0; k < (parser.PMTOffset - 4); k += 5)
+ {
+ byte streamType = PMT[k];
+
+ ushort streamPID = (ushort)
+ (((PMT[k + 1] & 0x1F) << 8) +
+ PMT[k + 2]);
+
+ ushort streamInfoLength = (ushort)
+ (((PMT[k + 3] & 0xF) << 8) +
+ PMT[k + 4]);
+
+ /*
+ if (streamInfoLength == 2)
+ {
+ // TODO: Cleanup
+ //streamInfoLength = 0;
+ }
+
+ Console.WriteLine(string.Format(
+ "Type: {0} PID: {1} Length: {2}",
+ streamType, streamPID, streamInfoLength));
+ */
+
+ if (!Streams.ContainsKey(streamPID))
+ {
+ List<TSDescriptor> streamDescriptors =
+ new List<TSDescriptor>();
+
+ /*
+ * TODO: Getting bad streamInfoLength
+ if (streamInfoLength > 0)
+ {
+ for (int d = 0; d < streamInfoLength; d++)
+ {
+ byte name = PMT[k + d + 5];
+ byte length = PMT[k + d + 6];
+ TSDescriptor descriptor =
+ new TSDescriptor(name, length);
+ for (int v = 0; v < length; v++)
+ {
+ descriptor.Value[v] =
+ PMT[k + d + v + 7];
+ }
+ streamDescriptors.Add(descriptor);
+ d += (length + 1);
+ }
+ }
+ */
+ CreateStream(streamPID, streamType, streamDescriptors);
+ }
+ k += streamInfoLength;
+ }
+ }
+ catch (Exception ex)
+ {
+ // TODO
+ //Console.WriteLine(ex.Message);
+ }
+ }
+ }
+ }
+ else
+ {
+ --parser.PacketLength;
+ if (parser.PMTSectionStart)
+ {
+ parser.PMTPointerField = buffer[i];
+ if (parser.PMTPointerField == 0)
+ {
+ parser.PMTSectionLengthParse = 3;
+ }
+ parser.PMTSectionStart = false;
+ }
+ else if (parser.PMTPointerField > 0)
+ {
+ --parser.PMTPointerField;
+ if (parser.PMTPointerField == 0)
+ {
+ parser.PMTSectionLengthParse = 3;
+ }
+ }
+ else if (parser.PMTSectionLengthParse > 0)
+ {
+ --parser.PMTSectionLengthParse;
+ switch (parser.PMTSectionLengthParse)
+ {
+ case 2:
+ if (buffer[i] != 0x2)
+ {
+ parser.PMTSectionLengthParse = 0;
+ }
+ break;
+ case 1:
+ parser.PMTSectionLength = (ushort)
+ ((buffer[i] & 0xF) << 8);
+ break;
+ case 0:
+ parser.PMTSectionLength |= buffer[i];
+ if (parser.PMTSectionLength > 1021)
+ {
+ parser.PMTSectionLength = 0;
+ }
+ else
+ {
+ parser.PMTSectionParse = 9;
+ }
+ break;
+ }
+ }
+ else if (parser.PMTSectionParse > 0)
+ {
+ --parser.PMTSectionLength;
+ --parser.PMTSectionParse;
+
+ switch (parser.PMTSectionParse)
+ {
+ case 8:
+ case 7:
+ break;
+ case 6:
+ parser.PMTTemp = buffer[i];
+ break;
+ case 5:
+ parser.PMTSectionNumber = buffer[i];
+ if (parser.PMTSectionNumber == 0)
+ {
+ parser.PMTOffset = 0;
+ }
+ break;
+ case 4:
+ parser.PMTLastSectionNumber = buffer[i];
+ break;
+ case 3:
+ parser.PCRPID = (ushort)
+ ((buffer[i] & 0x1F) << 8);
+ break;
+ case 2:
+ parser.PCRPID |= buffer[i];
+ break;
+ case 1:
+ parser.PMTProgramInfoLength = (ushort)
+ ((buffer[i] & 0xF) << 8);
+ break;
+ case 0:
+ parser.PMTProgramInfoLength |= buffer[i];
+ if (parser.PMTProgramInfoLength == 0)
+ {
+ parser.PMTTransferState = true;
+ }
+ else
+ {
+ parser.PMTProgramDescriptorLengthParse = 2;
+ }
+ break;
+ }
+ }
+ else if (parser.PMTProgramInfoLength > 0)
+ {
+ --parser.PMTSectionLength;
+ --parser.PMTProgramInfoLength;
+
+ if (parser.PMTProgramDescriptorLengthParse > 0)
+ {
+ --parser.PMTProgramDescriptorLengthParse;
+ switch (parser.PMTProgramDescriptorLengthParse)
+ {
+ case 1:
+ parser.PMTProgramDescriptor = buffer[i];
+ break;
+ case 0:
+ parser.PMTProgramDescriptorLength = buffer[i];
+ parser.PMTProgramDescriptors.Add(
+ new TSDescriptor(
+ parser.PMTProgramDescriptor,
+ parser.PMTProgramDescriptorLength));
+ break;
+ }
+ }
+ else if (parser.PMTProgramDescriptorLength > 0)
+ {
+ --parser.PMTProgramDescriptorLength;
+
+ TSDescriptor descriptor = parser.PMTProgramDescriptors[
+ parser.PMTProgramDescriptors.Count - 1];
+
+ int valueIndex =
+ descriptor.Value.Length -
+ parser.PMTProgramDescriptorLength - 1;
+
+ descriptor.Value[valueIndex] = buffer[i];
+
+ if (parser.PMTProgramDescriptorLength == 0 &&
+ parser.PMTProgramInfoLength > 0)
+ {
+ parser.PMTProgramDescriptorLengthParse = 2;
+ }
+ }
+ if (parser.PMTProgramInfoLength == 0)
+ {
+ parser.PMTTransferState = true;
+ }
+ }
+ }
+ if (parser.PacketLength == 0)
+ {
+ parser.SyncState = false;
+ }
+ }
+ else if (parser.Stream != null &&
+ parser.StreamState != null &&
+ parser.TransportScramblingControl == 0)
+ {
+ TSStream stream = parser.Stream;
+ TSStreamState streamState = parser.StreamState;
+
+ streamState.Parse =
+ (streamState.Parse << 8) + buffer[i];
+
+ if (streamState.TransferState)
+ {
+ if ((bufferLength - i) >= streamState.PacketLength &&
+ streamState.PacketLength > 0)
+ {
+ offset = streamState.PacketLength;
+ }
+ else
+ {
+ offset = (bufferLength - i);
+ }
+ if (parser.PacketLength <= offset)
+ {
+ offset = parser.PacketLength;
+ }
+ streamState.TransferLength = offset;
+
+ if (!stream.IsInitialized ||
+ stream.IsVideoStream)
+ {
+ streamState.StreamBuffer.Add(
+ buffer, i, offset);
+ }
+ else
+ {
+ streamState.StreamBuffer.TransferLength += offset;
+ }
+
+ i += (int)(streamState.TransferLength - 1);
+ streamState.PacketLength -= streamState.TransferLength;
+ parser.PacketLength -= (byte)streamState.TransferLength;
+
+ streamState.TotalBytes += (ulong)streamState.TransferLength;
+ streamState.WindowBytes += (ulong)streamState.TransferLength;
+
+ if (streamState.PacketLength == 0)
+ {
+ streamState.TransferState = false;
+ streamState.TransferCount++;
+ bool isFinished = ScanStream(
+ stream,
+ streamState,
+ streamState.StreamBuffer);
+
+ if (!isFullScan && isFinished)
+ {
+ return;
+ }
+ }
+ }
+ else
+ {
+ --parser.PacketLength;
+
+ bool headerFound = false;
+ if (stream.IsVideoStream &&
+ streamState.Parse == 0x000001FD)
+ {
+ headerFound = true;
+ }
+ if (stream.IsVideoStream &&
+ streamState.Parse >= 0x000001E0 &&
+ streamState.Parse <= 0x000001EF)
+ {
+ headerFound = true;
+ }
+ if (stream.IsAudioStream &&
+ streamState.Parse == 0x000001BD)
+ {
+ headerFound = true;
+ }
+ if (stream.IsAudioStream &&
+ (streamState.Parse == 0x000001FA ||
+ streamState.Parse == 0x000001FD))
+ {
+ headerFound = true;
+ }
+
+ if (!stream.IsVideoStream &&
+ !stream.IsAudioStream &&
+ (streamState.Parse == 0x000001FA ||
+ streamState.Parse == 0x000001FD ||
+ streamState.Parse == 0x000001BD ||
+ (streamState.Parse >= 0x000001E0 &&
+ streamState.Parse <= 0x000001EF)))
+ {
+ headerFound = true;
+ }
+
+ if (headerFound)
+ {
+ streamState.PacketLengthParse = 2;
+#if DEBUG
+ streamState.PESHeaderIndex = 0;
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)((streamState.Parse >> 24) & 0xFF);
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)((streamState.Parse >> 16) & 0xFF);
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)((streamState.Parse >> 8) & 0xFF);
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)(streamState.Parse & 0xFF);
+#endif
+ }
+ else if (streamState.PacketLengthParse > 0)
+ {
+ --streamState.PacketLengthParse;
+ switch (streamState.PacketLengthParse)
+ {
+ case 1:
+#if DEBUG
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)(streamState.Parse & 0xFF);
+#endif
+ break;
+
+ case 0:
+ streamState.PacketLength =
+ (int)(streamState.Parse & 0xFFFF);
+ streamState.PacketParse = 3;
+#if DEBUG
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)(streamState.Parse & 0xFF);
+#endif
+ break;
+ }
+ }
+ else if (streamState.PacketParse > 0)
+ {
+ --streamState.PacketLength;
+ --streamState.PacketParse;
+
+ switch (streamState.PacketParse)
+ {
+ case 2:
+#if DEBUG
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)(streamState.Parse & 0xFF);
+#endif
+ break;
+
+ case 1:
+ streamState.PESHeaderFlags =
+ (byte)(streamState.Parse & 0xFF);
+#if DEBUG
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)(streamState.Parse & 0xFF);
+#endif
+ break;
+
+ case 0:
+ streamState.PESHeaderLength =
+ (byte)(streamState.Parse & 0xFF);
+#if DEBUG
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)(streamState.Parse & 0xFF);
+#endif
+ if ((streamState.PESHeaderFlags & 0xC0) == 0x80)
+ {
+ streamState.PTSParse = 5;
+ }
+ else if ((streamState.PESHeaderFlags & 0xC0) == 0xC0)
+ {
+ streamState.DTSParse = 10;
+ }
+ if (streamState.PESHeaderLength == 0)
+ {
+ streamState.TransferState = true;
+ }
+ break;
+ }
+ }
+ else if (streamState.PTSParse > 0)
+ {
+ --streamState.PacketLength;
+ --streamState.PESHeaderLength;
+ --streamState.PTSParse;
+
+ switch (streamState.PTSParse)
+ {
+ case 4:
+ streamState.PTSTemp =
+ ((streamState.Parse & 0xE) << 29);
+#if DEBUG
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)(streamState.Parse & 0xff);
+#endif
+ break;
+
+ case 3:
+ streamState.PTSTemp |=
+ ((streamState.Parse & 0xFF) << 22);
+#if DEBUG
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)(streamState.Parse & 0xFF);
+#endif
+ break;
+
+ case 2:
+ streamState.PTSTemp |=
+ ((streamState.Parse & 0xFE) << 14);
+#if DEBUG
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)(streamState.Parse & 0xFF);
+#endif
+ break;
+
+ case 1:
+ streamState.PTSTemp |=
+ ((streamState.Parse & 0xFF) << 7);
+#if DEBUG
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)(streamState.Parse & 0xFF);
+#endif
+ break;
+
+ case 0:
+ streamState.PTSTemp |=
+ ((streamState.Parse & 0xFE) >> 1);
+#if DEBUG
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)(streamState.Parse & 0xff);
+#endif
+ streamState.PTS = streamState.PTSTemp;
+
+ if (streamState.PTS > streamState.PTSLast)
+ {
+ if (streamState.PTSLast > 0)
+ {
+ streamState.PTSTransfer = (streamState.PTS - streamState.PTSLast);
+ }
+ streamState.PTSLast = streamState.PTS;
+ }
+
+ streamState.PTSDiff = streamState.PTS - streamState.DTSPrev;
+
+ if (streamState.PTSCount > 0 &&
+ stream.IsVideoStream)
+ {
+ UpdateStreamBitrates(stream.PID, streamState.PTS, streamState.PTSDiff);
+ if (streamState.DTSTemp < parser.PTSFirst)
+ {
+ parser.PTSFirst = streamState.DTSTemp;
+ }
+ if (streamState.DTSTemp > parser.PTSLast)
+ {
+ parser.PTSLast = streamState.DTSTemp;
+ }
+ Length = (double)(parser.PTSLast - parser.PTSFirst) / 90000;
+ }
+
+ streamState.DTSPrev = streamState.PTS;
+ streamState.PTSCount++;
+ if (streamState.PESHeaderLength == 0)
+ {
+ streamState.TransferState = true;
+ }
+ break;
+ }
+ }
+ else if (streamState.DTSParse > 0)
+ {
+ --streamState.PacketLength;
+ --streamState.PESHeaderLength;
+ --streamState.DTSParse;
+
+ switch (streamState.DTSParse)
+ {
+ case 9:
+ streamState.PTSTemp =
+ ((streamState.Parse & 0xE) << 29);
+#if DEBUG
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)(streamState.Parse & 0xFF);
+#endif
+ break;
+
+ case 8:
+ streamState.PTSTemp |=
+ ((streamState.Parse & 0xFF) << 22);
+#if DEBUG
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)(streamState.Parse & 0xFF);
+#endif
+ break;
+
+ case 7:
+ streamState.PTSTemp |=
+ ((streamState.Parse & 0xFE) << 14);
+#if DEBUG
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)(streamState.Parse & 0xff);
+#endif
+ break;
+
+ case 6:
+ streamState.PTSTemp |=
+ ((streamState.Parse & 0xFF) << 7);
+#if DEBUG
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)(streamState.Parse & 0xFF);
+#endif
+ break;
+
+ case 5:
+ streamState.PTSTemp |=
+ ((streamState.Parse & 0xFE) >> 1);
+#if DEBUG
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)(streamState.Parse & 0xff);
+#endif
+ streamState.PTS = streamState.PTSTemp;
+ if (streamState.PTS > streamState.PTSLast)
+ {
+ streamState.PTSLast = streamState.PTS;
+ }
+ break;
+
+ case 4:
+ streamState.DTSTemp =
+ ((streamState.Parse & 0xE) << 29);
+#if DEBUG
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)(streamState.Parse & 0xff);
+#endif
+ break;
+
+ case 3:
+ streamState.DTSTemp |=
+ ((streamState.Parse & 0xFF) << 22);
+#if DEBUG
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)(streamState.Parse & 0xff);
+#endif
+ break;
+
+ case 2:
+ streamState.DTSTemp |=
+ ((streamState.Parse & 0xFE) << 14);
+#if DEBUG
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)(streamState.Parse & 0xff);
+#endif
+ break;
+
+ case 1:
+ streamState.DTSTemp |=
+ ((streamState.Parse & 0xFF) << 7);
+#if DEBUG
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)(streamState.Parse & 0xFF);
+#endif
+ break;
+
+ case 0:
+ streamState.DTSTemp |=
+ ((streamState.Parse & 0xFE) >> 1);
+#if DEBUG
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)(streamState.Parse & 0xff);
+#endif
+ streamState.PTSDiff = streamState.DTSTemp - streamState.DTSPrev;
+
+ if (streamState.PTSCount > 0 &&
+ stream.IsVideoStream)
+ {
+ UpdateStreamBitrates(stream.PID, streamState.DTSTemp, streamState.PTSDiff);
+ if (streamState.DTSTemp < parser.PTSFirst)
+ {
+ parser.PTSFirst = streamState.DTSTemp;
+ }
+ if (streamState.DTSTemp > parser.PTSLast)
+ {
+ parser.PTSLast = streamState.DTSTemp;
+ }
+ Length = (double)(parser.PTSLast - parser.PTSFirst) / 90000;
+ }
+ streamState.DTSPrev = streamState.DTSTemp;
+ streamState.PTSCount++;
+ if (streamState.PESHeaderLength == 0)
+ {
+ streamState.TransferState = true;
+ }
+ break;
+ }
+ }
+ else if (streamState.PESHeaderLength > 0)
+ {
+ --streamState.PacketLength;
+ --streamState.PESHeaderLength;
+#if DEBUG
+ streamState.PESHeader[streamState.PESHeaderIndex++] =
+ (byte)(streamState.Parse & 0xFF);
+#endif
+ if (streamState.PESHeaderLength == 0)
+ {
+ streamState.TransferState = true;
+ }
+ }
+ }
+ if (parser.PacketLength == 0)
+ {
+ parser.SyncState = false;
+ }
+ }
+ else
+ {
+ parser.PacketLength--;
+ if ((bufferLength - i) >= parser.PacketLength)
+ {
+ i = i + parser.PacketLength;
+ parser.PacketLength = 0;
+ }
+ else
+ {
+ parser.PacketLength -= (byte)((bufferLength - i) + 1);
+ i = bufferLength;
+ }
+ if (parser.PacketLength == 0)
+ {
+ parser.SyncState = false;
+ }
+ }
+ }
+ Size += bufferLength;
+ }
+
+ ulong PTSLast = 0;
+ ulong PTSDiff = 0;
+ foreach (TSStream stream in Streams.Values)
+ {
+ if (!stream.IsVideoStream) continue;
+
+ if (StreamStates.ContainsKey(stream.PID) &&
+ StreamStates[stream.PID].PTSLast > PTSLast)
+ {
+ PTSLast = StreamStates[stream.PID].PTSLast;
+ PTSDiff = PTSLast - StreamStates[stream.PID].DTSPrev;
+ }
+ UpdateStreamBitrates(stream.PID, PTSLast, PTSDiff);
+ }
+ }
+ finally
+ {
+ if (fileStream != null)
+ {
+ fileStream.Dispose();
+ }
+ }
+ }
+
+ private TSStream CreateStream(
+ ushort streamPID,
+ byte streamType,
+ List<TSDescriptor> streamDescriptors)
+ {
+ TSStream stream = null;
+
+ switch ((TSStreamType)streamType)
+ {
+ case TSStreamType.MVC_VIDEO:
+ case TSStreamType.AVC_VIDEO:
+ case TSStreamType.MPEG1_VIDEO:
+ case TSStreamType.MPEG2_VIDEO:
+ case TSStreamType.VC1_VIDEO:
+ {
+ stream = new TSVideoStream();
+ }
+ break;
+
+ case TSStreamType.AC3_AUDIO:
+ case TSStreamType.AC3_PLUS_AUDIO:
+ case TSStreamType.AC3_PLUS_SECONDARY_AUDIO:
+ case TSStreamType.AC3_TRUE_HD_AUDIO:
+ case TSStreamType.DTS_AUDIO:
+ case TSStreamType.DTS_HD_AUDIO:
+ case TSStreamType.DTS_HD_MASTER_AUDIO:
+ case TSStreamType.DTS_HD_SECONDARY_AUDIO:
+ case TSStreamType.LPCM_AUDIO:
+ case TSStreamType.MPEG1_AUDIO:
+ case TSStreamType.MPEG2_AUDIO:
+ {
+ stream = new TSAudioStream();
+ }
+ break;
+
+ case TSStreamType.INTERACTIVE_GRAPHICS:
+ case TSStreamType.PRESENTATION_GRAPHICS:
+ {
+ stream = new TSGraphicsStream();
+ }
+ break;
+
+ case TSStreamType.SUBTITLE:
+ {
+ stream = new TSTextStream();
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (stream != null &&
+ !Streams.ContainsKey(streamPID))
+ {
+ stream.PID = streamPID;
+ stream.StreamType = (TSStreamType)streamType;
+ stream.Descriptors = streamDescriptors;
+ Streams[stream.PID] = stream;
+ }
+ if (!StreamDiagnostics.ContainsKey(streamPID))
+ {
+ StreamDiagnostics[streamPID] =
+ new List<TSStreamDiagnostics>();
+ }
+
+ return stream;
+ }
+ }
+}
diff --git a/BDInfo/project.json b/BDInfo/project.json
new file mode 100644
index 0000000000..fbbe9eaf32
--- /dev/null
+++ b/BDInfo/project.json
@@ -0,0 +1,17 @@
+{
+ "frameworks":{
+ "netstandard1.6":{
+ "dependencies":{
+ "NETStandard.Library":"1.6.0",
+ }
+ },
+ ".NETPortable,Version=v4.5,Profile=Profile7":{
+ "buildOptions": {
+ "define": [ ]
+ },
+ "frameworkAssemblies":{
+
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index e69de29bb2..0000000000
--- a/CONTRIBUTING.md
+++ /dev/null
diff --git a/DvdLib/BigEndianBinaryReader.cs b/DvdLib/BigEndianBinaryReader.cs
new file mode 100644
index 0000000000..56d9db8255
--- /dev/null
+++ b/DvdLib/BigEndianBinaryReader.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.IO;
+
+namespace DvdLib
+{
+ public class BigEndianBinaryReader : BinaryReader
+ {
+ public BigEndianBinaryReader(Stream input)
+ : base(input)
+ {
+ }
+
+ public override ushort ReadUInt16()
+ {
+ return BitConverter.ToUInt16(ReadAndReverseBytes(2), 0);
+ }
+
+ public override uint ReadUInt32()
+ {
+ return BitConverter.ToUInt32(ReadAndReverseBytes(4), 0);
+ }
+
+ private byte[] ReadAndReverseBytes(int count)
+ {
+ byte[] val = base.ReadBytes(count);
+ Array.Reverse(val, 0, count);
+ return val;
+ }
+ }
+}
diff --git a/DvdLib/DvdLib.csproj b/DvdLib/DvdLib.csproj
new file mode 100644
index 0000000000..ba63e77f0f
--- /dev/null
+++ b/DvdLib/DvdLib.csproj
@@ -0,0 +1,71 @@
+<?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>
+ <ItemGroup>
+ <None Include="project.json" />
+ <!-- A reference to the entire .NET Framework is automatically included -->
+ </ItemGroup>
+ <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" />
+ </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
diff --git a/DvdLib/DvdLib.nuget.targets b/DvdLib/DvdLib.nuget.targets
new file mode 100644
index 0000000000..e69ce0e64f
--- /dev/null
+++ b/DvdLib/DvdLib.nuget.targets
@@ -0,0 +1,6 @@
+<?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/AudioAttributes.cs b/DvdLib/Ifo/AudioAttributes.cs
new file mode 100644
index 0000000000..5b3b9fd9a0
--- /dev/null
+++ b/DvdLib/Ifo/AudioAttributes.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace DvdLib.Ifo
+{
+ public enum AudioCodec
+ {
+ AC3 = 0,
+ MPEG1 = 2,
+ MPEG2ext = 3,
+ LPCM = 4,
+ DTS = 6,
+ }
+
+ public enum ApplicationMode
+ {
+ Unspecified = 0,
+ Karaoke = 1,
+ Surround = 2,
+ }
+
+ public class AudioAttributes
+ {
+ public readonly AudioCodec Codec;
+ public readonly bool MultichannelExtensionPresent;
+ public readonly ApplicationMode Mode;
+ public readonly byte QuantDRC;
+ public readonly byte SampleRate;
+ public readonly byte Channels;
+ public readonly ushort LanguageCode;
+ public readonly byte LanguageExtension;
+ public readonly byte CodeExtension;
+ }
+
+ public class MultiChannelExtension
+ {
+
+ }
+}
diff --git a/DvdLib/Ifo/Cell.cs b/DvdLib/Ifo/Cell.cs
new file mode 100644
index 0000000000..d0f442e362
--- /dev/null
+++ b/DvdLib/Ifo/Cell.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.IO;
+
+namespace DvdLib.Ifo
+{
+ public class Cell
+ {
+ public CellPlaybackInfo PlaybackInfo { get; private set; }
+ public CellPositionInfo PositionInfo { get; private set; }
+
+ internal void ParsePlayback(BinaryReader br)
+ {
+ PlaybackInfo = new CellPlaybackInfo(br);
+ }
+
+ internal void ParsePosition(BinaryReader br)
+ {
+ PositionInfo = new CellPositionInfo(br);
+ }
+ }
+}
diff --git a/DvdLib/Ifo/CellPlaybackInfo.cs b/DvdLib/Ifo/CellPlaybackInfo.cs
new file mode 100644
index 0000000000..ae3883eaa0
--- /dev/null
+++ b/DvdLib/Ifo/CellPlaybackInfo.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.IO;
+
+namespace DvdLib.Ifo
+{
+ public enum BlockMode
+ {
+ NotInBlock = 0,
+ FirstCell = 1,
+ InBlock = 2,
+ LastCell = 3,
+ }
+
+ public enum BlockType
+ {
+ Normal = 0,
+ Angle = 1,
+ }
+
+ public enum PlaybackMode
+ {
+ Normal = 0,
+ StillAfterEachVOBU = 1,
+ }
+
+ public class CellPlaybackInfo
+ {
+ public readonly BlockMode Mode;
+ public readonly BlockType Type;
+ public readonly bool SeamlessPlay;
+ public readonly bool Interleaved;
+ public readonly bool STCDiscontinuity;
+ public readonly bool SeamlessAngle;
+ public readonly PlaybackMode PlaybackMode;
+ public readonly bool Restricted;
+ public readonly byte StillTime;
+ public readonly byte CommandNumber;
+ public readonly DvdTime PlaybackTime;
+ public readonly uint FirstSector;
+ public readonly uint FirstILVUEndSector;
+ public readonly uint LastVOBUStartSector;
+ public readonly uint LastSector;
+
+ internal CellPlaybackInfo(BinaryReader br)
+ {
+ br.BaseStream.Seek(0x4, SeekOrigin.Current);
+ PlaybackTime = new DvdTime(br.ReadBytes(4));
+ br.BaseStream.Seek(0x10, SeekOrigin.Current);
+ }
+ }
+}
diff --git a/DvdLib/Ifo/CellPositionInfo.cs b/DvdLib/Ifo/CellPositionInfo.cs
new file mode 100644
index 0000000000..2e07159402
--- /dev/null
+++ b/DvdLib/Ifo/CellPositionInfo.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.IO;
+
+namespace DvdLib.Ifo
+{
+ public class CellPositionInfo
+ {
+ public readonly ushort VOBId;
+ public readonly byte CellId;
+
+ internal CellPositionInfo(BinaryReader br)
+ {
+ VOBId = br.ReadUInt16();
+ br.ReadByte();
+ CellId = br.ReadByte();
+ }
+ }
+}
diff --git a/DvdLib/Ifo/Chapter.cs b/DvdLib/Ifo/Chapter.cs
new file mode 100644
index 0000000000..802c6ce62a
--- /dev/null
+++ b/DvdLib/Ifo/Chapter.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace DvdLib.Ifo
+{
+ public class Chapter
+ {
+ public ushort ProgramChainNumber { get; private set; }
+ public ushort ProgramNumber { get; private set; }
+ public uint ChapterNumber { get; private set; }
+
+ public Chapter(ushort pgcNum, ushort programNum, uint chapterNum)
+ {
+ ProgramChainNumber = pgcNum;
+ ProgramNumber = programNum;
+ ChapterNumber = chapterNum;
+ }
+ }
+}
diff --git a/DvdLib/Ifo/Dvd.cs b/DvdLib/Ifo/Dvd.cs
new file mode 100644
index 0000000000..508c23db45
--- /dev/null
+++ b/DvdLib/Ifo/Dvd.cs
@@ -0,0 +1,161 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.IO;
+using System.Diagnostics;
+using MediaBrowser.Model.IO;
+
+namespace DvdLib.Ifo
+{
+ public class Dvd
+ {
+ private readonly ushort _titleSetCount;
+ public readonly List<Title> Titles;
+
+ private ushort _titleCount;
+ public readonly Dictionary<ushort, string> VTSPaths = new Dictionary<ushort, string>();
+ private readonly IFileSystem _fileSystem;
+
+ public Dvd(string path, IFileSystem fileSystem)
+ {
+ _fileSystem = fileSystem;
+ Titles = new List<Title>();
+ var allFiles = _fileSystem.GetFiles(path, true).ToList();
+
+ var vmgPath = allFiles.FirstOrDefault(i => string.Equals(i.Name, "VIDEO_TS.IFO", StringComparison.OrdinalIgnoreCase)) ??
+ allFiles.FirstOrDefault(i => string.Equals(i.Name, "VIDEO_TS.BUP", StringComparison.OrdinalIgnoreCase));
+
+ if (vmgPath == null)
+ {
+ var allIfos = allFiles.Where(i => string.Equals(i.Extension, ".ifo", StringComparison.OrdinalIgnoreCase));
+
+ foreach (var ifo in allIfos)
+ {
+ var num = ifo.Name.Split('_').ElementAtOrDefault(1);
+ ushort ifoNumber;
+ var numbersRead = new List<ushort>();
+
+ if (!string.IsNullOrEmpty(num) && ushort.TryParse(num, out ifoNumber) && !numbersRead.Contains(ifoNumber))
+ {
+ ReadVTS(ifoNumber, ifo.FullName);
+ numbersRead.Add(ifoNumber);
+ }
+ }
+ }
+ else
+ {
+ using (var vmgFs = _fileSystem.GetFileStream(vmgPath.FullName, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
+ {
+ using (BigEndianBinaryReader vmgRead = new BigEndianBinaryReader(vmgFs))
+ {
+ vmgFs.Seek(0x3E, SeekOrigin.Begin);
+ _titleSetCount = vmgRead.ReadUInt16();
+
+ // read address of TT_SRPT
+ vmgFs.Seek(0xC4, SeekOrigin.Begin);
+ uint ttSectorPtr = vmgRead.ReadUInt32();
+ vmgFs.Seek(ttSectorPtr * 2048, SeekOrigin.Begin);
+ ReadTT_SRPT(vmgRead);
+ }
+ }
+
+ for (ushort titleSetNum = 1; titleSetNum <= _titleSetCount; titleSetNum++)
+ {
+ ReadVTS(titleSetNum, allFiles);
+ }
+ }
+ }
+
+ private void ReadTT_SRPT(BinaryReader read)
+ {
+ _titleCount = read.ReadUInt16();
+ read.BaseStream.Seek(6, SeekOrigin.Current);
+ for (uint titleNum = 1; titleNum <= _titleCount; titleNum++)
+ {
+ Title t = new Title(titleNum);
+ t.ParseTT_SRPT(read);
+ Titles.Add(t);
+ }
+ }
+
+ private void ReadVTS(ushort vtsNum, List<FileSystemMetadata> allFiles)
+ {
+ var filename = String.Format("VTS_{0:00}_0.IFO", vtsNum);
+
+ var vtsPath = allFiles.FirstOrDefault(i => string.Equals(i.Name, filename, StringComparison.OrdinalIgnoreCase)) ??
+ allFiles.FirstOrDefault(i => string.Equals(i.Name, Path.ChangeExtension(filename, ".bup"), StringComparison.OrdinalIgnoreCase));
+
+ if (vtsPath == null)
+ {
+ throw new FileNotFoundException("Unable to find VTS IFO file");
+ }
+
+ ReadVTS(vtsNum, vtsPath.FullName);
+ }
+
+ private void ReadVTS(ushort vtsNum, string vtsPath)
+ {
+ VTSPaths[vtsNum] = vtsPath;
+
+ using (var vtsFs = _fileSystem.GetFileStream(vtsPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
+ {
+ using (BigEndianBinaryReader vtsRead = new BigEndianBinaryReader(vtsFs))
+ {
+ // Read VTS_PTT_SRPT
+ vtsFs.Seek(0xC8, SeekOrigin.Begin);
+ uint vtsPttSrptSecPtr = vtsRead.ReadUInt32();
+ uint baseAddr = (vtsPttSrptSecPtr * 2048);
+ vtsFs.Seek(baseAddr, SeekOrigin.Begin);
+
+ ushort numTitles = vtsRead.ReadUInt16();
+ vtsRead.ReadUInt16();
+ uint endaddr = vtsRead.ReadUInt32();
+ uint[] offsets = new uint[numTitles];
+ for (ushort titleNum = 0; titleNum < numTitles; titleNum++)
+ {
+ offsets[titleNum] = vtsRead.ReadUInt32();
+ }
+
+ for (uint titleNum = 0; titleNum < numTitles; titleNum++)
+ {
+ uint chapNum = 1;
+ vtsFs.Seek(baseAddr + offsets[titleNum], SeekOrigin.Begin);
+ Title t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum + 1));
+ if (t == null) continue;
+
+ do
+ {
+ t.Chapters.Add(new Chapter(vtsRead.ReadUInt16(), vtsRead.ReadUInt16(), chapNum));
+ if (titleNum + 1 < numTitles && vtsFs.Position == (baseAddr + offsets[titleNum + 1])) break;
+ chapNum++;
+ }
+ while (vtsFs.Position < (baseAddr + endaddr));
+ }
+
+ // Read VTS_PGCI
+ vtsFs.Seek(0xCC, SeekOrigin.Begin);
+ uint vtsPgciSecPtr = vtsRead.ReadUInt32();
+ vtsFs.Seek(vtsPgciSecPtr * 2048, SeekOrigin.Begin);
+
+ long startByte = vtsFs.Position;
+
+ ushort numPgcs = vtsRead.ReadUInt16();
+ vtsFs.Seek(6, SeekOrigin.Current);
+ for (ushort pgcNum = 1; pgcNum <= numPgcs; pgcNum++)
+ {
+ byte pgcCat = vtsRead.ReadByte();
+ bool entryPgc = (pgcCat & 0x80) != 0;
+ uint titleNum = (uint)(pgcCat & 0x7F);
+
+ vtsFs.Seek(3, SeekOrigin.Current);
+ uint vtsPgcOffset = vtsRead.ReadUInt32();
+
+ Title t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum));
+ if (t != null) t.AddPgc(vtsRead, startByte + vtsPgcOffset, entryPgc, pgcNum);
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/DvdLib/Ifo/DvdTime.cs b/DvdLib/Ifo/DvdTime.cs
new file mode 100644
index 0000000000..f565f5fdf7
--- /dev/null
+++ b/DvdLib/Ifo/DvdTime.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace DvdLib.Ifo
+{
+ public class DvdTime
+ {
+ public readonly byte Hour, Minute, Second, Frames, FrameRate;
+
+ public DvdTime(byte[] data)
+ {
+ Hour = GetBCDValue(data[0]);
+ Minute = GetBCDValue(data[1]);
+ Second = GetBCDValue(data[2]);
+ Frames = GetBCDValue((byte)(data[3] & 0x3F));
+
+ if ((data[3] & 0x80) != 0) FrameRate = 30;
+ else if ((data[3] & 0x40) != 0) FrameRate = 25;
+ }
+
+ private byte GetBCDValue(byte data)
+ {
+ return (byte)((((data & 0xF0) >> 4) * 10) + (data & 0x0F));
+ }
+
+ public static explicit operator TimeSpan(DvdTime time)
+ {
+ int ms = (int)(((1.0 / (double)time.FrameRate) * time.Frames) * 1000.0);
+ return new TimeSpan(0, time.Hour, time.Minute, time.Second, ms);
+ }
+ }
+}
diff --git a/DvdLib/Ifo/PgcCommandTable.cs b/DvdLib/Ifo/PgcCommandTable.cs
new file mode 100644
index 0000000000..2ead78cef9
--- /dev/null
+++ b/DvdLib/Ifo/PgcCommandTable.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace DvdLib.Ifo
+{
+ public class ProgramChainCommandTable
+ {
+ public readonly ushort LastByteAddress;
+ public readonly List<VirtualMachineCommand> PreCommands;
+ public readonly List<VirtualMachineCommand> PostCommands;
+ public readonly List<VirtualMachineCommand> CellCommands;
+ }
+
+ public class VirtualMachineCommand
+ {
+ public readonly byte[] Command;
+ }
+}
diff --git a/DvdLib/Ifo/Program.cs b/DvdLib/Ifo/Program.cs
new file mode 100644
index 0000000000..48870d9dd0
--- /dev/null
+++ b/DvdLib/Ifo/Program.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace DvdLib.Ifo
+{
+ public class Program
+ {
+ public readonly List<Cell> Cells;
+
+ public Program(List<Cell> cells)
+ {
+ Cells = cells;
+ }
+ }
+}
diff --git a/DvdLib/Ifo/ProgramChain.cs b/DvdLib/Ifo/ProgramChain.cs
new file mode 100644
index 0000000000..3179f73cd6
--- /dev/null
+++ b/DvdLib/Ifo/ProgramChain.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.IO;
+
+namespace DvdLib.Ifo
+{
+ public enum ProgramPlaybackMode
+ {
+ Sequential,
+ Random,
+ Shuffle
+ }
+
+ public class ProgramChain
+ {
+ private ushort _unknown1;
+
+ private byte _programCount;
+ public readonly List<Program> Programs;
+
+ private byte _cellCount;
+ public readonly List<Cell> Cells;
+
+ public DvdTime PlaybackTime { get; private set; }
+ public UserOperation ProhibitedUserOperations { get; private set; }
+ public byte[] AudioStreamControl { get; private set; } // 8*2 entries
+ public byte[] SubpictureStreamControl { get; private set; } // 32*4 entries
+
+ private ushort _nextProgramNumber;
+ public readonly ProgramChain Next;
+
+ private ushort _prevProgramNumber;
+ public readonly ProgramChain Previous;
+
+ private ushort _goupProgramNumber;
+ public readonly ProgramChain Goup; // ?? maybe Group
+
+ private byte _playbackMode;
+ public ProgramPlaybackMode PlaybackMode { get; private set; }
+ public uint ProgramCount { get; private set; }
+
+ public byte StillTime { get; private set; }
+ public byte[] Palette { get; private set; } // 16*4 entries
+
+ private ushort _commandTableOffset;
+ public readonly ProgramChainCommandTable CommandTable;
+
+ private ushort _programMapOffset;
+ private ushort _cellPlaybackOffset;
+ private ushort _cellPositionOffset;
+
+ public readonly uint VideoTitleSetIndex;
+
+ internal ProgramChain(uint vtsPgcNum)
+ {
+ VideoTitleSetIndex = vtsPgcNum;
+ Cells = new List<Cell>();
+ Programs = new List<Program>();
+ }
+
+ internal void ParseHeader(BinaryReader br)
+ {
+ long startPos = br.BaseStream.Position;
+
+ br.ReadUInt16();
+ _programCount = br.ReadByte();
+ _cellCount = br.ReadByte();
+ PlaybackTime = new DvdTime(br.ReadBytes(4));
+ ProhibitedUserOperations = (UserOperation)br.ReadUInt32();
+ AudioStreamControl = br.ReadBytes(16);
+ SubpictureStreamControl = br.ReadBytes(128);
+
+ _nextProgramNumber = br.ReadUInt16();
+ _prevProgramNumber = br.ReadUInt16();
+ _goupProgramNumber = br.ReadUInt16();
+
+ StillTime = br.ReadByte();
+ byte pbMode = br.ReadByte();
+ if (pbMode == 0) PlaybackMode = ProgramPlaybackMode.Sequential;
+ else PlaybackMode = ((pbMode & 0x80) == 0) ? ProgramPlaybackMode.Random : ProgramPlaybackMode.Shuffle;
+ ProgramCount = (uint)(pbMode & 0x7F);
+
+ Palette = br.ReadBytes(64);
+ _commandTableOffset = br.ReadUInt16();
+ _programMapOffset = br.ReadUInt16();
+ _cellPlaybackOffset = br.ReadUInt16();
+ _cellPositionOffset = br.ReadUInt16();
+
+ // read position info
+ br.BaseStream.Seek(startPos + _cellPositionOffset, SeekOrigin.Begin);
+ for (int cellNum = 0; cellNum < _cellCount; cellNum++)
+ {
+ Cell c = new Cell();
+ c.ParsePosition(br);
+ Cells.Add(c);
+ }
+
+ br.BaseStream.Seek(startPos + _cellPlaybackOffset, SeekOrigin.Begin);
+ for (int cellNum = 0; cellNum < _cellCount; cellNum++)
+ {
+ Cells[cellNum].ParsePlayback(br);
+ }
+
+ br.BaseStream.Seek(startPos + _programMapOffset, SeekOrigin.Begin);
+ List<int> cellNumbers = new List<int>();
+ for (int progNum = 0; progNum < _programCount; progNum++) cellNumbers.Add(br.ReadByte() - 1);
+
+ for (int i = 0; i < cellNumbers.Count; i++)
+ {
+ int max = (i + 1 == cellNumbers.Count) ? _cellCount : cellNumbers[i+1];
+ Programs.Add(new Program(Cells.Where((c, idx) => idx >= cellNumbers[i] && idx < max).ToList()));
+ }
+ }
+ }
+}
diff --git a/DvdLib/Ifo/Title.cs b/DvdLib/Ifo/Title.cs
new file mode 100644
index 0000000000..70deb45bfa
--- /dev/null
+++ b/DvdLib/Ifo/Title.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.IO;
+
+namespace DvdLib.Ifo
+{
+ public class Title
+ {
+ public uint TitleNumber { get; private set; }
+ public uint AngleCount { get; private set; }
+ public ushort ChapterCount { get; private set; }
+ public byte VideoTitleSetNumber { get; private set; }
+
+ private ushort _parentalManagementMask;
+ private byte _titleNumberInVTS;
+ private uint _vtsStartSector; // relative to start of entire disk
+
+ public ProgramChain EntryProgramChain { get; private set; }
+ public readonly List<ProgramChain> ProgramChains;
+
+ public readonly List<Chapter> Chapters;
+
+ public Title(uint titleNum)
+ {
+ ProgramChains = new List<ProgramChain>();
+ Chapters = new List<Chapter>();
+ Chapters = new List<Chapter>();
+ TitleNumber = titleNum;
+ }
+
+ public bool IsVTSTitle(uint vtsNum, uint vtsTitleNum)
+ {
+ return (vtsNum == VideoTitleSetNumber && vtsTitleNum == _titleNumberInVTS);
+ }
+
+ internal void ParseTT_SRPT(BinaryReader br)
+ {
+ byte titleType = br.ReadByte();
+ // TODO parse Title Type
+
+ AngleCount = br.ReadByte();
+ ChapterCount = br.ReadUInt16();
+ _parentalManagementMask = br.ReadUInt16();
+ VideoTitleSetNumber = br.ReadByte();
+ _titleNumberInVTS = br.ReadByte();
+ _vtsStartSector = br.ReadUInt32();
+ }
+
+ internal void AddPgc(BinaryReader br, long startByte, bool entryPgc, uint pgcNum)
+ {
+ long curPos = br.BaseStream.Position;
+ br.BaseStream.Seek(startByte, SeekOrigin.Begin);
+
+ ProgramChain pgc = new ProgramChain(pgcNum);
+ pgc.ParseHeader(br);
+ ProgramChains.Add(pgc);
+ if (entryPgc) EntryProgramChain = pgc;
+
+ br.BaseStream.Seek(curPos, SeekOrigin.Begin);
+ }
+ }
+}
diff --git a/DvdLib/Ifo/UserOperation.cs b/DvdLib/Ifo/UserOperation.cs
new file mode 100644
index 0000000000..c3cffd4870
--- /dev/null
+++ b/DvdLib/Ifo/UserOperation.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace DvdLib.Ifo
+{
+ [Flags]
+ public enum UserOperation
+ {
+ None = 0,
+ TitleOrTimePlay = 1,
+ ChapterSearchOrPlay = 2,
+ TitlePlay = 4,
+ Stop = 8,
+ GoUp = 16,
+ TimeOrChapterSearch = 32,
+ PrevOrTopProgramSearch = 64,
+ NextProgramSearch = 128,
+ ForwardScan = 256,
+ BackwardScan = 512,
+ TitleMenuCall = 1024,
+ RootMenuCall = 2048,
+ SubpictureMenuCall = 4096,
+ AudioMenuCall = 8192,
+ AngleMenuCall = 16384,
+ ChapterMenuCall = 32768,
+ Resume = 65536,
+ ButtonSelectOrActive = 131072,
+ StillOff = 262144,
+ PauseOn = 524288,
+ AudioStreamChange = 1048576,
+ SubpictureStreamChange = 2097152,
+ AngleChange = 4194304,
+ KaraokeAudioPresentationModeChange = 8388608,
+ VideoPresentationModeChange = 16777216,
+ }
+}
diff --git a/DvdLib/Ifo/VideoAttributes.cs b/DvdLib/Ifo/VideoAttributes.cs
new file mode 100644
index 0000000000..b2d3759426
--- /dev/null
+++ b/DvdLib/Ifo/VideoAttributes.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace DvdLib.Ifo
+{
+ public enum VideoCodec
+ {
+ MPEG1 = 0,
+ MPEG2 = 1,
+ }
+
+ public enum VideoFormat
+ {
+ NTSC = 0,
+ PAL = 1,
+ }
+
+ public enum AspectRatio
+ {
+ ar4to3 = 0,
+ ar16to9 = 3
+ }
+
+ public enum FilmMode
+ {
+ None = -1,
+ Camera = 0,
+ Film = 1,
+ }
+
+ public class VideoAttributes
+ {
+ public readonly VideoCodec Codec;
+ public readonly VideoFormat Format;
+ public readonly AspectRatio Aspect;
+ public readonly bool AutomaticPanScan;
+ public readonly bool AutomaticLetterBox;
+ public readonly bool Line21CCField1;
+ public readonly bool Line21CCField2;
+ public readonly int Width;
+ public readonly int Height;
+ public readonly bool Letterboxed;
+ public readonly FilmMode FilmMode;
+
+ public VideoAttributes()
+ {
+ }
+ }
+}
diff --git a/DvdLib/Properties/AssemblyInfo.cs b/DvdLib/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..cca792684f
--- /dev/null
+++ b/DvdLib/Properties/AssemblyInfo.cs
@@ -0,0 +1,29 @@
+using System.Resources;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("DvdLib")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("DvdLib")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[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
+//
+// 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.1")] \ No newline at end of file
diff --git a/DvdLib/project.json b/DvdLib/project.json
new file mode 100644
index 0000000000..fbbe9eaf32
--- /dev/null
+++ b/DvdLib/project.json
@@ -0,0 +1,17 @@
+{
+ "frameworks":{
+ "netstandard1.6":{
+ "dependencies":{
+ "NETStandard.Library":"1.6.0",
+ }
+ },
+ ".NETPortable,Version=v4.5,Profile=Profile7":{
+ "buildOptions": {
+ "define": [ ]
+ },
+ "frameworkAssemblies":{
+
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Common.Implementations/Archiving/ZipClient.cs b/Emby.Common.Implementations/Archiving/ZipClient.cs
index 1272e43b90..791c6678cd 100644
--- a/MediaBrowser.Common.Implementations/Archiving/ZipClient.cs
+++ b/Emby.Common.Implementations/Archiving/ZipClient.cs
@@ -1,21 +1,20 @@
-using MediaBrowser.Model.IO;
-using SharpCompress.Archive.Rar;
-using SharpCompress.Archive.SevenZip;
-using SharpCompress.Archive.Tar;
+using System.IO;
+using MediaBrowser.Model.IO;
+using SharpCompress.Archives.Rar;
+using SharpCompress.Archives.SevenZip;
+using SharpCompress.Archives.Tar;
using SharpCompress.Common;
-using SharpCompress.Reader;
-using SharpCompress.Reader.Zip;
-using System.IO;
-using CommonIO;
+using SharpCompress.Readers;
+using SharpCompress.Readers.Zip;
-namespace MediaBrowser.Common.Implementations.Archiving
+namespace Emby.Common.Implementations.Archiving
{
/// <summary>
/// Class DotNetZipClient
/// </summary>
public class ZipClient : IZipClient
{
- private IFileSystem _fileSystem;
+ private readonly IFileSystem _fileSystem;
public ZipClient(IFileSystem fileSystem)
{
@@ -46,11 +45,12 @@ namespace MediaBrowser.Common.Implementations.Archiving
{
using (var reader = ReaderFactory.Open(source))
{
- var options = ExtractOptions.ExtractFullPath;
+ var options = new ExtractionOptions();
+ options.ExtractFullPath = true;
if (overwriteExistingFiles)
{
- options = options | ExtractOptions.Overwrite;
+ options.Overwrite = true;
}
reader.WriteAllToDirectory(targetPath, options);
@@ -61,11 +61,12 @@ namespace MediaBrowser.Common.Implementations.Archiving
{
using (var reader = ZipReader.Open(source))
{
- var options = ExtractOptions.ExtractFullPath;
+ var options = new ExtractionOptions();
+ options.ExtractFullPath = true;
if (overwriteExistingFiles)
{
- options = options | ExtractOptions.Overwrite;
+ options.Overwrite = true;
}
reader.WriteAllToDirectory(targetPath, options);
@@ -98,11 +99,12 @@ namespace MediaBrowser.Common.Implementations.Archiving
{
using (var reader = archive.ExtractAllEntries())
{
- var options = ExtractOptions.ExtractFullPath;
+ var options = new ExtractionOptions();
+ options.ExtractFullPath = true;
if (overwriteExistingFiles)
{
- options = options | ExtractOptions.Overwrite;
+ options.Overwrite = true;
}
reader.WriteAllToDirectory(targetPath, options);
@@ -137,11 +139,12 @@ namespace MediaBrowser.Common.Implementations.Archiving
{
using (var reader = archive.ExtractAllEntries())
{
- var options = ExtractOptions.ExtractFullPath;
+ var options = new ExtractionOptions();
+ options.ExtractFullPath = true;
if (overwriteExistingFiles)
{
- options = options | ExtractOptions.Overwrite;
+ options.Overwrite = true;
}
reader.WriteAllToDirectory(targetPath, options);
@@ -175,11 +178,12 @@ namespace MediaBrowser.Common.Implementations.Archiving
{
using (var reader = archive.ExtractAllEntries())
{
- var options = ExtractOptions.ExtractFullPath;
+ var options = new ExtractionOptions();
+ options.ExtractFullPath = true;
if (overwriteExistingFiles)
{
- options = options | ExtractOptions.Overwrite;
+ options.Overwrite = true;
}
reader.WriteAllToDirectory(targetPath, options);
diff --git a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs b/Emby.Common.Implementations/BaseApplicationHost.cs
index e68fee829d..02d7cb31fd 100644
--- a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs
+++ b/Emby.Common.Implementations/BaseApplicationHost.cs
@@ -1,16 +1,12 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events;
-using MediaBrowser.Common.Implementations.Archiving;
-using MediaBrowser.Common.Implementations.Devices;
-using MediaBrowser.Common.Implementations.IO;
-using MediaBrowser.Common.Implementations.ScheduledTasks;
-using MediaBrowser.Common.Implementations.Security;
-using MediaBrowser.Common.Implementations.Serialization;
-using MediaBrowser.Common.Implementations.Updates;
+using Emby.Common.Implementations.Devices;
+using Emby.Common.Implementations.IO;
+using Emby.Common.Implementations.ScheduledTasks;
+using Emby.Common.Implementations.Serialization;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Progress;
-using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Common.Security;
using MediaBrowser.Common.Updates;
using MediaBrowser.Model.Events;
@@ -18,27 +14,42 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Updates;
-using ServiceStack;
-using SimpleInjector;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
+using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.Extensions;
+using Emby.Common.Implementations.Cryptography;
+using Emby.Common.Implementations.Diagnostics;
+using Emby.Common.Implementations.Net;
+using Emby.Common.Implementations.EnvironmentInfo;
+using Emby.Common.Implementations.Threading;
+using MediaBrowser.Common;
using MediaBrowser.Common.IO;
-
-namespace MediaBrowser.Common.Implementations
+using MediaBrowser.Model.Cryptography;
+using MediaBrowser.Model.Diagnostics;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.System;
+using MediaBrowser.Model.Tasks;
+using MediaBrowser.Model.Threading;
+
+#if NETSTANDARD1_6
+using System.Runtime.Loader;
+#endif
+
+namespace Emby.Common.Implementations
{
/// <summary>
/// Class BaseApplicationHost
/// </summary>
/// <typeparam name="TApplicationPathsType">The type of the T application paths type.</typeparam>
- public abstract class BaseApplicationHost<TApplicationPathsType> : IApplicationHost, IDependencyContainer
+ public abstract class BaseApplicationHost<TApplicationPathsType> : IApplicationHost
where TApplicationPathsType : class, IApplicationPaths
{
/// <summary>
@@ -67,7 +78,7 @@ namespace MediaBrowser.Common.Implementations
/// Gets or sets the plugins.
/// </summary>
/// <value>The plugins.</value>
- public IEnumerable<IPlugin> Plugins { get; protected set; }
+ public IPlugin[] Plugins { get; protected set; }
/// <summary>
/// Gets or sets the log manager.
@@ -82,11 +93,6 @@ namespace MediaBrowser.Common.Implementations
protected TApplicationPathsType ApplicationPaths { get; private set; }
/// <summary>
- /// The container
- /// </summary>
- protected readonly Container Container = new Container();
-
- /// <summary>
/// The json serializer
/// </summary>
public IJsonSerializer JsonSerializer { get; private set; }
@@ -125,11 +131,6 @@ namespace MediaBrowser.Common.Implementations
/// <value>The kernel.</value>
protected ITaskManager TaskManager { get; private set; }
/// <summary>
- /// Gets the security manager.
- /// </summary>
- /// <value>The security manager.</value>
- protected ISecurityManager SecurityManager { get; private set; }
- /// <summary>
/// Gets the HTTP client.
/// </summary>
/// <value>The HTTP client.</value>
@@ -146,22 +147,14 @@ namespace MediaBrowser.Common.Implementations
/// <value>The configuration manager.</value>
protected IConfigurationManager ConfigurationManager { get; private set; }
- /// <summary>
- /// Gets or sets the installation manager.
- /// </summary>
- /// <value>The installation manager.</value>
- protected IInstallationManager InstallationManager { get; private set; }
-
protected IFileSystem FileSystemManager { get; private set; }
- /// <summary>
- /// Gets or sets the zip client.
- /// </summary>
- /// <value>The zip client.</value>
- protected IZipClient ZipClient { get; private set; }
-
protected IIsoManager IsoManager { get; private set; }
+ protected IProcessFactory ProcessFactory { get; private set; }
+ protected ITimerFactory TimerFactory { get; private set; }
+ protected ISocketFactory SocketFactory { get; private set; }
+
/// <summary>
/// Gets the name.
/// </summary>
@@ -174,6 +167,10 @@ namespace MediaBrowser.Common.Implementations
/// <value><c>true</c> if this instance is running as service; otherwise, <c>false</c>.</value>
public abstract bool IsRunningAsService { get; }
+ protected ICryptoProvider CryptographyProvider = new CryptographyProvider();
+
+ protected IEnvironmentInfo EnvironmentInfo { get; private set; }
+
private DeviceId _deviceId;
public string SystemId
{
@@ -183,26 +180,44 @@ namespace MediaBrowser.Common.Implementations
{
_deviceId = new DeviceId(ApplicationPaths, LogManager.GetLogger("SystemId"), FileSystemManager);
}
-
+
return _deviceId.Value;
}
}
public virtual string OperatingSystemDisplayName
{
- get { return Environment.OSVersion.VersionString; }
+ get { return EnvironmentInfo.OperatingSystemName; }
}
- public IMemoryStreamProvider MemoryStreamProvider { get; set; }
+ /// <summary>
+ /// The container
+ /// </summary>
+ protected readonly SimpleInjector.Container Container = new SimpleInjector.Container();
+
+ protected ISystemEvents SystemEvents { get; private set; }
+ protected IMemoryStreamFactory MemoryStreamFactory { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="BaseApplicationHost{TApplicationPathsType}"/> class.
/// </summary>
- protected BaseApplicationHost(TApplicationPathsType applicationPaths,
- ILogManager logManager,
- IFileSystem fileSystem)
- {
- XmlSerializer = new XmlSerializer (fileSystem, logManager.GetLogger("XmlSerializer"));
+ protected BaseApplicationHost(TApplicationPathsType applicationPaths,
+ ILogManager logManager,
+ IFileSystem fileSystem,
+ IEnvironmentInfo environmentInfo,
+ ISystemEvents systemEvents,
+ IMemoryStreamFactory memoryStreamFactory,
+ INetworkManager networkManager)
+ {
+ NetworkManager = networkManager;
+ EnvironmentInfo = environmentInfo;
+ SystemEvents = systemEvents;
+ MemoryStreamFactory = memoryStreamFactory;
+
+ // hack alert, until common can target .net core
+ BaseExtensions.CryptographyProvider = CryptographyProvider;
+
+ XmlSerializer = new MyXmlSerializer(fileSystem, logManager.GetLogger("XmlSerializer"));
FailedAssemblies = new List<string>();
ApplicationPaths = applicationPaths;
@@ -221,28 +236,10 @@ namespace MediaBrowser.Common.Implementations
/// <returns>Task.</returns>
public virtual async Task Init(IProgress<double> progress)
{
- try
- {
- // https://github.com/ServiceStack/ServiceStack/blob/master/tests/ServiceStack.WebHost.IntegrationTests/Web.config#L4
- Licensing.RegisterLicense("1001-e1JlZjoxMDAxLE5hbWU6VGVzdCBCdXNpbmVzcyxUeXBlOkJ1c2luZXNzLEhhc2g6UHVNTVRPclhvT2ZIbjQ5MG5LZE1mUTd5RUMzQnBucTFEbTE3TDczVEF4QUNMT1FhNXJMOWkzVjFGL2ZkVTE3Q2pDNENqTkQyUktRWmhvUVBhYTBiekJGUUZ3ZE5aZHFDYm9hL3lydGlwUHI5K1JsaTBYbzNsUC85cjVJNHE5QVhldDN6QkE4aTlvdldrdTgyTk1relY2eis2dFFqTThYN2lmc0JveHgycFdjPSxFeHBpcnk6MjAxMy0wMS0wMX0=");
- }
- catch
- {
- // Failing under mono
- }
progress.Report(1);
JsonSerializer = CreateJsonSerializer();
- if (Environment.OSVersion.Platform == PlatformID.Win32NT)
- {
- MemoryStreamProvider = new RecyclableMemoryStreamProvider();
- }
- else
- {
- MemoryStreamProvider = new MemoryStreamProvider();
- }
-
OnLoggerLoaded(true);
LogManager.LoggerLoaded += (s, e) => OnLoggerLoaded(false);
@@ -310,11 +307,10 @@ namespace MediaBrowser.Common.Implementations
builder.AppendLine(string.Format("Command line: {0}", string.Join(" ", Environment.GetCommandLineArgs())));
+#if NET46
builder.AppendLine(string.Format("Operating system: {0}", Environment.OSVersion));
- builder.AppendLine(string.Format("Processor count: {0}", Environment.ProcessorCount));
builder.AppendLine(string.Format("64-Bit OS: {0}", Environment.Is64BitOperatingSystem));
builder.AppendLine(string.Format("64-Bit Process: {0}", Environment.Is64BitProcess));
- builder.AppendLine(string.Format("Program data path: {0}", appPaths.ProgramDataPath));
Type type = Type.GetType("Mono.Runtime");
if (type != null)
@@ -325,23 +321,25 @@ namespace MediaBrowser.Common.Implementations
builder.AppendLine("Mono: " + displayName.Invoke(null, null));
}
}
+#endif
- builder.AppendLine(string.Format("Application Path: {0}", appPaths.ApplicationPath));
+ builder.AppendLine(string.Format("Processor count: {0}", Environment.ProcessorCount));
+ builder.AppendLine(string.Format("Program data path: {0}", appPaths.ProgramDataPath));
+ builder.AppendLine(string.Format("Application directory: {0}", appPaths.ProgramSystemPath));
return builder;
}
- protected virtual IJsonSerializer CreateJsonSerializer()
- {
- return new JsonSerializer(FileSystemManager, LogManager.GetLogger("JsonSerializer"));
- }
+ protected abstract IJsonSerializer CreateJsonSerializer();
private void SetHttpLimit()
{
try
{
// Increase the max http request limit
+#if NET46
ServicePointManager.DefaultConnectionLimit = Math.Max(96, ServicePointManager.DefaultConnectionLimit);
+#endif
}
catch (Exception ex)
{
@@ -386,13 +384,13 @@ namespace MediaBrowser.Common.Implementations
/// <returns>Task.</returns>
public virtual Task RunStartupTasks()
{
- Resolve<ITaskManager>().AddTasks(GetExports<IScheduledTask>(false));
+ Resolve<ITaskManager>().AddTasks(GetExports<IScheduledTask>(false));
- ConfigureAutorun ();
+ ConfigureAutorun();
- ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
+ ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
- return Task.FromResult (true);
+ return Task.FromResult(true);
}
/// <summary>
@@ -427,10 +425,56 @@ namespace MediaBrowser.Common.Implementations
/// </summary>
protected virtual void FindParts()
{
- RegisterModules();
-
ConfigurationManager.AddParts(GetExports<IConfigurationFactory>());
- Plugins = GetExports<IPlugin>();
+ Plugins = GetExports<IPlugin>().Select(LoadPlugin).Where(i => i != null).ToArray();
+ }
+
+ private IPlugin LoadPlugin(IPlugin plugin)
+ {
+ try
+ {
+ var assemblyPlugin = plugin as IPluginAssembly;
+
+ if (assemblyPlugin != null)
+ {
+#if NET46
+ var assembly = plugin.GetType().Assembly;
+ var assemblyName = assembly.GetName();
+
+ var attribute = (GuidAttribute)assembly.GetCustomAttributes(typeof(GuidAttribute), true)[0];
+ var assemblyId = new Guid(attribute.Value);
+
+ var assemblyFileName = assemblyName.Name + ".dll";
+ var assemblyFilePath = Path.Combine(ApplicationPaths.PluginsPath, assemblyFileName);
+
+ assemblyPlugin.SetAttributes(assemblyFilePath, assemblyFileName, assemblyName.Version, assemblyId);
+#elif NETSTANDARD1_6
+ var typeInfo = plugin.GetType().GetTypeInfo();
+ var assembly = typeInfo.Assembly;
+ var assemblyName = assembly.GetName();
+
+ var attribute = (GuidAttribute)assembly.GetCustomAttribute(typeof(GuidAttribute));
+ var assemblyId = new Guid(attribute.Value);
+
+ var assemblyFileName = assemblyName.Name + ".dll";
+ var assemblyFilePath = Path.Combine(ApplicationPaths.PluginsPath, assemblyFileName);
+
+ assemblyPlugin.SetAttributes(assemblyFilePath, assemblyFileName, assemblyName.Version, assemblyId);
+#else
+return null;
+#endif
+ }
+
+ var isFirstRun = !File.Exists(plugin.ConfigurationFilePath);
+ plugin.SetStartupInfo(isFirstRun, File.GetLastWriteTimeUtc, s => Directory.CreateDirectory(s));
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error loading plugin {0}", ex, plugin.GetType().FullName);
+ return null;
+ }
+
+ return plugin;
}
/// <summary>
@@ -449,7 +493,17 @@ namespace MediaBrowser.Common.Implementations
AllConcreteTypes = assemblies
.SelectMany(GetTypes)
- .Where(t => t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType)
+ .Where(t =>
+ {
+#if NET46
+ return t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType;
+#endif
+#if NETSTANDARD1_6
+ var typeInfo = t.GetTypeInfo();
+ return typeInfo.IsClass && !typeInfo.IsAbstract && !typeInfo.IsInterface && !typeInfo.IsGenericType;
+#endif
+ return false;
+ })
.ToArray();
}
@@ -459,62 +513,46 @@ namespace MediaBrowser.Common.Implementations
/// <returns>Task.</returns>
protected virtual Task RegisterResources(IProgress<double> progress)
{
- RegisterSingleInstance(ConfigurationManager);
- RegisterSingleInstance<IApplicationHost>(this);
+ RegisterSingleInstance(ConfigurationManager);
+ RegisterSingleInstance<IApplicationHost>(this);
- RegisterSingleInstance<IApplicationPaths>(ApplicationPaths);
+ RegisterSingleInstance<IApplicationPaths>(ApplicationPaths);
- TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, LogManager.GetLogger("TaskManager"), FileSystemManager);
+ TaskManager = new TaskManager(ApplicationPaths, JsonSerializer, LogManager.GetLogger("TaskManager"), FileSystemManager, SystemEvents);
- RegisterSingleInstance(JsonSerializer);
- RegisterSingleInstance(XmlSerializer);
- RegisterSingleInstance(MemoryStreamProvider);
+ RegisterSingleInstance(JsonSerializer);
+ RegisterSingleInstance(XmlSerializer);
+ RegisterSingleInstance(MemoryStreamFactory);
+ RegisterSingleInstance(SystemEvents);
- RegisterSingleInstance(LogManager);
- RegisterSingleInstance(Logger);
+ RegisterSingleInstance(LogManager);
+ RegisterSingleInstance(Logger);
- RegisterSingleInstance(TaskManager);
+ RegisterSingleInstance(TaskManager);
+ RegisterSingleInstance(EnvironmentInfo);
- RegisterSingleInstance(FileSystemManager);
+ RegisterSingleInstance(FileSystemManager);
- HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager, MemoryStreamProvider);
- RegisterSingleInstance(HttpClient);
+ HttpClient = new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager, MemoryStreamFactory);
+ RegisterSingleInstance(HttpClient);
- NetworkManager = CreateNetworkManager(LogManager.GetLogger("NetworkManager"));
- RegisterSingleInstance(NetworkManager);
+ RegisterSingleInstance(NetworkManager);
- SecurityManager = new PluginSecurityManager(this, HttpClient, JsonSerializer, ApplicationPaths, LogManager);
- RegisterSingleInstance(SecurityManager);
+ IsoManager = new IsoManager();
+ RegisterSingleInstance(IsoManager);
- InstallationManager = new InstallationManager(LogManager.GetLogger("InstallationManager"), this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ConfigurationManager, FileSystemManager);
- RegisterSingleInstance(InstallationManager);
+ ProcessFactory = new ProcessFactory();
+ RegisterSingleInstance(ProcessFactory);
- ZipClient = new ZipClient(FileSystemManager);
- RegisterSingleInstance(ZipClient);
+ TimerFactory = new TimerFactory();
+ RegisterSingleInstance(TimerFactory);
- IsoManager = new IsoManager();
- RegisterSingleInstance(IsoManager);
+ SocketFactory = new SocketFactory(LogManager.GetLogger("SocketFactory"));
+ RegisterSingleInstance(SocketFactory);
- return Task.FromResult (true);
- }
+ RegisterSingleInstance(CryptographyProvider);
- 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);
- }
- }
+ return Task.FromResult(true);
}
/// <summary>
@@ -544,14 +582,12 @@ namespace MediaBrowser.Common.Implementations
Logger.Error("LoaderException: " + loaderException.Message);
}
}
-
+
// If it fails we can still get a list of the Types it was able to resolve
return ex.Types.Where(t => t != null);
}
}
- protected abstract INetworkManager CreateNetworkManager(ILogger logger);
-
/// <summary>
/// Creates an instance of type and resolves all constructor dependancies
/// </summary>
@@ -565,7 +601,7 @@ namespace MediaBrowser.Common.Implementations
}
catch (Exception ex)
{
- Logger.ErrorException("Error creating {0}", ex, type.Name);
+ Logger.ErrorException("Error creating {0}", ex, type.FullName);
throw;
}
@@ -584,17 +620,12 @@ namespace MediaBrowser.Common.Implementations
}
catch (Exception ex)
{
- Logger.ErrorException("Error creating {0}", ex, type.Name);
+ Logger.ErrorException("Error creating {0}", ex, type.FullName);
// Don't blow up in release mode
return null;
}
}
- void IDependencyContainer.RegisterSingleInstance<T>(T obj, bool manageLifetime)
- {
- RegisterSingleInstance(obj, manageLifetime);
- }
-
/// <summary>
/// Registers the specified obj.
/// </summary>
@@ -617,11 +648,6 @@ namespace MediaBrowser.Common.Implementations
}
}
- void IDependencyContainer.RegisterSingleInstance<T>(Func<T> func)
- {
- RegisterSingleInstance(func);
- }
-
/// <summary>
/// Registers the single instance.
/// </summary>
@@ -633,11 +659,6 @@ namespace MediaBrowser.Common.Implementations
Container.RegisterSingleton(func);
}
- void IDependencyContainer.Register(Type typeInterface, Type typeImplementation)
- {
- Container.Register(typeInterface, typeImplementation);
- }
-
/// <summary>
/// Resolves this instance.
/// </summary>
@@ -673,7 +694,13 @@ namespace MediaBrowser.Common.Implementations
{
try
{
+#if NET46
return Assembly.Load(File.ReadAllBytes(file));
+#elif NETSTANDARD1_6
+
+ return AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(File.ReadAllBytes(file)));
+#endif
+ return null;
}
catch (Exception ex)
{
@@ -692,7 +719,14 @@ namespace MediaBrowser.Common.Implementations
{
var currentType = typeof(T);
- return AllConcreteTypes.AsParallel().Where(currentType.IsAssignableFrom);
+#if NET46
+ return AllConcreteTypes.Where(currentType.IsAssignableFrom);
+#elif NETSTANDARD1_6
+ var currentTypeInfo = currentType.GetTypeInfo();
+
+ return AllConcreteTypes.Where(currentTypeInfo.IsAssignableFrom);
+#endif
+ return new List<Type>();
}
/// <summary>
@@ -747,7 +781,7 @@ namespace MediaBrowser.Common.Implementations
{
var list = Plugins.ToList();
list.Remove(plugin);
- Plugins = list;
+ Plugins = list.ToArray();
}
/// <summary>
diff --git a/MediaBrowser.Common.Implementations/BaseApplicationPaths.cs b/Emby.Common.Implementations/BaseApplicationPaths.cs
index 9ba2effd3a..8792778ba6 100644
--- a/MediaBrowser.Common.Implementations/BaseApplicationPaths.cs
+++ b/Emby.Common.Implementations/BaseApplicationPaths.cs
@@ -1,7 +1,7 @@
-using MediaBrowser.Common.Configuration;
-using System.IO;
+using System.IO;
+using MediaBrowser.Common.Configuration;
-namespace MediaBrowser.Common.Implementations
+namespace Emby.Common.Implementations
{
/// <summary>
/// Provides a base class to hold common application paths used by both the Ui and Server.
@@ -12,22 +12,18 @@ namespace MediaBrowser.Common.Implementations
/// <summary>
/// Initializes a new instance of the <see cref="BaseApplicationPaths"/> class.
/// </summary>
- protected BaseApplicationPaths(string programDataPath, string applicationPath)
+ protected BaseApplicationPaths(string programDataPath, string appFolderPath)
{
ProgramDataPath = programDataPath;
- ApplicationPath = applicationPath;
+ ProgramSystemPath = appFolderPath;
}
- public string ApplicationPath { get; private set; }
public string ProgramDataPath { get; private set; }
/// <summary>
/// Gets the path to the system folder
/// </summary>
- public string ProgramSystemPath
- {
- get { return Path.GetDirectoryName(ApplicationPath); }
- }
+ public string ProgramSystemPath { get; private set; }
/// <summary>
/// The _data directory
diff --git a/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs b/Emby.Common.Implementations/Configuration/BaseConfigurationManager.cs
index fa15023ca5..27c9fe6157 100644
--- a/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs
+++ b/Emby.Common.Implementations/Configuration/BaseConfigurationManager.cs
@@ -1,18 +1,19 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Events;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Serialization;
-using System;
+using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
+using Emby.Common.Implementations;
+using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Serialization;
-namespace MediaBrowser.Common.Implementations.Configuration
+namespace Emby.Common.Implementations.Configuration
{
/// <summary>
/// Class BaseConfigurationManager
@@ -79,7 +80,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
get
{
// Lazy load
- LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationLoaded, ref _configurationSyncLock, () => (BaseApplicationConfiguration)ConfigurationHelper.GetXmlConfiguration(ConfigurationType, CommonApplicationPaths.SystemConfigurationFilePath, XmlSerializer));
+ LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationLoaded, ref _configurationSyncLock, () => (BaseApplicationConfiguration)ConfigurationHelper.GetXmlConfiguration(ConfigurationType, CommonApplicationPaths.SystemConfigurationFilePath, XmlSerializer, FileSystem));
return _configuration;
}
protected set
@@ -126,7 +127,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
Logger.Info("Saving system configuration");
var path = CommonApplicationPaths.SystemConfigurationFilePath;
- Directory.CreateDirectory(Path.GetDirectoryName(path));
+ FileSystem.CreateDirectory(Path.GetDirectoryName(path));
lock (_configurationSyncLock)
{
@@ -196,9 +197,9 @@ namespace MediaBrowser.Common.Implementations.Configuration
&& !string.Equals(CommonConfiguration.CachePath ?? string.Empty, newPath))
{
// Validate
- if (!Directory.Exists(newPath))
+ if (!FileSystem.DirectoryExists(newPath))
{
- throw new DirectoryNotFoundException(string.Format("{0} does not exist.", newPath));
+ throw new FileNotFoundException(string.Format("{0} does not exist.", newPath));
}
EnsureWriteAccess(newPath);
@@ -253,7 +254,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
{
return Activator.CreateInstance(configurationType);
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
return Activator.CreateInstance(configurationType);
}
@@ -293,7 +294,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
_configurations.AddOrUpdate(key, configuration, (k, v) => configuration);
var path = GetConfigurationFile(key);
- Directory.CreateDirectory(Path.GetDirectoryName(path));
+ FileSystem.CreateDirectory(Path.GetDirectoryName(path));
lock (_configurationSyncLock)
{
diff --git a/MediaBrowser.Common.Implementations/Configuration/ConfigurationHelper.cs b/Emby.Common.Implementations/Configuration/ConfigurationHelper.cs
index 419b85fa73..0d43a651ea 100644
--- a/MediaBrowser.Common.Implementations/Configuration/ConfigurationHelper.cs
+++ b/Emby.Common.Implementations/Configuration/ConfigurationHelper.cs
@@ -1,9 +1,10 @@
-using MediaBrowser.Model.Serialization;
-using System;
+using System;
using System.IO;
using System.Linq;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Serialization;
-namespace MediaBrowser.Common.Implementations.Configuration
+namespace Emby.Common.Implementations.Configuration
{
/// <summary>
/// Class ConfigurationHelper
@@ -18,7 +19,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
/// <param name="path">The path.</param>
/// <param name="xmlSerializer">The XML serializer.</param>
/// <returns>System.Object.</returns>
- public static object GetXmlConfiguration(Type type, string path, IXmlSerializer xmlSerializer)
+ public static object GetXmlConfiguration(Type type, string path, IXmlSerializer xmlSerializer, IFileSystem fileSystem)
{
object configuration;
@@ -27,7 +28,7 @@ namespace MediaBrowser.Common.Implementations.Configuration
// Use try/catch to avoid the extra file system lookup using File.Exists
try
{
- buffer = File.ReadAllBytes(path);
+ buffer = fileSystem.ReadAllBytes(path);
configuration = xmlSerializer.DeserializeFromBytes(type, buffer);
}
@@ -46,10 +47,10 @@ namespace MediaBrowser.Common.Implementations.Configuration
// If the file didn't exist before, or if something has changed, re-save
if (buffer == null || !buffer.SequenceEqual(newBytes))
{
- Directory.CreateDirectory(Path.GetDirectoryName(path));
+ fileSystem.CreateDirectory(Path.GetDirectoryName(path));
// Save it after load in case we got new items
- File.WriteAllBytes(path, newBytes);
+ fileSystem.WriteAllBytes(path, newBytes);
}
return configuration;
diff --git a/Emby.Common.Implementations/Cryptography/CryptographyProvider.cs b/Emby.Common.Implementations/Cryptography/CryptographyProvider.cs
new file mode 100644
index 0000000000..01a31bcc03
--- /dev/null
+++ b/Emby.Common.Implementations/Cryptography/CryptographyProvider.cs
@@ -0,0 +1,40 @@
+using System;
+using System.IO;
+using System.Security.Cryptography;
+using System.Text;
+using MediaBrowser.Model.Cryptography;
+
+namespace Emby.Common.Implementations.Cryptography
+{
+ public class CryptographyProvider : ICryptoProvider
+ {
+ public Guid GetMD5(string str)
+ {
+ return new Guid(ComputeMD5(Encoding.Unicode.GetBytes(str)));
+ }
+
+ public byte[] ComputeSHA1(byte[] bytes)
+ {
+ using (var provider = SHA1.Create())
+ {
+ return provider.ComputeHash(bytes);
+ }
+ }
+
+ public byte[] ComputeMD5(Stream str)
+ {
+ using (var provider = MD5.Create())
+ {
+ return provider.ComputeHash(str);
+ }
+ }
+
+ public byte[] ComputeMD5(byte[] bytes)
+ {
+ using (var provider = MD5.Create())
+ {
+ return provider.ComputeHash(bytes);
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Common.Implementations/Devices/DeviceId.cs b/Emby.Common.Implementations/Devices/DeviceId.cs
index f1581704b8..3d23ab872b 100644
--- a/MediaBrowser.Common.Implementations/Devices/DeviceId.cs
+++ b/Emby.Common.Implementations/Devices/DeviceId.cs
@@ -1,11 +1,11 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.Logging;
-using System;
+using System;
using System.IO;
using System.Text;
-using CommonIO;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
-namespace MediaBrowser.Common.Implementations.Devices
+namespace Emby.Common.Implementations.Devices
{
public class DeviceId
{
diff --git a/Emby.Common.Implementations/Diagnostics/CommonProcess.cs b/Emby.Common.Implementations/Diagnostics/CommonProcess.cs
new file mode 100644
index 0000000000..462345ced5
--- /dev/null
+++ b/Emby.Common.Implementations/Diagnostics/CommonProcess.cs
@@ -0,0 +1,108 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Diagnostics;
+
+namespace Emby.Common.Implementations.Diagnostics
+{
+ public class CommonProcess : IProcess
+ {
+ public event EventHandler Exited;
+
+ private readonly ProcessOptions _options;
+ private readonly Process _process;
+
+ public CommonProcess(ProcessOptions options)
+ {
+ _options = options;
+
+ var startInfo = new ProcessStartInfo
+ {
+ Arguments = options.Arguments,
+ FileName = options.FileName,
+ WorkingDirectory = options.WorkingDirectory,
+ UseShellExecute = options.UseShellExecute,
+ CreateNoWindow = options.CreateNoWindow,
+ RedirectStandardError = options.RedirectStandardError,
+ RedirectStandardInput = options.RedirectStandardInput,
+ RedirectStandardOutput = options.RedirectStandardOutput
+ };
+
+#if NET46
+ startInfo.ErrorDialog = options.ErrorDialog;
+
+ if (options.IsHidden)
+ {
+ startInfo.WindowStyle = ProcessWindowStyle.Hidden;
+ }
+#endif
+
+ _process = new Process
+ {
+ StartInfo = startInfo
+ };
+
+ if (options.EnableRaisingEvents)
+ {
+ _process.EnableRaisingEvents = true;
+ _process.Exited += _process_Exited;
+ }
+ }
+
+ private void _process_Exited(object sender, EventArgs e)
+ {
+ if (Exited != null)
+ {
+ Exited(this, e);
+ }
+ }
+
+ public ProcessOptions StartInfo
+ {
+ get { return _options; }
+ }
+
+ public StreamWriter StandardInput
+ {
+ get { return _process.StandardInput; }
+ }
+
+ public StreamReader StandardError
+ {
+ get { return _process.StandardError; }
+ }
+
+ public StreamReader StandardOutput
+ {
+ get { return _process.StandardOutput; }
+ }
+
+ public int ExitCode
+ {
+ get { return _process.ExitCode; }
+ }
+
+ public void Start()
+ {
+ _process.Start();
+ }
+
+ public void Kill()
+ {
+ _process.Kill();
+ }
+
+ public bool WaitForExit(int timeMs)
+ {
+ return _process.WaitForExit(timeMs);
+ }
+
+ public void Dispose()
+ {
+ _process.Dispose();
+ }
+ }
+}
diff --git a/Emby.Common.Implementations/Diagnostics/ProcessFactory.cs b/Emby.Common.Implementations/Diagnostics/ProcessFactory.cs
new file mode 100644
index 0000000000..292da023c7
--- /dev/null
+++ b/Emby.Common.Implementations/Diagnostics/ProcessFactory.cs
@@ -0,0 +1,12 @@
+using MediaBrowser.Model.Diagnostics;
+
+namespace Emby.Common.Implementations.Diagnostics
+{
+ public class ProcessFactory : IProcessFactory
+ {
+ public IProcess Create(ProcessOptions options)
+ {
+ return new CommonProcess(options);
+ }
+ }
+}
diff --git a/Emby.Common.Implementations/Emby.Common.Implementations.xproj b/Emby.Common.Implementations/Emby.Common.Implementations.xproj
new file mode 100644
index 0000000000..5bb6e4e589
--- /dev/null
+++ b/Emby.Common.Implementations/Emby.Common.Implementations.xproj
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
+ <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+ </PropertyGroup>
+ <Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>5a27010a-09c6-4e86-93ea-437484c10917</ProjectGuid>
+ <RootNamespace>Emby.Common.Implementations</RootNamespace>
+ <BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
+ <OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
+ <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
+ </PropertyGroup>
+ <PropertyGroup>
+ <SchemaVersion>2.0</SchemaVersion>
+ </PropertyGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
+ </ItemGroup>
+ <Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
+</Project> \ No newline at end of file
diff --git a/Emby.Common.Implementations/EnvironmentInfo/EnvironmentInfo.cs b/Emby.Common.Implementations/EnvironmentInfo/EnvironmentInfo.cs
new file mode 100644
index 0000000000..6cc4626eaf
--- /dev/null
+++ b/Emby.Common.Implementations/EnvironmentInfo/EnvironmentInfo.cs
@@ -0,0 +1,119 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+using MediaBrowser.Model.System;
+
+namespace Emby.Common.Implementations.EnvironmentInfo
+{
+ public class EnvironmentInfo : IEnvironmentInfo
+ {
+ public MediaBrowser.Model.System.Architecture? CustomArchitecture { get; set; }
+
+ public MediaBrowser.Model.System.OperatingSystem OperatingSystem
+ {
+ get
+ {
+#if NET46
+ switch (Environment.OSVersion.Platform)
+ {
+ case PlatformID.MacOSX:
+ return MediaBrowser.Model.System.OperatingSystem.OSX;
+ case PlatformID.Win32NT:
+ return MediaBrowser.Model.System.OperatingSystem.Windows;
+ case PlatformID.Unix:
+ return MediaBrowser.Model.System.OperatingSystem.Linux;
+ }
+#elif NETSTANDARD1_6
+ if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ return OperatingSystem.OSX;
+ }
+ if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return OperatingSystem.Windows;
+ }
+ if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ return OperatingSystem.Linux;
+ }
+#endif
+ return MediaBrowser.Model.System.OperatingSystem.Windows;
+ }
+ }
+
+ public string OperatingSystemName
+ {
+ get
+ {
+#if NET46
+ return Environment.OSVersion.Platform.ToString();
+#elif NETSTANDARD1_6
+ return System.Runtime.InteropServices.RuntimeInformation.OSDescription;
+#endif
+ return "Operating System";
+ }
+ }
+
+ public string OperatingSystemVersion
+ {
+ get
+ {
+#if NET46
+ return Environment.OSVersion.Version.ToString() + " " + Environment.OSVersion.ServicePack.ToString();
+#elif NETSTANDARD1_6
+ return System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription;
+#endif
+ return "1.0";
+ }
+ }
+
+ public MediaBrowser.Model.System.Architecture SystemArchitecture
+ {
+ get
+ {
+ if (CustomArchitecture.HasValue)
+ {
+ return CustomArchitecture.Value;
+ }
+#if NET46
+ return Environment.Is64BitOperatingSystem ? MediaBrowser.Model.System.Architecture.X64 : MediaBrowser.Model.System.Architecture.X86;
+#elif NETSTANDARD1_6
+ switch(System.Runtime.InteropServices.RuntimeInformation.OSArchitecture)
+ {
+ case System.Runtime.InteropServices.Architecture.Arm:
+ return MediaBrowser.Model.System.Architecture.Arm;
+ case System.Runtime.InteropServices.Architecture.Arm64:
+ return MediaBrowser.Model.System.Architecture.Arm64;
+ case System.Runtime.InteropServices.Architecture.X64:
+ return MediaBrowser.Model.System.Architecture.X64;
+ case System.Runtime.InteropServices.Architecture.X86:
+ return MediaBrowser.Model.System.Architecture.X86;
+ }
+#endif
+ return MediaBrowser.Model.System.Architecture.X64;
+ }
+ }
+
+ public string GetEnvironmentVariable(string name)
+ {
+ return Environment.GetEnvironmentVariable(name);
+ }
+
+ public virtual string GetUserId()
+ {
+ return null;
+ }
+
+ public string StackTrace
+ {
+ get { return Environment.StackTrace; }
+ }
+
+ public void SetProcessEnvironmentVariable(string name, string value)
+ {
+ Environment.SetEnvironmentVariable(name, value);
+ }
+ }
+}
diff --git a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientInfo.cs b/Emby.Common.Implementations/HttpClientManager/HttpClientInfo.cs
index 8af6ef6c6e..ca481b33e7 100644
--- a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientInfo.cs
+++ b/Emby.Common.Implementations/HttpClientManager/HttpClientInfo.cs
@@ -1,6 +1,6 @@
using System;
-namespace MediaBrowser.Common.Implementations.HttpClientManager
+namespace Emby.Common.Implementations.HttpClientManager
{
/// <summary>
/// Class HttpClientInfo
diff --git a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Common.Implementations/HttpClientManager/HttpClientManager.cs
index eec18e9857..06af5af536 100644
--- a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
+++ b/Emby.Common.Implementations/HttpClientManager/HttpClientManager.cs
@@ -13,13 +13,13 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
-using System.Net.Cache;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using Emby.Common.Implementations.HttpClientManager;
+using MediaBrowser.Model.IO;
-namespace MediaBrowser.Common.Implementations.HttpClientManager
+namespace Emby.Common.Implementations.HttpClientManager
{
/// <summary>
/// Class HttpClientManager
@@ -42,7 +42,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
private readonly IApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem;
- private readonly IMemoryStreamProvider _memoryStreamProvider;
+ private readonly IMemoryStreamFactory _memoryStreamProvider;
/// <summary>
/// Initializes a new instance of the <see cref="HttpClientManager" /> class.
@@ -53,7 +53,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
/// <exception cref="System.ArgumentNullException">appPaths
/// or
/// logger</exception>
- public HttpClientManager(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem, IMemoryStreamProvider memoryStreamProvider)
+ public HttpClientManager(IApplicationPaths appPaths, ILogger logger, IFileSystem fileSystem, IMemoryStreamFactory memoryStreamProvider)
{
if (appPaths == null)
{
@@ -69,11 +69,13 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
_memoryStreamProvider = memoryStreamProvider;
_appPaths = appPaths;
+#if NET46
// http://stackoverflow.com/questions/566437/http-post-returns-the-error-417-expectation-failed-c
ServicePointManager.Expect100Continue = false;
// Trakt requests sometimes fail without this
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;
+#endif
}
/// <summary>
@@ -130,6 +132,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
private void AddIpv4Option(HttpWebRequest request, HttpRequestOptions options)
{
+#if NET46
request.ServicePoint.BindIPEndPointDelegate = (servicePount, remoteEndPoint, retryCount) =>
{
if (remoteEndPoint.AddressFamily == AddressFamily.InterNetwork)
@@ -138,6 +141,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
}
throw new InvalidOperationException("no IPv4 address");
};
+#endif
}
private WebRequest GetRequest(HttpRequestOptions options, string method)
@@ -164,34 +168,66 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
AddRequestHeaders(httpWebRequest, options);
- httpWebRequest.AutomaticDecompression = options.EnableHttpCompression ?
- (options.DecompressionMethod ?? DecompressionMethods.Deflate) :
- DecompressionMethods.None;
+#if NET46
+ if (options.EnableHttpCompression)
+ {
+ if (options.DecompressionMethod.HasValue)
+ {
+ httpWebRequest.AutomaticDecompression = options.DecompressionMethod.Value == CompressionMethod.Gzip
+ ? DecompressionMethods.GZip
+ : DecompressionMethods.Deflate;
+ }
+ else
+ {
+ httpWebRequest.AutomaticDecompression = DecompressionMethods.Deflate;
+ }
+ }
+ else
+ {
+ httpWebRequest.AutomaticDecompression = DecompressionMethods.None;
+ }
+#endif
}
- request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache);
+
+
+#if NET46
+ request.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.BypassCache);
+#endif
if (httpWebRequest != null)
{
if (options.EnableKeepAlive)
{
+#if NET46
httpWebRequest.KeepAlive = true;
+#endif
}
}
request.Method = method;
+#if NET46
request.Timeout = options.TimeoutMs;
-
+#endif
+
if (httpWebRequest != null)
{
if (!string.IsNullOrEmpty(options.Host))
{
+#if NET46
httpWebRequest.Host = options.Host;
+#elif NETSTANDARD1_6
+ httpWebRequest.Headers["Host"] = options.Host;
+#endif
}
if (!string.IsNullOrEmpty(options.Referer))
{
+#if NET46
httpWebRequest.Referer = options.Referer;
+#elif NETSTANDARD1_6
+ httpWebRequest.Headers["Referer"] = options.Referer;
+#endif
}
}
@@ -201,7 +237,10 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
if (parts.Length == 2)
{
request.Credentials = GetCredential(url, parts[0], parts[1]);
+ // TODO: .net core ??
+#if NET46
request.PreAuthenticate = true;
+#endif
}
}
@@ -226,11 +265,19 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
}
else if (string.Equals(header.Key, "User-Agent", StringComparison.OrdinalIgnoreCase))
{
+#if NET46
request.UserAgent = header.Value;
+#elif NETSTANDARD1_6
+ request.Headers["User-Agent"] = header.Value;
+#endif
}
else
{
+#if NET46
request.Headers.Set(header.Key, header.Value);
+#elif NETSTANDARD1_6
+ request.Headers[header.Key] = header.Value;
+#endif
}
}
}
@@ -330,7 +377,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
{
if (_fileSystem.GetLastWriteTimeUtc(responseCachePath).Add(cacheLength) > DateTime.UtcNow)
{
- using (var stream = _fileSystem.GetFileStream(responseCachePath, FileMode.Open, FileAccess.Read, FileShare.Read, true))
+ using (var stream = _fileSystem.GetFileStream(responseCachePath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true))
{
var memoryStream = _memoryStreamProvider.CreateNew();
@@ -342,7 +389,6 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
ResponseUrl = url,
Content = memoryStream,
StatusCode = HttpStatusCode.OK,
- Headers = new NameValueCollection(),
ContentLength = memoryStream.Length
};
}
@@ -370,7 +416,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
await responseStream.CopyToAsync(memoryStream).ConfigureAwait(false);
memoryStream.Position = 0;
- using (var fileStream = _fileSystem.GetFileStream(responseCachePath, FileMode.Create, FileAccess.Write, FileShare.None, true))
+ using (var fileStream = _fileSystem.GetFileStream(responseCachePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.None, true))
{
await memoryStream.CopyToAsync(fileStream).ConfigureAwait(false);
@@ -407,8 +453,10 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
httpWebRequest.ContentType = options.RequestContentType ?? "application/x-www-form-urlencoded";
+#if NET46
httpWebRequest.ContentLength = bytes.Length;
- httpWebRequest.GetRequestStream().Write(bytes, 0, bytes.Length);
+#endif
+ (await httpWebRequest.GetRequestStreamAsync().ConfigureAwait(false)).Write(bytes, 0, bytes.Length);
}
if (options.ResourcePool != null)
@@ -487,7 +535,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
private HttpResponseInfo GetResponseInfo(HttpWebResponse httpResponse, Stream content, long? contentLength, IDisposable disposable)
{
- return new HttpResponseInfo(disposable)
+ var responseInfo = new HttpResponseInfo(disposable)
{
Content = content,
@@ -495,17 +543,22 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
ContentType = httpResponse.ContentType,
- Headers = new NameValueCollection(httpResponse.Headers),
-
ContentLength = contentLength,
ResponseUrl = httpResponse.ResponseUri.ToString()
};
+
+ if (httpResponse.Headers != null)
+ {
+ SetHeaders(httpResponse.Headers, responseInfo);
+ }
+
+ return responseInfo;
}
private HttpResponseInfo GetResponseInfo(HttpWebResponse httpResponse, string tempFile, long? contentLength)
{
- return new HttpResponseInfo
+ var responseInfo = new HttpResponseInfo
{
TempFilePath = tempFile,
@@ -513,10 +566,23 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
ContentType = httpResponse.ContentType,
- Headers = httpResponse.Headers,
-
ContentLength = contentLength
};
+
+ if (httpResponse.Headers != null)
+ {
+ SetHeaders(httpResponse.Headers, responseInfo);
+ }
+
+ return responseInfo;
+ }
+
+ private void SetHeaders(WebHeaderCollection headers, HttpResponseInfo responseInfo)
+ {
+ foreach (var key in headers.AllKeys)
+ {
+ responseInfo.Headers[key] = headers[key];
+ }
}
public Task<HttpResponseInfo> Post(HttpRequestOptions options)
@@ -621,7 +687,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
// We're not able to track progress
using (var stream = httpResponse.GetResponseStream())
{
- using (var fs = _fileSystem.GetFileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read, true))
+ using (var fs = _fileSystem.GetFileStream(tempFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
{
await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false);
}
@@ -631,7 +697,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
{
using (var stream = ProgressStream.CreateReadProgressStream(httpResponse.GetResponseStream(), options.Progress.Report, contentLength.Value))
{
- using (var fs = _fileSystem.GetFileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read, true))
+ using (var fs = _fileSystem.GetFileStream(tempFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
{
await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false);
}
@@ -867,6 +933,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
private Task<WebResponse> GetResponseAsync(WebRequest request, TimeSpan timeout)
{
+#if NET46
var taskCompletion = new TaskCompletionSource<WebResponse>();
Task<WebResponse> asyncTask = Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
@@ -879,6 +946,9 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
asyncTask.ContinueWith(callback.OnError, TaskContinuationOptions.OnlyOnFaulted);
return taskCompletion.Task;
+#endif
+
+ return request.GetResponseAsync();
}
private static void TimeoutCallback(object state, bool timedOut)
diff --git a/MediaBrowser.Common.Implementations/IO/IsoManager.cs b/Emby.Common.Implementations/IO/IsoManager.cs
index de88ddadab..14614acaf8 100644
--- a/MediaBrowser.Common.Implementations/IO/IsoManager.cs
+++ b/Emby.Common.Implementations/IO/IsoManager.cs
@@ -1,11 +1,11 @@
-using MediaBrowser.Model.IO;
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.IO;
-namespace MediaBrowser.Common.Implementations.IO
+namespace Emby.Common.Implementations.IO
{
/// <summary>
/// Class IsoManager
diff --git a/Emby.Common.Implementations/IO/ManagedFileSystem.cs b/Emby.Common.Implementations/IO/ManagedFileSystem.cs
new file mode 100644
index 0000000000..62d285072c
--- /dev/null
+++ b/Emby.Common.Implementations/IO/ManagedFileSystem.cs
@@ -0,0 +1,794 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+
+namespace Emby.Common.Implementations.IO
+{
+ /// <summary>
+ /// Class ManagedFileSystem
+ /// </summary>
+ public class ManagedFileSystem : IFileSystem
+ {
+ protected ILogger Logger;
+
+ private readonly bool _supportsAsyncFileStreams;
+ private char[] _invalidFileNameChars;
+ private readonly List<IShortcutHandler> _shortcutHandlers = new List<IShortcutHandler>();
+ private bool EnableFileSystemRequestConcat = true;
+
+ public ManagedFileSystem(ILogger logger, bool supportsAsyncFileStreams, bool enableManagedInvalidFileNameChars, bool enableFileSystemRequestConcat)
+ {
+ Logger = logger;
+ _supportsAsyncFileStreams = supportsAsyncFileStreams;
+ EnableFileSystemRequestConcat = enableFileSystemRequestConcat;
+ SetInvalidFileNameChars(enableManagedInvalidFileNameChars);
+ }
+
+ public void AddShortcutHandler(IShortcutHandler handler)
+ {
+ _shortcutHandlers.Add(handler);
+ }
+
+ protected void SetInvalidFileNameChars(bool enableManagedInvalidFileNameChars)
+ {
+ if (enableManagedInvalidFileNameChars)
+ {
+ _invalidFileNameChars = Path.GetInvalidFileNameChars();
+ }
+ 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', ':', '*', '?', '\\', '/' };
+ }
+ }
+
+ public char DirectorySeparatorChar
+ {
+ get
+ {
+ return Path.DirectorySeparatorChar;
+ }
+ }
+
+ public char PathSeparator
+ {
+ get
+ {
+ return Path.PathSeparator;
+ }
+ }
+
+ public string GetFullPath(string path)
+ {
+ return Path.GetFullPath(path);
+ }
+
+ /// <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>
+ /// <exception cref="System.ArgumentNullException">filename</exception>
+ public virtual bool IsShortcut(string filename)
+ {
+ if (string.IsNullOrEmpty(filename))
+ {
+ throw new ArgumentNullException("filename");
+ }
+
+ var extension = Path.GetExtension(filename);
+ return _shortcutHandlers.Any(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgnoreCase));
+ }
+
+ /// <summary>
+ /// Resolves the shortcut.
+ /// </summary>
+ /// <param name="filename">The filename.</param>
+ /// <returns>System.String.</returns>
+ /// <exception cref="System.ArgumentNullException">filename</exception>
+ public virtual string ResolveShortcut(string filename)
+ {
+ if (string.IsNullOrEmpty(filename))
+ {
+ throw new ArgumentNullException("filename");
+ }
+
+ var extension = Path.GetExtension(filename);
+ var handler = _shortcutHandlers.FirstOrDefault(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgnoreCase));
+
+ if (handler != null)
+ {
+ return handler.Resolve(filename);
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Creates the shortcut.
+ /// </summary>
+ /// <param name="shortcutPath">The shortcut path.</param>
+ /// <param name="target">The target.</param>
+ /// <exception cref="System.ArgumentNullException">
+ /// shortcutPath
+ /// or
+ /// target
+ /// </exception>
+ public void CreateShortcut(string shortcutPath, string target)
+ {
+ if (string.IsNullOrEmpty(shortcutPath))
+ {
+ throw new ArgumentNullException("shortcutPath");
+ }
+
+ if (string.IsNullOrEmpty(target))
+ {
+ throw new ArgumentNullException("target");
+ }
+
+ var extension = Path.GetExtension(shortcutPath);
+ var handler = _shortcutHandlers.FirstOrDefault(i => string.Equals(extension, i.Extension, StringComparison.OrdinalIgnoreCase));
+
+ if (handler != null)
+ {
+ handler.Create(shortcutPath, target);
+ }
+ else
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ /// <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>
+ public FileSystemMetadata GetFileSystemInfo(string path)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ throw new ArgumentNullException("path");
+ }
+
+ // Take a guess to try and avoid two file system hits, but we'll double-check by calling Exists
+ if (Path.HasExtension(path))
+ {
+ var fileInfo = new FileInfo(path);
+
+ if (fileInfo.Exists)
+ {
+ return GetFileSystemMetadata(fileInfo);
+ }
+
+ return GetFileSystemMetadata(new DirectoryInfo(path));
+ }
+ else
+ {
+ var fileInfo = new DirectoryInfo(path);
+
+ if (fileInfo.Exists)
+ {
+ return GetFileSystemMetadata(fileInfo);
+ }
+
+ return GetFileSystemMetadata(new FileInfo(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>
+ public FileSystemMetadata GetFileInfo(string path)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ throw new ArgumentNullException("path");
+ }
+
+ var fileInfo = new FileInfo(path);
+
+ return GetFileSystemMetadata(fileInfo);
+ }
+
+ /// <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>
+ public FileSystemMetadata GetDirectoryInfo(string path)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ throw new ArgumentNullException("path");
+ }
+
+ var fileInfo = new DirectoryInfo(path);
+
+ return GetFileSystemMetadata(fileInfo);
+ }
+
+ private FileSystemMetadata GetFileSystemMetadata(FileSystemInfo info)
+ {
+ var result = new FileSystemMetadata();
+
+ result.Exists = info.Exists;
+ result.FullName = info.FullName;
+ result.Extension = info.Extension;
+ result.Name = info.Name;
+
+ 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;
+
+ var fileInfo = info as FileInfo;
+ if (fileInfo != null)
+ {
+ result.Length = fileInfo.Length;
+ result.DirectoryName = fileInfo.DirectoryName;
+ }
+
+ result.CreationTimeUtc = GetCreationTimeUtc(info);
+ result.LastWriteTimeUtc = GetLastWriteTimeUtc(info);
+ }
+ else
+ {
+ result.IsDirectory = info is DirectoryInfo;
+ }
+
+ return result;
+ }
+
+ /// <summary>
+ /// The space char
+ /// </summary>
+ private const char SpaceChar = ' ';
+
+ /// <summary>
+ /// Takes a filename and removes invalid characters
+ /// </summary>
+ /// <param name="filename">The filename.</param>
+ /// <returns>System.String.</returns>
+ /// <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)
+ {
+ builder = builder.Replace(c, SpaceChar);
+ }
+
+ return builder.ToString();
+ }
+
+ /// <summary>
+ /// Gets the creation time UTC.
+ /// </summary>
+ /// <param name="info">The info.</param>
+ /// <returns>DateTime.</returns>
+ public DateTime GetCreationTimeUtc(FileSystemInfo info)
+ {
+ // This could throw an error on some file systems that have dates out of range
+ try
+ {
+ return info.CreationTimeUtc;
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error determining CreationTimeUtc for {0}", ex, info.FullName);
+ return DateTime.MinValue;
+ }
+ }
+
+ /// <summary>
+ /// Gets the creation time UTC.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ /// <returns>DateTime.</returns>
+ public DateTime GetCreationTimeUtc(string path)
+ {
+ return GetCreationTimeUtc(GetFileSystemInfo(path));
+ }
+
+ public DateTime GetCreationTimeUtc(FileSystemMetadata info)
+ {
+ return info.CreationTimeUtc;
+ }
+
+ public DateTime GetLastWriteTimeUtc(FileSystemMetadata info)
+ {
+ return info.LastWriteTimeUtc;
+ }
+
+ /// <summary>
+ /// Gets the creation time UTC.
+ /// </summary>
+ /// <param name="info">The info.</param>
+ /// <returns>DateTime.</returns>
+ public DateTime GetLastWriteTimeUtc(FileSystemInfo info)
+ {
+ // This could throw an error on some file systems that have dates out of range
+ try
+ {
+ return info.LastWriteTimeUtc;
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error determining LastAccessTimeUtc for {0}", ex, info.FullName);
+ return DateTime.MinValue;
+ }
+ }
+
+ /// <summary>
+ /// Gets the last write time UTC.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ /// <returns>DateTime.</returns>
+ public DateTime GetLastWriteTimeUtc(string path)
+ {
+ return GetLastWriteTimeUtc(GetFileSystemInfo(path));
+ }
+
+ /// <summary>
+ /// Gets the file stream.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ /// <param name="mode">The mode.</param>
+ /// <param name="access">The access.</param>
+ /// <param name="share">The share.</param>
+ /// <param name="isAsync">if set to <c>true</c> [is asynchronous].</param>
+ /// <returns>FileStream.</returns>
+ public Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share, bool isAsync = false)
+ {
+ if (_supportsAsyncFileStreams && isAsync)
+ {
+ return new FileStream(path, GetFileMode(mode), GetFileAccess(access), GetFileShare(share), 262144, true);
+ }
+
+ return new FileStream(path, GetFileMode(mode), GetFileAccess(access), GetFileShare(share), 262144);
+ }
+
+ private FileMode GetFileMode(FileOpenMode mode)
+ {
+ switch (mode)
+ {
+ case FileOpenMode.Append:
+ return FileMode.Append;
+ case FileOpenMode.Create:
+ return FileMode.Create;
+ case FileOpenMode.CreateNew:
+ return FileMode.CreateNew;
+ case FileOpenMode.Open:
+ return FileMode.Open;
+ case FileOpenMode.OpenOrCreate:
+ return FileMode.OpenOrCreate;
+ case FileOpenMode.Truncate:
+ return FileMode.Truncate;
+ default:
+ throw new Exception("Unrecognized FileOpenMode");
+ }
+ }
+
+ private FileAccess GetFileAccess(FileAccessMode mode)
+ {
+ switch (mode)
+ {
+ case FileAccessMode.ReadWrite:
+ return FileAccess.ReadWrite;
+ case FileAccessMode.Write:
+ return FileAccess.Write;
+ case FileAccessMode.Read:
+ return FileAccess.Read;
+ default:
+ throw new Exception("Unrecognized FileAccessMode");
+ }
+ }
+
+ private FileShare GetFileShare(FileShareMode mode)
+ {
+ switch (mode)
+ {
+ case FileShareMode.ReadWrite:
+ return FileShare.ReadWrite;
+ case FileShareMode.Write:
+ return FileShare.Write;
+ case FileShareMode.Read:
+ return FileShare.Read;
+ case FileShareMode.None:
+ return FileShare.None;
+ default:
+ throw new Exception("Unrecognized FileShareMode");
+ }
+ }
+
+ public void SetHidden(string path, bool isHidden)
+ {
+ var info = GetFileInfo(path);
+
+ if (info.Exists && info.IsHidden != isHidden)
+ {
+ if (isHidden)
+ {
+ File.SetAttributes(path, File.GetAttributes(path) | FileAttributes.Hidden);
+ }
+ else
+ {
+ FileAttributes attributes = File.GetAttributes(path);
+ attributes = RemoveAttribute(attributes, FileAttributes.Hidden);
+ File.SetAttributes(path, attributes);
+ }
+ }
+ }
+
+ public void SetReadOnly(string path, bool isReadOnly)
+ {
+ var info = GetFileInfo(path);
+
+ if (info.Exists && info.IsReadOnly != isReadOnly)
+ {
+ if (isReadOnly)
+ {
+ File.SetAttributes(path, File.GetAttributes(path) | FileAttributes.ReadOnly);
+ }
+ else
+ {
+ FileAttributes attributes = File.GetAttributes(path);
+ attributes = RemoveAttribute(attributes, FileAttributes.ReadOnly);
+ File.SetAttributes(path, attributes);
+ }
+ }
+ }
+
+ private static FileAttributes RemoveAttribute(FileAttributes attributes, FileAttributes attributesToRemove)
+ {
+ return attributes & ~attributesToRemove;
+ }
+
+ /// <summary>
+ /// Swaps the files.
+ /// </summary>
+ /// <param name="file1">The file1.</param>
+ /// <param name="file2">The file2.</param>
+ public void SwapFiles(string file1, string file2)
+ {
+ if (string.IsNullOrEmpty(file1))
+ {
+ throw new ArgumentNullException("file1");
+ }
+
+ if (string.IsNullOrEmpty(file2))
+ {
+ throw new ArgumentNullException("file2");
+ }
+
+ var temp1 = Path.GetTempFileName();
+
+ // Copying over will fail against hidden files
+ RemoveHiddenAttribute(file1);
+ RemoveHiddenAttribute(file2);
+
+ CopyFile(file1, temp1, true);
+
+ CopyFile(file2, file1, true);
+ CopyFile(temp1, file2, true);
+
+ DeleteFile(temp1);
+ }
+
+ /// <summary>
+ /// Removes the hidden attribute.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ private void RemoveHiddenAttribute(string path)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ throw new ArgumentNullException("path");
+ }
+
+ var currentFile = new FileInfo(path);
+
+ // This will fail if the file is hidden
+ if (currentFile.Exists)
+ {
+ if ((currentFile.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
+ {
+ currentFile.Attributes &= ~FileAttributes.Hidden;
+ }
+ }
+ }
+
+ public bool ContainsSubPath(string parentPath, string path)
+ {
+ if (string.IsNullOrEmpty(parentPath))
+ {
+ throw new ArgumentNullException("parentPath");
+ }
+
+ if (string.IsNullOrEmpty(path))
+ {
+ throw new ArgumentNullException("path");
+ }
+
+ return path.IndexOf(parentPath.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase) != -1;
+ }
+
+ public bool IsRootPath(string path)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ throw new ArgumentNullException("path");
+ }
+
+ var parent = Path.GetDirectoryName(path);
+
+ if (!string.IsNullOrEmpty(parent))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ public string NormalizePath(string path)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ throw new ArgumentNullException("path");
+ }
+
+ if (path.EndsWith(":\\", StringComparison.OrdinalIgnoreCase))
+ {
+ return path;
+ }
+
+ return path.TrimEnd(Path.DirectorySeparatorChar);
+ }
+
+ public string GetFileNameWithoutExtension(FileSystemMetadata info)
+ {
+ if (info.IsDirectory)
+ {
+ return info.Name;
+ }
+
+ return Path.GetFileNameWithoutExtension(info.FullName);
+ }
+
+ public string GetFileNameWithoutExtension(string path)
+ {
+ return Path.GetFileNameWithoutExtension(path);
+ }
+
+ 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 (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) != -1 &&
+ !path.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ return true;
+
+ //return Path.IsPathRooted(path);
+ }
+
+ public void DeleteFile(string path)
+ {
+ var fileInfo = GetFileInfo(path);
+
+ if (fileInfo.Exists)
+ {
+ if (fileInfo.IsHidden)
+ {
+ SetHidden(path, false);
+ }
+ if (fileInfo.IsReadOnly)
+ {
+ SetReadOnly(path, false);
+ }
+ }
+
+ File.Delete(path);
+ }
+
+ public void DeleteDirectory(string path, bool recursive)
+ {
+ Directory.Delete(path, recursive);
+ }
+
+ public void CreateDirectory(string path)
+ {
+ Directory.CreateDirectory(path);
+ }
+
+ public List<FileSystemMetadata> GetDrives()
+ {
+ // Only include drives in the ready state or this method could end up being very slow, waiting for drives to timeout
+ return DriveInfo.GetDrives().Where(d => d.IsReady).Select(d => new FileSystemMetadata
+ {
+ Name = GetName(d),
+ FullName = d.RootDirectory.FullName,
+ IsDirectory = true
+
+ }).ToList();
+ }
+
+ private string GetName(DriveInfo drive)
+ {
+ return drive.Name;
+ }
+
+ public IEnumerable<FileSystemMetadata> GetDirectories(string path, bool recursive = false)
+ {
+ var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
+
+ return ToMetadata(path, new DirectoryInfo(path).EnumerateDirectories("*", searchOption));
+ }
+
+ public IEnumerable<FileSystemMetadata> GetFiles(string path, bool recursive = false)
+ {
+ var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
+
+ return ToMetadata(path, new DirectoryInfo(path).EnumerateFiles("*", searchOption));
+ }
+
+ public IEnumerable<FileSystemMetadata> GetFileSystemEntries(string path, bool recursive = false)
+ {
+ var directoryInfo = new DirectoryInfo(path);
+ var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
+
+ if (EnableFileSystemRequestConcat)
+ {
+ return ToMetadata(path, directoryInfo.EnumerateDirectories("*", searchOption))
+ .Concat(ToMetadata(path, directoryInfo.EnumerateFiles("*", searchOption)));
+ }
+
+ return ToMetadata(path, directoryInfo.EnumerateFileSystemInfos("*", searchOption));
+ }
+
+ private IEnumerable<FileSystemMetadata> ToMetadata(string parentPath, IEnumerable<FileSystemInfo> infos)
+ {
+ return infos.Select(i =>
+ {
+ try
+ {
+ return GetFileSystemMetadata(i);
+ }
+ catch (PathTooLongException)
+ {
+ // Can't log using the FullName because it will throw the PathTooLongExceptiona again
+ //Logger.Warn("Path too long: {0}", i.FullName);
+ Logger.Warn("File or directory path too long. Parent folder: {0}", parentPath);
+ return null;
+ }
+
+ }).Where(i => i != null);
+ }
+
+ public string[] ReadAllLines(string path)
+ {
+ return File.ReadAllLines(path);
+ }
+
+ public void WriteAllLines(string path, IEnumerable<string> lines)
+ {
+ File.WriteAllLines(path, lines);
+ }
+
+ public Stream OpenRead(string path)
+ {
+ return File.OpenRead(path);
+ }
+
+ public void CopyFile(string source, string target, bool overwrite)
+ {
+ File.Copy(source, target, overwrite);
+ }
+
+ public void MoveFile(string source, string target)
+ {
+ File.Move(source, target);
+ }
+
+ public void MoveDirectory(string source, string target)
+ {
+ Directory.Move(source, target);
+ }
+
+ public bool DirectoryExists(string path)
+ {
+ return Directory.Exists(path);
+ }
+
+ public bool FileExists(string path)
+ {
+ return File.Exists(path);
+ }
+
+ public string ReadAllText(string path)
+ {
+ return File.ReadAllText(path);
+ }
+
+ public byte[] ReadAllBytes(string path)
+ {
+ return File.ReadAllBytes(path);
+ }
+
+ public void WriteAllText(string path, string text, Encoding encoding)
+ {
+ File.WriteAllText(path, text, encoding);
+ }
+
+ public void WriteAllText(string path, string text)
+ {
+ File.WriteAllText(path, text);
+ }
+
+ public void WriteAllBytes(string path, byte[] bytes)
+ {
+ File.WriteAllBytes(path, bytes);
+ }
+
+ public string ReadAllText(string path, Encoding encoding)
+ {
+ return File.ReadAllText(path, encoding);
+ }
+
+ public IEnumerable<string> GetDirectoryPaths(string path, bool recursive = false)
+ {
+ var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
+ return Directory.EnumerateDirectories(path, "*", searchOption);
+ }
+
+ public IEnumerable<string> GetFilePaths(string path, bool recursive = false)
+ {
+ var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
+ return Directory.EnumerateFiles(path, "*", searchOption);
+ }
+
+ public IEnumerable<string> GetFileSystemEntryPaths(string path, bool recursive = false)
+ {
+ var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
+ return Directory.EnumerateFileSystemEntries(path, "*", searchOption);
+ }
+
+ public virtual void SetExecutable(string path)
+ {
+
+ }
+ }
+}
diff --git a/MediaBrowser.Common.Implementations/Logging/NLogger.cs b/Emby.Common.Implementations/Logging/NLogger.cs
index 11f41261a2..8abd3d0d92 100644
--- a/MediaBrowser.Common.Implementations/Logging/NLogger.cs
+++ b/Emby.Common.Implementations/Logging/NLogger.cs
@@ -2,7 +2,7 @@
using System;
using System.Text;
-namespace MediaBrowser.Common.Implementations.Logging
+namespace Emby.Common.Implementations.Logging
{
/// <summary>
/// Class NLogger
diff --git a/Emby.Common.Implementations/Logging/NlogManager.cs b/Emby.Common.Implementations/Logging/NlogManager.cs
new file mode 100644
index 0000000000..f7b723e8bc
--- /dev/null
+++ b/Emby.Common.Implementations/Logging/NlogManager.cs
@@ -0,0 +1,544 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using NLog;
+using NLog.Config;
+using NLog.Filters;
+using NLog.Targets;
+using NLog.Targets.Wrappers;
+using MediaBrowser.Model.Logging;
+
+namespace Emby.Common.Implementations.Logging
+{
+ /// <summary>
+ /// Class NlogManager
+ /// </summary>
+ public class NlogManager : ILogManager
+ {
+ #region Private Fields
+
+ private LogSeverity _severity = LogSeverity.Debug;
+
+ /// <summary>
+ /// Gets or sets the log directory.
+ /// </summary>
+ /// <value>The log directory.</value>
+ private readonly string LogDirectory;
+
+ /// <summary>
+ /// Gets or sets the log file prefix.
+ /// </summary>
+ /// <value>The log file prefix.</value>
+ private readonly string LogFilePrefix;
+
+ #endregion
+
+ #region Event Declarations
+
+ /// <summary>
+ /// Occurs when [logger loaded].
+ /// </summary>
+ public event EventHandler LoggerLoaded;
+
+ #endregion
+
+ #region Public Properties
+
+ /// <summary>
+ /// Gets the log file path.
+ /// </summary>
+ /// <value>The log file path.</value>
+ public string LogFilePath { get; private set; }
+
+ /// <summary>
+ /// Gets or sets the exception message prefix.
+ /// </summary>
+ /// <value>The exception message prefix.</value>
+ public string ExceptionMessagePrefix { get; set; }
+
+ public string NLogConfigurationFilePath { get; set; }
+
+ public LogSeverity LogSeverity
+ {
+
+ get
+ {
+ return _severity;
+ }
+
+ set
+ {
+ DebugFileWriter(
+ LogDirectory, String.Format(
+ "SET LogSeverity, _severity = [{0}], value = [{1}]",
+ _severity.ToString(),
+ value.ToString()
+ ));
+
+ var changed = _severity != value;
+
+ _severity = value;
+
+ if (changed)
+ {
+ UpdateLogLevel(value);
+ }
+
+ }
+ }
+
+ #endregion
+
+ #region Constructor(s)
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NlogManager" /> class.
+ /// </summary>
+ /// <param name="logDirectory">The log directory.</param>
+ /// <param name="logFileNamePrefix">The log file name prefix.</param>
+ public NlogManager(string logDirectory, string logFileNamePrefix)
+ {
+ DebugFileWriter(
+ logDirectory, String.Format(
+ "NlogManager constructor called, logDirectory is [{0}], logFileNamePrefix is [{1}], _severity is [{2}].",
+ logDirectory,
+ logFileNamePrefix,
+ _severity.ToString()
+ ));
+
+ LogDirectory = logDirectory;
+ LogFilePrefix = logFileNamePrefix;
+
+ LogManager.Configuration = new LoggingConfiguration();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NlogManager" /> class.
+ /// </summary>
+ /// <param name="logDirectory">The log directory.</param>
+ /// <param name="logFileNamePrefix">The log file name prefix.</param>
+ public NlogManager(string logDirectory, string logFileNamePrefix, LogSeverity initialSeverity) : this(logDirectory, logFileNamePrefix)
+ {
+ _severity = initialSeverity;
+
+ DebugFileWriter(
+ logDirectory, String.Format(
+ "NlogManager constructor called, logDirectory is [{0}], logFileNamePrefix is [{1}], _severity is [{2}].",
+ logDirectory,
+ logFileNamePrefix,
+ _severity.ToString()
+ ));
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ /// <summary>
+ /// Adds the file target.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ /// <param name="level">The level.</param>
+ private void AddFileTarget(string path, LogSeverity level)
+ {
+
+ DebugFileWriter(
+ LogDirectory, String.Format(
+ "AddFileTarget called, path = [{0}], level = [{1}].",
+ path,
+ level.ToString()
+ ));
+
+ RemoveTarget("ApplicationLogFileWrapper");
+
+ var wrapper = new AsyncTargetWrapper();
+ wrapper.Name = "ApplicationLogFileWrapper";
+
+ var logFile = new FileTarget
+ {
+ FileName = path,
+ Layout = "${longdate} ${level} ${logger}: ${message}"
+ };
+
+ logFile.Name = "ApplicationLogFile";
+
+ wrapper.WrappedTarget = logFile;
+
+ AddLogTarget(wrapper, level);
+
+ }
+
+ /// <summary>
+ /// Gets the log level.
+ /// </summary>
+ /// <param name="severity">The severity.</param>
+ /// <returns>LogLevel.</returns>
+ /// <exception cref="System.ArgumentException">Unrecognized LogSeverity</exception>
+ private LogLevel GetLogLevel(LogSeverity severity)
+ {
+ switch (severity)
+ {
+ case LogSeverity.Debug:
+ return LogLevel.Debug;
+ case LogSeverity.Error:
+ return LogLevel.Error;
+ case LogSeverity.Fatal:
+ return LogLevel.Fatal;
+ case LogSeverity.Info:
+ return LogLevel.Info;
+ case LogSeverity.Warn:
+ return LogLevel.Warn;
+ default:
+ throw new ArgumentException("Unrecognized LogSeverity");
+ }
+ }
+
+ private void UpdateLogLevel(LogSeverity newLevel)
+ {
+ DebugFileWriter(
+ LogDirectory, String.Format(
+ "UpdateLogLevel called, newLevel = [{0}].",
+ newLevel.ToString()
+ ));
+
+ var level = GetLogLevel(newLevel);
+
+ var rules = LogManager.Configuration.LoggingRules;
+
+ foreach (var rule in rules)
+ {
+ if (!rule.IsLoggingEnabledForLevel(level))
+ {
+ rule.EnableLoggingForLevel(level);
+ }
+ foreach (var lev in rule.Levels.ToArray())
+ {
+ if (lev < level)
+ {
+ rule.DisableLoggingForLevel(lev);
+ }
+ }
+ }
+ }
+
+ private void AddCustomFilters(string defaultLoggerNamePattern, LoggingRule defaultRule)
+ {
+ DebugFileWriter(
+ LogDirectory, String.Format(
+ "AddCustomFilters called, defaultLoggerNamePattern = [{0}], defaultRule.LoggerNamePattern = [{1}].",
+ defaultLoggerNamePattern,
+ defaultRule.LoggerNamePattern
+ ));
+
+ try
+ {
+ var customConfig = new NLog.Config.XmlLoggingConfiguration(NLogConfigurationFilePath);
+
+ DebugFileWriter(
+ LogDirectory, String.Format(
+ "Custom Configuration Loaded, Rule Count = [{0}].",
+ customConfig.LoggingRules.Count.ToString()
+ ));
+
+ foreach (var customRule in customConfig.LoggingRules)
+ {
+
+ DebugFileWriter(
+ LogDirectory, String.Format(
+ "Read Custom Rule, LoggerNamePattern = [{0}], Targets = [{1}].",
+ customRule.LoggerNamePattern,
+ string.Join(",", customRule.Targets.Select(x => x.Name).ToList())
+ ));
+
+ if (customRule.LoggerNamePattern.Equals(defaultLoggerNamePattern))
+ {
+
+ if (customRule.Targets.Any((arg) => arg.Name.Equals(defaultRule.Targets.First().Name)))
+ {
+
+ DebugFileWriter(
+ LogDirectory, String.Format(
+ "Custom rule filters can be applied to this target, Filter Count = [{0}].",
+ customRule.Filters.Count.ToString()
+ ));
+
+ foreach (ConditionBasedFilter customFilter in customRule.Filters)
+ {
+
+ DebugFileWriter(
+ LogDirectory, String.Format(
+ "Read Custom Filter, Filter = [{0}], Action = [{1}], Type = [{2}].",
+ customFilter.Condition.ToString(),
+ customFilter.Action.ToString(),
+ customFilter.GetType().ToString()
+ ));
+
+ defaultRule.Filters.Add(customFilter);
+
+ }
+ }
+ else
+ {
+
+ DebugFileWriter(
+ LogDirectory, String.Format(
+ "Ignoring custom rule as [Target] does not match."
+ ));
+
+ }
+
+ }
+ else
+ {
+
+ DebugFileWriter(
+ LogDirectory, String.Format(
+ "Ignoring custom rule as [LoggerNamePattern] does not match."
+ ));
+
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ // Intentionally do nothing, prevent issues affecting normal execution.
+ DebugFileWriter(
+ LogDirectory, String.Format(
+ "Exception in AddCustomFilters, ex.Message = [{0}].",
+ ex.Message
+ )
+ );
+
+ }
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ /// <summary>
+ /// Gets the logger.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ /// <returns>ILogger.</returns>
+ public MediaBrowser.Model.Logging.ILogger GetLogger(string name)
+ {
+
+ DebugFileWriter(
+ LogDirectory, String.Format(
+ "GetLogger called, name = [{0}].",
+ name
+ ));
+
+ return new NLogger(name, this);
+
+ }
+
+ /// <summary>
+ /// Adds the log target.
+ /// </summary>
+ /// <param name="target">The target.</param>
+ /// <param name="level">The level.</param>
+ public void AddLogTarget(Target target, LogSeverity level)
+ {
+
+ DebugFileWriter(
+ LogDirectory, String.Format(
+ "AddLogTarget called, target.Name = [{0}], level = [{1}].",
+ target.Name,
+ level.ToString()
+ ));
+
+ string loggerNamePattern = "*";
+ var config = LogManager.Configuration;
+ var rule = new LoggingRule(loggerNamePattern, GetLogLevel(level), target);
+
+ config.AddTarget(target.Name, target);
+
+ AddCustomFilters(loggerNamePattern, rule);
+
+ config.LoggingRules.Add(rule);
+
+ LogManager.Configuration = config;
+
+ }
+
+ /// <summary>
+ /// Removes the target.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ public void RemoveTarget(string name)
+ {
+
+ DebugFileWriter(
+ LogDirectory, String.Format(
+ "RemoveTarget called, name = [{0}].",
+ name
+ ));
+
+ var config = LogManager.Configuration;
+
+ var target = config.FindTargetByName(name);
+
+ if (target != null)
+ {
+ foreach (var rule in config.LoggingRules.ToList())
+ {
+ var contains = rule.Targets.Contains(target);
+
+ rule.Targets.Remove(target);
+
+ if (contains)
+ {
+ config.LoggingRules.Remove(rule);
+ }
+ }
+
+ config.RemoveTarget(name);
+ LogManager.Configuration = config;
+ }
+ }
+
+ public void AddConsoleOutput()
+ {
+
+ DebugFileWriter(
+ LogDirectory, String.Format(
+ "AddConsoleOutput called."
+ ));
+
+ RemoveTarget("ConsoleTargetWrapper");
+
+ var wrapper = new AsyncTargetWrapper();
+ wrapper.Name = "ConsoleTargetWrapper";
+
+ var target = new ConsoleTarget()
+ {
+ Layout = "${level}, ${logger}, ${message}",
+ Error = false
+ };
+
+ target.Name = "ConsoleTarget";
+
+ wrapper.WrappedTarget = target;
+
+ AddLogTarget(wrapper, LogSeverity);
+
+ }
+
+ public void RemoveConsoleOutput()
+ {
+
+ DebugFileWriter(
+ LogDirectory, String.Format(
+ "RemoveConsoleOutput called."
+ ));
+
+ RemoveTarget("ConsoleTargetWrapper");
+
+ }
+
+ /// <summary>
+ /// Reloads the logger, maintaining the current log level.
+ /// </summary>
+ public void ReloadLogger()
+ {
+ ReloadLogger(LogSeverity);
+ }
+
+ /// <summary>
+ /// Reloads the logger, using the specified logging level.
+ /// </summary>
+ /// <param name="level">The level.</param>
+ public void ReloadLogger(LogSeverity level)
+ {
+
+ DebugFileWriter(
+ LogDirectory, String.Format(
+ "ReloadLogger called, level = [{0}], LogFilePath (existing) = [{1}].",
+ level.ToString(),
+ LogFilePath
+ ));
+
+ LogFilePath = Path.Combine(LogDirectory, LogFilePrefix + "-" + decimal.Floor(DateTime.Now.Ticks / 10000000) + ".txt");
+
+ Directory.CreateDirectory(Path.GetDirectoryName(LogFilePath));
+
+ AddFileTarget(LogFilePath, level);
+
+ LogSeverity = level;
+
+ if (LoggerLoaded != null)
+ {
+ try
+ {
+
+ DebugFileWriter(
+ LogDirectory, String.Format(
+ "ReloadLogger called, raised event LoggerLoaded."
+ ));
+
+ LoggerLoaded(this, EventArgs.Empty);
+
+ }
+ catch (Exception ex)
+ {
+ GetLogger("Logger").ErrorException("Error in LoggerLoaded event", ex);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Flushes this instance.
+ /// </summary>
+ public void Flush()
+ {
+
+ DebugFileWriter(
+ LogDirectory, String.Format(
+ "Flush called."
+ ));
+
+ LogManager.Flush();
+
+ }
+
+ #endregion
+
+ #region Conditional Debug Methods
+
+ /// <summary>
+ /// DEBUG: Standalone method to write out debug to assist with logger development/troubleshooting.
+ /// <list type="bullet">
+ /// <item><description>The output file will be written to the server's log directory.</description></item>
+ /// <item><description>Calls to the method are safe and will never throw any exceptions.</description></item>
+ /// <item><description>Method calls will be omitted unless the library is compiled with DEBUG defined.</description></item>
+ /// </list>
+ /// </summary>
+ private static void DebugFileWriter(string logDirectory, string message)
+ {
+#if DEBUG
+ try
+ {
+
+ System.IO.File.AppendAllText(
+ Path.Combine(logDirectory, "NlogManager.txt"),
+ String.Format(
+ "{0} : {1}{2}",
+ System.DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"),
+ message,
+ System.Environment.NewLine
+ )
+ );
+
+ }
+ catch (Exception ex)
+ {
+ // Intentionally do nothing, prevent issues affecting normal execution.
+ }
+#endif
+ }
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/Emby.Common.Implementations/Net/DisposableManagedObjectBase.cs b/Emby.Common.Implementations/Net/DisposableManagedObjectBase.cs
new file mode 100644
index 0000000000..8476cea326
--- /dev/null
+++ b/Emby.Common.Implementations/Net/DisposableManagedObjectBase.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Emby.Common.Implementations.Net
+{
+ /// <summary>
+ /// Correclty implements the <see cref="IDisposable"/> interface and pattern for an object containing only managed resources, and adds a few common niceities not on the interface such as an <see cref="IsDisposed"/> property.
+ /// </summary>
+ public abstract class DisposableManagedObjectBase : IDisposable
+ {
+
+ #region Public Methods
+
+ /// <summary>
+ /// Override this method and dispose any objects you own the lifetime of if disposing is true;
+ /// </summary>
+ /// <param name="disposing">True if managed objects should be disposed, if false, only unmanaged resources should be released.</param>
+ protected abstract void Dispose(bool disposing);
+
+ /// <summary>
+ /// Throws and <see cref="System.ObjectDisposedException"/> if the <see cref="IsDisposed"/> property is true.
+ /// </summary>
+ /// <seealso cref="IsDisposed"/>
+ /// <exception cref="System.ObjectDisposedException">Thrown if the <see cref="IsDisposed"/> property is true.</exception>
+ /// <seealso cref="Dispose()"/>
+ protected virtual void ThrowIfDisposed()
+ {
+ if (this.IsDisposed) throw new ObjectDisposedException(this.GetType().FullName);
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ /// <summary>
+ /// Sets or returns a boolean indicating whether or not this instance has been disposed.
+ /// </summary>
+ /// <seealso cref="Dispose()"/>
+ public bool IsDisposed
+ {
+ get;
+ private set;
+ }
+
+ #endregion
+
+ #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"/>
+ public void Dispose()
+ {
+ try
+ {
+ IsDisposed = true;
+
+ Dispose(true);
+ }
+ finally
+ {
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Emby.Common.Implementations/Net/NetSocket.cs b/Emby.Common.Implementations/Net/NetSocket.cs
new file mode 100644
index 0000000000..bc012dfe2d
--- /dev/null
+++ b/Emby.Common.Implementations/Net/NetSocket.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+using Emby.Common.Implementations.Networking;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Logging;
+
+namespace Emby.Common.Implementations.Net
+{
+ public class NetSocket : ISocket
+ {
+ public Socket Socket { get; private set; }
+ private readonly ILogger _logger;
+
+ public bool DualMode { get; private set; }
+
+ public NetSocket(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 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);
+ }
+
+ private SocketAcceptor _acceptor;
+ public void StartAccept(Action<ISocket> onAccept, Func<bool> isClosed)
+ {
+ _acceptor = new SocketAcceptor(_logger, Socket, onAccept, isClosed, DualMode);
+
+ _acceptor.StartAccept();
+ }
+
+ public void Dispose()
+ {
+ Socket.Dispose();
+ }
+ }
+}
diff --git a/Emby.Common.Implementations/Net/SocketAcceptor.cs b/Emby.Common.Implementations/Net/SocketAcceptor.cs
new file mode 100644
index 0000000000..d4c6d33e52
--- /dev/null
+++ b/Emby.Common.Implementations/Net/SocketAcceptor.cs
@@ -0,0 +1,127 @@
+using System;
+using System.Net.Sockets;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Net;
+
+namespace Emby.Common.Implementations.Net
+{
+ public class SocketAcceptor
+ {
+ private readonly ILogger _logger;
+ private readonly Socket _originalSocket;
+ private readonly Func<bool> _isClosed;
+ private readonly Action<ISocket> _onAccept;
+ private readonly bool _isDualMode;
+
+ public SocketAcceptor(ILogger logger, Socket originalSocket, Action<ISocket> onAccept, Func<bool> isClosed, bool isDualMode)
+ {
+ 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;
+ _isDualMode = isDualMode;
+ _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
+ {
+ // socket 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.socket.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(new NetSocket(acceptSocket, _logger, _isDualMode));
+ }
+
+ // Accept the next connection request
+ StartAccept(e, ref acceptSocket);
+ }
+ }
+}
diff --git a/Emby.Common.Implementations/Net/SocketFactory.cs b/Emby.Common.Implementations/Net/SocketFactory.cs
new file mode 100644
index 0000000000..70c7ba8458
--- /dev/null
+++ b/Emby.Common.Implementations/Net/SocketFactory.cs
@@ -0,0 +1,160 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading.Tasks;
+using Emby.Common.Implementations.Networking;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Net;
+
+namespace Emby.Common.Implementations.Net
+{
+ public class SocketFactory : ISocketFactory
+ {
+ // 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.
+
+ // Not entirely happy with this. Would have liked to have done something more generic/reusable,
+ // but that wasn't really the point so kept to YAGNI principal for now, even if the
+ // interfaces are a bit ugly, specific and make assumptions.
+
+ private readonly ILogger _logger;
+
+ public SocketFactory(ILogger logger)
+ {
+ if (logger == null)
+ {
+ throw new ArgumentNullException("logger");
+ }
+
+ _logger = logger;
+ }
+
+ public ISocket 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 NetSocket(socket, _logger, dualMode);
+ }
+ catch (SocketException ex)
+ {
+ throw new SocketCreateException(ex.SocketErrorCode.ToString(), ex);
+ }
+ }
+
+ #region ISocketFactory Members
+
+ /// <summary>
+ /// Creates a new UDP socket and binds it to the specified local port.
+ /// </summary>
+ /// <param name="localPort">An integer specifying the local port to bind the socket to.</param>
+ public IUdpSocket CreateUdpSocket(int localPort)
+ {
+ if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", "localPort");
+
+ var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp);
+ try
+ {
+ retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
+ return new UdpSocket(retVal, localPort, IPAddress.Any);
+ }
+ catch
+ {
+ if (retVal != null)
+ retVal.Dispose();
+
+ throw;
+ }
+ }
+
+ /// <summary>
+ /// Creates a new UDP socket that is a member of the SSDP multicast local admin group and binds it to the specified local port.
+ /// </summary>
+ /// <returns>An implementation of the <see cref="IUdpSocket"/> interface used by RSSDP components to perform socket operations.</returns>
+ public IUdpSocket CreateSsdpUdpSocket(IpAddressInfo localIpAddress, int localPort)
+ {
+ if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", "localPort");
+
+ var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp);
+ try
+ {
+ retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
+ retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4);
+
+ var localIp = NetworkManager.ToIPAddress(localIpAddress);
+
+ retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse("239.255.255.250"), localIp));
+ return new UdpSocket(retVal, localPort, localIp);
+ }
+ catch
+ {
+ if (retVal != null)
+ retVal.Dispose();
+
+ throw;
+ }
+ }
+
+ /// <summary>
+ /// Creates a new UDP socket that is a member of the specified multicast IP address, and binds it to the specified local port.
+ /// </summary>
+ /// <param name="ipAddress">The multicast IP address to make the socket a member of.</param>
+ /// <param name="multicastTimeToLive">The multicast time to live value for the socket.</param>
+ /// <param name="localPort">The number of the local port to bind to.</param>
+ /// <returns></returns>
+ public IUdpSocket CreateUdpMulticastSocket(string ipAddress, int multicastTimeToLive, int localPort)
+ {
+ if (ipAddress == null) throw new ArgumentNullException("ipAddress");
+ if (ipAddress.Length == 0) throw new ArgumentException("ipAddress cannot be an empty string.", "ipAddress");
+ if (multicastTimeToLive <= 0) throw new ArgumentException("multicastTimeToLive cannot be zero or less.", "multicastTimeToLive");
+ if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", "localPort");
+
+ var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp);
+
+ try
+ {
+#if NET46
+ retVal.ExclusiveAddressUse = false;
+#else
+ // The ExclusiveAddressUse socket option is a Windows-specific option that, when set to "true," tells Windows not to allow another socket to use the same local address as this socket
+ // See https://github.com/dotnet/corefx/pull/11509 for more details
+ if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows))
+ {
+ retVal.ExclusiveAddressUse = false;
+ }
+#endif
+ //retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
+ retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
+ retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, multicastTimeToLive);
+
+ var localIp = IPAddress.Any;
+
+ retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse(ipAddress), localIp));
+ retVal.MulticastLoopback = true;
+
+ return new UdpSocket(retVal, localPort, localIp);
+ }
+ catch
+ {
+ if (retVal != null)
+ retVal.Dispose();
+
+ throw;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Emby.Common.Implementations/Net/UdpSocket.cs b/Emby.Common.Implementations/Net/UdpSocket.cs
new file mode 100644
index 0000000000..b2af9d162e
--- /dev/null
+++ b/Emby.Common.Implementations/Net/UdpSocket.cs
@@ -0,0 +1,242 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Security;
+using System.Threading.Tasks;
+using Emby.Common.Implementations.Networking;
+using MediaBrowser.Model.Net;
+
+namespace Emby.Common.Implementations.Net
+{
+ // 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.
+
+ internal sealed class UdpSocket : DisposableManagedObjectBase, IUdpSocket
+ {
+
+ #region Fields
+
+ private Socket _Socket;
+ private int _LocalPort;
+ #endregion
+
+ #region Constructors
+
+ public UdpSocket(Socket socket, int localPort, IPAddress ip)
+ {
+ if (socket == null) throw new ArgumentNullException("socket");
+
+ _Socket = socket;
+ _LocalPort = localPort;
+ LocalIPAddress = NetworkManager.ToIpAddressInfo(ip);
+
+ _Socket.Bind(new IPEndPoint(ip, _LocalPort));
+ }
+
+ #endregion
+
+ public IpAddressInfo LocalIPAddress
+ {
+ get;
+ private set;
+ }
+
+ #region IUdpSocket Members
+
+ public Task<SocketReceiveResult> ReceiveAsync()
+ {
+ ThrowIfDisposed();
+
+ var tcs = new TaskCompletionSource<SocketReceiveResult>();
+
+ EndPoint receivedFromEndPoint = new IPEndPoint(IPAddress.Any, 0);
+ var state = new AsyncReceiveState(_Socket, receivedFromEndPoint);
+ state.TaskCompletionSource = tcs;
+
+#if NETSTANDARD1_6
+ _Socket.ReceiveFromAsync(new ArraySegment<Byte>(state.Buffer),SocketFlags.None, state.RemoteEndPoint)
+ .ContinueWith((task, asyncState) =>
+ {
+ if (task.Status != TaskStatus.Faulted)
+ {
+ var receiveState = asyncState as AsyncReceiveState;
+ receiveState.RemoteEndPoint = task.Result.RemoteEndPoint;
+ ProcessResponse(receiveState, () => task.Result.ReceivedBytes, LocalIPAddress);
+ }
+ }, state);
+#else
+ _Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, ref state.RemoteEndPoint, ProcessResponse, state);
+#endif
+
+ return tcs.Task;
+ }
+
+ public Task SendAsync(byte[] buffer, int size, IpEndPointInfo endPoint)
+ {
+ ThrowIfDisposed();
+
+ if (buffer == null) throw new ArgumentNullException("messageData");
+ if (endPoint == null) throw new ArgumentNullException("endPoint");
+
+ var ipEndPoint = NetworkManager.ToIPEndPoint(endPoint);
+
+#if NETSTANDARD1_6
+
+ if (size != buffer.Length)
+ {
+ byte[] copy = new byte[size];
+ Buffer.BlockCopy(buffer, 0, copy, 0, size);
+ buffer = copy;
+ }
+
+ _Socket.SendTo(buffer, ipEndPoint);
+ return Task.FromResult(true);
+#else
+ var taskSource = new TaskCompletionSource<bool>();
+
+ try
+ {
+ _Socket.BeginSendTo(buffer, 0, size, SocketFlags.None, ipEndPoint, result =>
+ {
+ try
+ {
+ _Socket.EndSend(result);
+ taskSource.TrySetResult(true);
+ }
+ catch (Exception ex)
+ {
+ taskSource.TrySetException(ex);
+ }
+
+ }, null);
+ }
+ catch (Exception ex)
+ {
+ taskSource.TrySetException(ex);
+ }
+
+ //_Socket.SendTo(messageData, new System.Net.IPEndPoint(IPAddress.Parse(RemoteEndPoint.IPAddress), RemoteEndPoint.Port));
+
+ return taskSource.Task;
+#endif
+ }
+
+ #endregion
+
+ #region Overrides
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ var socket = _Socket;
+ if (socket != null)
+ socket.Dispose();
+ }
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private static void ProcessResponse(AsyncReceiveState state, Func<int> receiveData, IpAddressInfo localIpAddress)
+ {
+ try
+ {
+ var bytesRead = receiveData();
+
+ var ipEndPoint = state.RemoteEndPoint as IPEndPoint;
+ state.TaskCompletionSource.SetResult(
+ new SocketReceiveResult
+ {
+ Buffer = state.Buffer,
+ ReceivedBytes = bytesRead,
+ RemoteEndPoint = ToIpEndPointInfo(ipEndPoint),
+ LocalIPAddress = localIpAddress
+ }
+ );
+ }
+ catch (ObjectDisposedException)
+ {
+ state.TaskCompletionSource.SetCanceled();
+ }
+ catch (SocketException se)
+ {
+ if (se.SocketErrorCode != SocketError.Interrupted && se.SocketErrorCode != SocketError.OperationAborted && se.SocketErrorCode != SocketError.Shutdown)
+ state.TaskCompletionSource.SetException(se);
+ else
+ state.TaskCompletionSource.SetCanceled();
+ }
+ catch (Exception ex)
+ {
+ state.TaskCompletionSource.SetException(ex);
+ }
+ }
+
+ private static IpEndPointInfo ToIpEndPointInfo(IPEndPoint endpoint)
+ {
+ if (endpoint == null)
+ {
+ return null;
+ }
+
+ return NetworkManager.ToIpEndPointInfo(endpoint);
+ }
+
+ private void ProcessResponse(IAsyncResult asyncResult)
+ {
+#if NET46
+ var state = asyncResult.AsyncState as AsyncReceiveState;
+ try
+ {
+ var bytesRead = state.Socket.EndReceiveFrom(asyncResult, ref state.RemoteEndPoint);
+
+ var ipEndPoint = state.RemoteEndPoint as IPEndPoint;
+ state.TaskCompletionSource.SetResult(
+ new SocketReceiveResult
+ {
+ Buffer = state.Buffer,
+ ReceivedBytes = bytesRead,
+ RemoteEndPoint = ToIpEndPointInfo(ipEndPoint),
+ LocalIPAddress = LocalIPAddress
+ }
+ );
+ }
+ catch (ObjectDisposedException)
+ {
+ state.TaskCompletionSource.SetCanceled();
+ }
+ catch (Exception ex)
+ {
+ state.TaskCompletionSource.SetException(ex);
+ }
+#endif
+ }
+
+ #endregion
+
+ #region Private Classes
+
+ private class AsyncReceiveState
+ {
+ public AsyncReceiveState(Socket socket, EndPoint remoteEndPoint)
+ {
+ this.Socket = socket;
+ this.RemoteEndPoint = remoteEndPoint;
+ }
+
+ public EndPoint RemoteEndPoint;
+ public byte[] Buffer = new byte[8192];
+
+ public Socket Socket { get; private set; }
+
+ public TaskCompletionSource<SocketReceiveResult> TaskCompletionSource { get; set; }
+
+ }
+
+ #endregion
+
+ }
+}
diff --git a/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs b/Emby.Common.Implementations/Networking/NetworkManager.cs
index 1b5e260d71..4485e8b14e 100644
--- a/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs
+++ b/Emby.Common.Implementations/Networking/NetworkManager.cs
@@ -6,28 +6,28 @@ using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
-using MoreLinq;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Common.Net;
-namespace MediaBrowser.Common.Implementations.Networking
+namespace Emby.Common.Implementations.Networking
{
- public abstract class BaseNetworkManager
+ public class NetworkManager : INetworkManager
{
protected ILogger Logger { get; private set; }
private DateTime _lastRefresh;
- protected BaseNetworkManager(ILogger logger)
+ public NetworkManager(ILogger logger)
{
Logger = logger;
}
- private List<IPAddress> _localIpAddresses;
+ private List<IpAddressInfo> _localIpAddresses;
private readonly object _localIpAddressSyncLock = new object();
- /// <summary>
- /// Gets the machine's local ip address
- /// </summary>
- /// <returns>IPAddress.</returns>
- public IEnumerable<IPAddress> GetLocalIpAddresses()
+ public List<IpAddressInfo> GetLocalIpAddresses()
{
const int cacheMinutes = 5;
@@ -37,7 +37,7 @@ namespace MediaBrowser.Common.Implementations.Networking
if (_localIpAddresses == null || forceRefresh)
{
- var addresses = GetLocalIpAddressesInternal().ToList();
+ var addresses = GetLocalIpAddressesInternal().Select(ToIpAddressInfo).ToList();
_localIpAddresses = addresses;
_lastRefresh = DateTime.UtcNow;
@@ -49,24 +49,24 @@ namespace MediaBrowser.Common.Implementations.Networking
return _localIpAddresses;
}
- private IEnumerable<IPAddress> GetLocalIpAddressesInternal()
+ private IEnumerable<IPAddress> GetLocalIpAddressesInternal()
{
var list = GetIPsDefault()
.ToList();
if (list.Count == 0)
{
- list.AddRange(GetLocalIpAddressesFallback());
+ list.AddRange(GetLocalIpAddressesFallback().Result);
}
- return list.Where(FilterIpAddress).DistinctBy(i => i.ToString());
+ return list.Where(FilterIpAddress).DistinctBy(i => i.ToString());
}
- private bool FilterIpAddress(IPAddress address)
+ private bool FilterIpAddress(IPAddress address)
{
- var addressString = address.ToString ();
+ var addressString = address.ToString();
- if (addressString.StartsWith("169.", StringComparison.OrdinalIgnoreCase))
+ if (addressString.StartsWith("169.", StringComparison.OrdinalIgnoreCase))
{
return false;
}
@@ -154,12 +154,12 @@ namespace MediaBrowser.Common.Implementations.Networking
{
var prefix = addressString.Substring(0, lengthMatch);
- if (GetLocalIpAddresses().Any(i => i.ToString().StartsWith(prefix, StringComparison.OrdinalIgnoreCase)))
+ if (GetLocalIpAddresses().Any(i => i.ToString().StartsWith(prefix, StringComparison.OrdinalIgnoreCase)))
{
return true;
}
}
- }
+ }
else if (resolveHost)
{
Uri uri;
@@ -170,7 +170,7 @@ namespace MediaBrowser.Common.Implementations.Networking
var host = uri.DnsSafeHost;
Logger.Debug("Resolving host {0}", host);
- address = GetIpAddresses(host).FirstOrDefault();
+ address = GetIpAddresses(host).Result.FirstOrDefault();
if (address != null)
{
@@ -193,52 +193,76 @@ namespace MediaBrowser.Common.Implementations.Networking
return false;
}
- public IEnumerable<IPAddress> GetIpAddresses(string hostName)
+ private Task<IPAddress[]> GetIpAddresses(string hostName)
{
- return Dns.GetHostAddresses(hostName);
+ return Dns.GetHostAddressesAsync(hostName);
}
- private List<IPAddress> GetIPsDefault()
- {
- NetworkInterface[] interfaces;
+ private readonly List<NetworkInterfaceType> _validNetworkInterfaceTypes = new List<NetworkInterfaceType>
+ {
+ NetworkInterfaceType.Ethernet,
+ NetworkInterfaceType.Wireless80211
+ };
+
+ private List<IPAddress> GetIPsDefault()
+ {
+ NetworkInterface[] interfaces;
- try
- {
- interfaces = NetworkInterface.GetAllNetworkInterfaces();
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error in GetAllNetworkInterfaces", ex);
- return new List<IPAddress>();
- }
+ try
+ {
+ var validStatuses = new[] { OperationalStatus.Up, OperationalStatus.Unknown };
- return interfaces.SelectMany(network => {
+ interfaces = NetworkInterface.GetAllNetworkInterfaces()
+ .Where(i => validStatuses.Contains(i.OperationalStatus))
+ .ToArray();
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error in GetAllNetworkInterfaces", ex);
+ return new List<IPAddress>();
+ }
- try
- {
+ return interfaces.SelectMany(network =>
+ {
+
+ try
+ {
Logger.Debug("Querying interface: {0}. Type: {1}. Status: {2}", network.Name, network.NetworkInterfaceType, network.OperationalStatus);
- var properties = network.GetIPProperties();
+ var ipProperties = network.GetIPProperties();
+
+ // Try to exclude virtual adapters
+ // http://stackoverflow.com/questions/8089685/c-sharp-finding-my-machines-local-ip-address-and-not-the-vms
+ var addr = ipProperties.GatewayAddresses.FirstOrDefault();
+ if (addr == null|| string.Equals(addr.Address.ToString(), "0.0.0.0", StringComparison.OrdinalIgnoreCase))
+ {
+ return new List<IPAddress>();
+ }
+
+ //if (!_validNetworkInterfaceTypes.Contains(network.NetworkInterfaceType))
+ //{
+ // return new List<IPAddress>();
+ //}
- return properties.UnicastAddresses
- .Where(i => i.IsDnsEligible)
+ return ipProperties.UnicastAddresses
+ //.Where(i => i.IsDnsEligible)
.Select(i => i.Address)
.Where(i => i.AddressFamily == AddressFamily.InterNetwork)
- .ToList();
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error querying network interface", ex);
- return new List<IPAddress>();
- }
-
- }).DistinctBy(i => i.ToString())
- .ToList();
- }
-
- private IEnumerable<IPAddress> GetLocalIpAddressesFallback()
+ .ToList();
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error querying network interface", ex);
+ return new List<IPAddress>();
+ }
+
+ }).DistinctBy(i => i.ToString())
+ .ToList();
+ }
+
+ private async Task<IEnumerable<IPAddress>> GetLocalIpAddressesFallback()
{
- var host = Dns.GetHostEntry(Dns.GetHostName());
+ var host = await Dns.GetHostEntryAsync(Dns.GetHostName()).ConfigureAwait(false);
// Reverse them because the last one is usually the correct one
// It's not fool-proof so ultimately the consumer will have to examine them and decide
@@ -279,7 +303,7 @@ namespace MediaBrowser.Common.Implementations.Networking
/// <returns>IPEndPoint.</returns>
public IPEndPoint Parse(string endpointstring)
{
- return Parse(endpointstring, -1);
+ return Parse(endpointstring, -1).Result;
}
/// <summary>
@@ -290,7 +314,7 @@ namespace MediaBrowser.Common.Implementations.Networking
/// <returns>IPEndPoint.</returns>
/// <exception cref="System.ArgumentException">Endpoint descriptor may not be empty.</exception>
/// <exception cref="System.FormatException"></exception>
- private static IPEndPoint Parse(string endpointstring, int defaultport)
+ private static async Task<IPEndPoint> Parse(string endpointstring, int defaultport)
{
if (String.IsNullOrEmpty(endpointstring)
|| endpointstring.Trim().Length == 0)
@@ -316,7 +340,7 @@ namespace MediaBrowser.Common.Implementations.Networking
//try to use the address as IPv4, otherwise get hostname
if (!IPAddress.TryParse(values[0], out ipaddy))
- ipaddy = GetIPfromHost(values[0]);
+ ipaddy = await GetIPfromHost(values[0]).ConfigureAwait(false);
}
else if (values.Length > 2) //ipv6
{
@@ -372,14 +396,130 @@ namespace MediaBrowser.Common.Implementations.Networking
/// <param name="p">The p.</param>
/// <returns>IPAddress.</returns>
/// <exception cref="System.ArgumentException"></exception>
- private static IPAddress GetIPfromHost(string p)
+ private static async Task<IPAddress> GetIPfromHost(string p)
{
- var hosts = Dns.GetHostAddresses(p);
+ var hosts = await Dns.GetHostAddressesAsync(p).ConfigureAwait(false);
if (hosts == null || hosts.Length == 0)
throw new ArgumentException(String.Format("Host not found: {0}", p));
return hosts[0];
}
+
+ public IpAddressInfo ParseIpAddress(string ipAddress)
+ {
+ IpAddressInfo info;
+ if (TryParseIpAddress(ipAddress, out info))
+ {
+ return info;
+ }
+
+ throw new ArgumentException("Invalid ip address: " + ipAddress);
+ }
+
+ public bool TryParseIpAddress(string ipAddress, out IpAddressInfo ipAddressInfo)
+ {
+ IPAddress address;
+ if (IPAddress.TryParse(ipAddress, out address))
+ {
+ ipAddressInfo = ToIpAddressInfo(address);
+ return true;
+ }
+
+ ipAddressInfo = null;
+ return false;
+ }
+
+ public static IpEndPointInfo ToIpEndPointInfo(IPEndPoint endpoint)
+ {
+ if (endpoint == null)
+ {
+ return null;
+ }
+
+ return new IpEndPointInfo(ToIpAddressInfo(endpoint.Address), endpoint.Port);
+ }
+
+ public static IPEndPoint ToIPEndPoint(IpEndPointInfo endpoint)
+ {
+ if (endpoint == null)
+ {
+ return null;
+ }
+
+ return new IPEndPoint(ToIPAddress(endpoint.IpAddress), endpoint.Port);
+ }
+
+ public static IPAddress ToIPAddress(IpAddressInfo address)
+ {
+ if (address.Equals(IpAddressInfo.Any))
+ {
+ return IPAddress.Any;
+ }
+ if (address.Equals(IpAddressInfo.IPv6Any))
+ {
+ return IPAddress.IPv6Any;
+ }
+ if (address.Equals(IpAddressInfo.Loopback))
+ {
+ return IPAddress.Loopback;
+ }
+ if (address.Equals(IpAddressInfo.IPv6Loopback))
+ {
+ return IPAddress.IPv6Loopback;
+ }
+
+ return IPAddress.Parse(address.Address);
+ }
+
+ public static IpAddressInfo ToIpAddressInfo(IPAddress address)
+ {
+ if (address.Equals(IPAddress.Any))
+ {
+ return IpAddressInfo.Any;
+ }
+ if (address.Equals(IPAddress.IPv6Any))
+ {
+ return IpAddressInfo.IPv6Any;
+ }
+ if (address.Equals(IPAddress.Loopback))
+ {
+ return IpAddressInfo.Loopback;
+ }
+ if (address.Equals(IPAddress.IPv6Loopback))
+ {
+ return IpAddressInfo.IPv6Loopback;
+ }
+ return new IpAddressInfo
+ {
+ Address = address.ToString(),
+ AddressFamily = address.AddressFamily == AddressFamily.InterNetworkV6 ? IpAddressFamily.InterNetworkV6 : IpAddressFamily.InterNetwork
+ };
+ }
+
+ public async Task<IpAddressInfo[]> GetHostAddressesAsync(string host)
+ {
+ var addresses = await Dns.GetHostAddressesAsync(host).ConfigureAwait(false);
+ return addresses.Select(ToIpAddressInfo).ToArray();
+ }
+
+ /// <summary>
+ /// Gets the network shares.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ /// <returns>IEnumerable{NetworkShare}.</returns>
+ public virtual IEnumerable<NetworkShare> GetNetworkShares(string path)
+ {
+ return new List<NetworkShare>();
+ }
+
+ /// <summary>
+ /// Gets available devices within the domain
+ /// </summary>
+ /// <returns>PC's in the Domain</returns>
+ public virtual IEnumerable<FileSystemEntryInfo> GetNetworkDevices()
+ {
+ return new List<FileSystemEntryInfo>();
+ }
}
}
diff --git a/Emby.Common.Implementations/Properties/AssemblyInfo.cs b/Emby.Common.Implementations/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..1a5abcb274
--- /dev/null
+++ b/Emby.Common.Implementations/Properties/AssemblyInfo.cs
@@ -0,0 +1,19 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Emby.Common.Implementations")]
+[assembly: AssemblyTrademark("")]
+
+// 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("5a27010a-09c6-4e86-93ea-437484c10917")]
diff --git a/Emby.Common.Implementations/Reflection/AssemblyInfo.cs b/Emby.Common.Implementations/Reflection/AssemblyInfo.cs
new file mode 100644
index 0000000000..7a92f02d6b
--- /dev/null
+++ b/Emby.Common.Implementations/Reflection/AssemblyInfo.cs
@@ -0,0 +1,31 @@
+using System;
+using System.IO;
+using MediaBrowser.Model.Reflection;
+using System.Reflection;
+
+namespace Emby.Common.Implementations.Reflection
+{
+ public class AssemblyInfo : IAssemblyInfo
+ {
+ public Stream GetManifestResourceStream(Type type, string resource)
+ {
+#if NET46
+ return type.Assembly.GetManifestResourceStream(resource);
+#endif
+ return type.GetTypeInfo().Assembly.GetManifestResourceStream(resource);
+ }
+
+ public string[] GetManifestResourceNames(Type type)
+ {
+#if NET46
+ return type.Assembly.GetManifestResourceNames();
+#endif
+ return type.GetTypeInfo().Assembly.GetManifestResourceNames();
+ }
+
+ public Assembly[] GetCurrentAssemblies()
+ {
+ return AppDomain.CurrentDomain.GetAssemblies();
+ }
+ }
+}
diff --git a/MediaBrowser.Common/ScheduledTasks/DailyTrigger.cs b/Emby.Common.Implementations/ScheduledTasks/DailyTrigger.cs
index 3d33e958de..5735f80260 100644
--- a/MediaBrowser.Common/ScheduledTasks/DailyTrigger.cs
+++ b/Emby.Common.Implementations/ScheduledTasks/DailyTrigger.cs
@@ -1,11 +1,11 @@
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Tasks;
-using System;
+using System;
using System.Globalization;
using System.Threading;
+using MediaBrowser.Model.Events;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Common.ScheduledTasks
+namespace Emby.Common.Implementations.ScheduledTasks
{
/// <summary>
/// Represents a task trigger that fires everyday
diff --git a/MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs b/Emby.Common.Implementations/ScheduledTasks/IntervalTrigger.cs
index 8038d5551d..4d2769d8fb 100644
--- a/MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs
+++ b/Emby.Common.Implementations/ScheduledTasks/IntervalTrigger.cs
@@ -1,11 +1,11 @@
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Tasks;
-using System;
+using System;
using System.Linq;
using System.Threading;
+using MediaBrowser.Model.Events;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Common.ScheduledTasks
+namespace Emby.Common.Implementations.ScheduledTasks
{
/// <summary>
/// Represents a task trigger that runs repeatedly on an interval
diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
index ced85780f8..cbc7c7c2d8 100644
--- a/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
+++ b/Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
@@ -1,20 +1,20 @@
-using MediaBrowser.Common.Configuration;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Model.Events;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.System;
using MediaBrowser.Model.Tasks;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using CommonIO;
-namespace MediaBrowser.Common.Implementations.ScheduledTasks
+namespace Emby.Common.Implementations.ScheduledTasks
{
/// <summary>
/// Class ScheduledTaskWorker
@@ -53,6 +53,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
/// <value>The task manager.</value>
private ITaskManager TaskManager { get; set; }
private readonly IFileSystem _fileSystem;
+ private readonly ISystemEvents _systemEvents;
/// <summary>
/// Initializes a new instance of the <see cref="ScheduledTaskWorker" /> class.
@@ -73,7 +74,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
/// or
/// logger
/// </exception>
- public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, IJsonSerializer jsonSerializer, ILogger logger, IFileSystem fileSystem)
+ public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, IJsonSerializer jsonSerializer, ILogger logger, IFileSystem fileSystem, ISystemEvents systemEvents)
{
if (scheduledTask == null)
{
@@ -102,6 +103,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
JsonSerializer = jsonSerializer;
Logger = logger;
_fileSystem = fileSystem;
+ _systemEvents = systemEvents;
InitTriggerEvents();
}
@@ -232,13 +234,12 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
/// <summary>
/// The _triggers
/// </summary>
- private List<ITaskTrigger> _triggers;
+ private Tuple<TaskTriggerInfo,ITaskTrigger>[] _triggers;
/// <summary>
/// Gets the triggers that define when the task will run
/// </summary>
/// <value>The triggers.</value>
- /// <exception cref="System.ArgumentNullException">value</exception>
- public IEnumerable<ITaskTrigger> Triggers
+ private Tuple<TaskTriggerInfo, ITaskTrigger>[] InternalTriggers
{
get
{
@@ -257,11 +258,33 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
DisposeTriggers();
}
- _triggers = value.ToList();
+ _triggers = value.ToArray();
ReloadTriggerEvents(false);
+ }
+ }
+
+ /// <summary>
+ /// Gets the triggers that define when the task will run
+ /// </summary>
+ /// <value>The triggers.</value>
+ /// <exception cref="System.ArgumentNullException">value</exception>
+ public TaskTriggerInfo[] Triggers
+ {
+ get
+ {
+ return InternalTriggers.Select(i => i.Item1).ToArray();
+ }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException("value");
+ }
+
+ SaveTriggers(value);
- SaveTriggers(_triggers);
+ InternalTriggers = value.Select(i => new Tuple<TaskTriggerInfo, ITaskTrigger>(i, GetTrigger(i))).ToArray();
}
}
@@ -304,8 +327,10 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
/// <param name="isApplicationStartup">if set to <c>true</c> [is application startup].</param>
private void ReloadTriggerEvents(bool isApplicationStartup)
{
- foreach (var trigger in Triggers)
+ foreach (var triggerInfo in InternalTriggers)
{
+ var trigger = triggerInfo.Item2;
+
trigger.Stop();
trigger.Triggered -= trigger_Triggered;
@@ -362,6 +387,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
finally
{
_currentTask = null;
+ GC.Collect();
}
}
@@ -507,23 +533,29 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
/// Loads the triggers.
/// </summary>
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
- private List<ITaskTrigger> LoadTriggers()
+ private Tuple<TaskTriggerInfo, ITaskTrigger>[] LoadTriggers()
+ {
+ var settings = LoadTriggerSettings();
+
+ return settings.Select(i => new Tuple<TaskTriggerInfo, ITaskTrigger>(i, GetTrigger(i))).ToArray();
+ }
+
+ private TaskTriggerInfo[] LoadTriggerSettings()
{
try
{
return JsonSerializer.DeserializeFromFile<IEnumerable<TaskTriggerInfo>>(GetConfigurationFilePath())
- .Select(ScheduledTaskHelpers.GetTrigger)
- .ToList();
+ .ToArray();
}
catch (FileNotFoundException)
{
// File doesn't exist. No biggie. Return defaults.
- return ScheduledTask.GetDefaultTriggers().ToList();
+ return ScheduledTask.GetDefaultTriggers().ToArray();
}
catch (DirectoryNotFoundException)
{
// File doesn't exist. No biggie. Return defaults.
- return ScheduledTask.GetDefaultTriggers().ToList();
+ return ScheduledTask.GetDefaultTriggers().ToArray();
}
}
@@ -531,13 +563,13 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
/// Saves the triggers.
/// </summary>
/// <param name="triggers">The triggers.</param>
- private void SaveTriggers(IEnumerable<ITaskTrigger> triggers)
+ private void SaveTriggers(TaskTriggerInfo[] triggers)
{
var path = GetConfigurationFilePath();
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
- JsonSerializer.SerializeToFile(triggers.Select(ScheduledTaskHelpers.GetTriggerInfo), path);
+ JsonSerializer.SerializeToFile(triggers, path);
}
/// <summary>
@@ -561,11 +593,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
Id = Id
};
- var hasKey = ScheduledTask as IHasKey;
- if (hasKey != null)
- {
- result.Key = hasKey.Key;
- }
+ result.Key = ScheduledTask.Key;
if (ex != null)
{
@@ -656,12 +684,97 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
}
/// <summary>
+ /// Converts a TaskTriggerInfo into a concrete BaseTaskTrigger
+ /// </summary>
+ /// <param name="info">The info.</param>
+ /// <returns>BaseTaskTrigger.</returns>
+ /// <exception cref="System.ArgumentNullException"></exception>
+ /// <exception cref="System.ArgumentException">Invalid trigger type: + info.Type</exception>
+ private ITaskTrigger GetTrigger(TaskTriggerInfo info)
+ {
+ var options = new TaskExecutionOptions
+ {
+ MaxRuntimeMs = info.MaxRuntimeMs
+ };
+
+ if (info.Type.Equals(typeof(DailyTrigger).Name, StringComparison.OrdinalIgnoreCase))
+ {
+ if (!info.TimeOfDayTicks.HasValue)
+ {
+ throw new ArgumentNullException();
+ }
+
+ return new DailyTrigger
+ {
+ TimeOfDay = TimeSpan.FromTicks(info.TimeOfDayTicks.Value),
+ TaskOptions = options
+ };
+ }
+
+ if (info.Type.Equals(typeof(WeeklyTrigger).Name, StringComparison.OrdinalIgnoreCase))
+ {
+ if (!info.TimeOfDayTicks.HasValue)
+ {
+ throw new ArgumentNullException();
+ }
+
+ if (!info.DayOfWeek.HasValue)
+ {
+ throw new ArgumentNullException();
+ }
+
+ return new WeeklyTrigger
+ {
+ TimeOfDay = TimeSpan.FromTicks(info.TimeOfDayTicks.Value),
+ DayOfWeek = info.DayOfWeek.Value,
+ TaskOptions = options
+ };
+ }
+
+ if (info.Type.Equals(typeof(IntervalTrigger).Name, StringComparison.OrdinalIgnoreCase))
+ {
+ if (!info.IntervalTicks.HasValue)
+ {
+ throw new ArgumentNullException();
+ }
+
+ return new IntervalTrigger
+ {
+ Interval = TimeSpan.FromTicks(info.IntervalTicks.Value),
+ TaskOptions = options
+ };
+ }
+
+ if (info.Type.Equals(typeof(SystemEventTrigger).Name, StringComparison.OrdinalIgnoreCase))
+ {
+ if (!info.SystemEvent.HasValue)
+ {
+ throw new ArgumentNullException();
+ }
+
+ return new SystemEventTrigger(_systemEvents)
+ {
+ SystemEvent = info.SystemEvent.Value,
+ TaskOptions = options
+ };
+ }
+
+ if (info.Type.Equals(typeof(StartupTrigger).Name, StringComparison.OrdinalIgnoreCase))
+ {
+ return new StartupTrigger();
+ }
+
+ throw new ArgumentException("Unrecognized trigger type: " + info.Type);
+ }
+
+ /// <summary>
/// Disposes each trigger
/// </summary>
private void DisposeTriggers()
{
- foreach (var trigger in Triggers)
+ foreach (var triggerInfo in InternalTriggers)
{
+ var trigger = triggerInfo.Item2;
trigger.Triggered -= trigger_Triggered;
trigger.Stop();
}
diff --git a/MediaBrowser.Common/ScheduledTasks/StartupTrigger.cs b/Emby.Common.Implementations/ScheduledTasks/StartupTrigger.cs
index 41f58a7ad5..8aae644bc9 100644
--- a/MediaBrowser.Common/ScheduledTasks/StartupTrigger.cs
+++ b/Emby.Common.Implementations/ScheduledTasks/StartupTrigger.cs
@@ -1,10 +1,10 @@
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Tasks;
-using System;
+using System;
using System.Threading.Tasks;
+using MediaBrowser.Model.Events;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Common.ScheduledTasks
+namespace Emby.Common.Implementations.ScheduledTasks
{
/// <summary>
/// Class StartupTaskTrigger
diff --git a/MediaBrowser.Common/ScheduledTasks/SystemEventTrigger.cs b/Emby.Common.Implementations/ScheduledTasks/SystemEventTrigger.cs
index 9972dc8044..a136a975ae 100644
--- a/MediaBrowser.Common/ScheduledTasks/SystemEventTrigger.cs
+++ b/Emby.Common.Implementations/ScheduledTasks/SystemEventTrigger.cs
@@ -1,11 +1,11 @@
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Tasks;
-using Microsoft.Win32;
-using System;
+using System;
using System.Threading.Tasks;
+using MediaBrowser.Model.Events;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.System;
+using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Common.ScheduledTasks
+namespace Emby.Common.Implementations.ScheduledTasks
{
/// <summary>
/// Class SystemEventTrigger
@@ -26,6 +26,13 @@ namespace MediaBrowser.Common.ScheduledTasks
/// </value>
public TaskExecutionOptions TaskOptions { get; set; }
+ private readonly ISystemEvents _systemEvents;
+
+ public SystemEventTrigger(ISystemEvents systemEvents)
+ {
+ _systemEvents = systemEvents;
+ }
+
/// <summary>
/// Stars waiting for the trigger action
/// </summary>
@@ -36,27 +43,14 @@ namespace MediaBrowser.Common.ScheduledTasks
switch (SystemEvent)
{
case SystemEvent.WakeFromSleep:
- SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
+ _systemEvents.Resume += _systemEvents_Resume;
break;
}
}
- /// <summary>
- /// Stops waiting for the trigger action
- /// </summary>
- public void Stop()
- {
- SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
- }
-
- /// <summary>
- /// Handles the PowerModeChanged event of the SystemEvents control.
- /// </summary>
- /// <param name="sender">The source of the event.</param>
- /// <param name="e">The <see cref="PowerModeChangedEventArgs" /> instance containing the event data.</param>
- async void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
+ private async void _systemEvents_Resume(object sender, EventArgs e)
{
- if (e.Mode == PowerModes.Resume && SystemEvent == SystemEvent.WakeFromSleep)
+ if (SystemEvent == SystemEvent.WakeFromSleep)
{
// This value is a bit arbitrary, but add a delay to help ensure network connections have been restored before running the task
await Task.Delay(10000).ConfigureAwait(false);
@@ -66,6 +60,14 @@ namespace MediaBrowser.Common.ScheduledTasks
}
/// <summary>
+ /// Stops waiting for the trigger action
+ /// </summary>
+ public void Stop()
+ {
+ _systemEvents.Resume -= _systemEvents_Resume;
+ }
+
+ /// <summary>
/// Occurs when [triggered].
/// </summary>
public event EventHandler<GenericEventArgs<TaskExecutionOptions>> Triggered;
diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs b/Emby.Common.Implementations/ScheduledTasks/TaskManager.cs
index b3a00b35f5..b0153c5882 100644
--- a/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs
+++ b/Emby.Common.Implementations/ScheduledTasks/TaskManager.cs
@@ -1,6 +1,5 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events;
-using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
@@ -10,10 +9,10 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-using CommonIO;
-using Microsoft.Win32;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.System;
-namespace MediaBrowser.Common.Implementations.ScheduledTasks
+namespace Emby.Common.Implementations.ScheduledTasks
{
/// <summary>
/// Class TaskManager
@@ -47,6 +46,8 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
/// <value>The application paths.</value>
private IApplicationPaths ApplicationPaths { get; set; }
+ private readonly ISystemEvents _systemEvents;
+
/// <summary>
/// Gets the logger.
/// </summary>
@@ -54,25 +55,6 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
private ILogger Logger { get; set; }
private readonly IFileSystem _fileSystem;
- private bool _suspendTriggers;
-
- public bool SuspendTriggers
- {
- get { return _suspendTriggers; }
- set
- {
- Logger.Info("Setting SuspendTriggers to {0}", value);
- var executeQueued = _suspendTriggers && !value;
-
- _suspendTriggers = value;
-
- if (executeQueued)
- {
- ExecuteQueuedTasks();
- }
- }
- }
-
/// <summary>
/// Initializes a new instance of the <see cref="TaskManager" /> class.
/// </summary>
@@ -80,29 +62,23 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
/// <param name="jsonSerializer">The json serializer.</param>
/// <param name="logger">The logger.</param>
/// <exception cref="System.ArgumentException">kernel</exception>
- public TaskManager(IApplicationPaths applicationPaths, IJsonSerializer jsonSerializer, ILogger logger, IFileSystem fileSystem)
+ public TaskManager(IApplicationPaths applicationPaths, IJsonSerializer jsonSerializer, ILogger logger, IFileSystem fileSystem, ISystemEvents systemEvents)
{
ApplicationPaths = applicationPaths;
JsonSerializer = jsonSerializer;
Logger = logger;
_fileSystem = fileSystem;
+ _systemEvents = systemEvents;
ScheduledTasks = new IScheduledTaskWorker[] { };
}
private void BindToSystemEvent()
{
- try
- {
- SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
- }
- catch
- {
-
- }
+ _systemEvents.Resume += _systemEvents_Resume;
}
- void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
+ private void _systemEvents_Resume(object sender, EventArgs e)
{
foreach (var task in ScheduledTasks)
{
@@ -235,7 +211,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
lock (_taskQueue)
{
- if (task.State == TaskState.Idle && !SuspendTriggers)
+ if (task.State == TaskState.Idle)
{
Execute(task, options);
return;
@@ -254,7 +230,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
var myTasks = ScheduledTasks.ToList();
var list = tasks.ToList();
- myTasks.AddRange(list.Select(t => new ScheduledTaskWorker(t, ApplicationPaths, this, JsonSerializer, Logger, _fileSystem)));
+ myTasks.AddRange(list.Select(t => new ScheduledTaskWorker(t, ApplicationPaths, this, JsonSerializer, Logger, _fileSystem, _systemEvents)));
ScheduledTasks = myTasks.ToArray();
@@ -327,11 +303,6 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
/// </summary>
private void ExecuteQueuedTasks()
{
- if (SuspendTriggers)
- {
- return;
- }
-
Logger.Info("ExecuteQueuedTasks");
// Execute queued tasks
diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/Emby.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
index 0a2b9222a0..1cad2e9b83 100644
--- a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
+++ b/Emby.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
@@ -1,15 +1,15 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.ScheduledTasks;
-using MediaBrowser.Model.Logging;
-using System;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
+namespace Emby.Common.Implementations.ScheduledTasks.Tasks
{
/// <summary>
/// Deletes old cache files
@@ -40,13 +40,12 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
/// Creates the triggers that define when the task will run
/// </summary>
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
- public IEnumerable<ITaskTrigger> GetDefaultTriggers()
+ public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- // Until we can vary these default triggers per server and MBT, we need something that makes sense for both
- return new ITaskTrigger[] {
+ return new[] {
// Every so often
- new IntervalTrigger { Interval = TimeSpan.FromHours(24)}
+ new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
};
}
@@ -95,7 +94,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
/// <param name="progress">The progress.</param>
private void DeleteCacheFilesFromDirectory(CancellationToken cancellationToken, string directory, DateTime minDateModified, IProgress<double> progress)
{
- var filesToDelete = _fileSystem.GetFiles(directory, true)
+ var filesToDelete = _fileSystem.GetFiles(directory, true)
.Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
.ToList();
@@ -168,6 +167,11 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
get { return "Cache file cleanup"; }
}
+ public string Key
+ {
+ get { return "DeleteCacheFiles"; }
+ }
+
/// <summary>
/// Gets the description.
/// </summary>
@@ -202,5 +206,10 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
{
get { return true; }
}
+
+ public bool IsLogged
+ {
+ get { return true; }
+ }
}
}
diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/Emby.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
index b18ea03b1e..3f43fa8894 100644
--- a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
+++ b/Emby.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
@@ -1,13 +1,13 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.ScheduledTasks;
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
+namespace Emby.Common.Implementations.ScheduledTasks.Tasks
{
/// <summary>
/// Deletes old log files
@@ -36,13 +36,12 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
/// Creates the triggers that define when the task will run
/// </summary>
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
- public IEnumerable<ITaskTrigger> GetDefaultTriggers()
+ public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- // Until we can vary these default triggers per server and MBT, we need something that makes sense for both
- return new ITaskTrigger[] {
+ return new[] {
// Every so often
- new IntervalTrigger { Interval = TimeSpan.FromHours(24)}
+ new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
};
}
@@ -82,6 +81,11 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
return Task.FromResult(true);
}
+ public string Key
+ {
+ get { return "CleanLogFiles"; }
+ }
+
/// <summary>
/// Gets the name of the task
/// </summary>
@@ -125,5 +129,10 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
{
get { return true; }
}
+
+ public bool IsLogged
+ {
+ get { return true; }
+ }
}
}
diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/ReloadLoggerFileTask.cs b/Emby.Common.Implementations/ScheduledTasks/Tasks/ReloadLoggerFileTask.cs
index 78f60632fa..80411de055 100644
--- a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/ReloadLoggerFileTask.cs
+++ b/Emby.Common.Implementations/ScheduledTasks/Tasks/ReloadLoggerFileTask.cs
@@ -1,12 +1,12 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.ScheduledTasks;
-using MediaBrowser.Model.Logging;
-using System;
+using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
+namespace Emby.Common.Implementations.ScheduledTasks.Tasks
{
/// <summary>
/// Class ReloadLoggerFileTask
@@ -39,9 +39,9 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
/// Gets the default triggers.
/// </summary>
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
- public IEnumerable<ITaskTrigger> GetDefaultTriggers()
+ public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- var trigger = new DailyTrigger { TimeOfDay = TimeSpan.FromHours(0) }; //12am
+ var trigger = new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerDaily, TimeOfDayTicks = TimeSpan.FromHours(0).Ticks }; //12am
return new[] { trigger };
}
@@ -74,6 +74,8 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
get { return "Start new log file"; }
}
+ public string Key { get; }
+
/// <summary>
/// Gets the description.
/// </summary>
@@ -101,5 +103,10 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
{
get { return true; }
}
+
+ public bool IsLogged
+ {
+ get { return true; }
+ }
}
}
diff --git a/MediaBrowser.Common/ScheduledTasks/WeeklyTrigger.cs b/Emby.Common.Implementations/ScheduledTasks/WeeklyTrigger.cs
index 318802e07d..91540ba164 100644
--- a/MediaBrowser.Common/ScheduledTasks/WeeklyTrigger.cs
+++ b/Emby.Common.Implementations/ScheduledTasks/WeeklyTrigger.cs
@@ -4,7 +4,7 @@ using MediaBrowser.Model.Events;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Common.ScheduledTasks
+namespace Emby.Common.Implementations.ScheduledTasks
{
/// <summary>
/// Represents a task trigger that fires on a weekly basis
diff --git a/MediaBrowser.Common.Implementations/Serialization/JsonSerializer.cs b/Emby.Common.Implementations/Serialization/JsonSerializer.cs
index 5dbbe53731..c9db336890 100644
--- a/MediaBrowser.Common.Implementations/Serialization/JsonSerializer.cs
+++ b/Emby.Common.Implementations/Serialization/JsonSerializer.cs
@@ -1,10 +1,10 @@
-using MediaBrowser.Model.Serialization;
-using System;
+using System;
using System.IO;
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Serialization;
-namespace MediaBrowser.Common.Implementations.Serialization
+namespace Emby.Common.Implementations.Serialization
{
/// <summary>
/// Provides a wrapper around third party json serialization.
@@ -60,7 +60,7 @@ namespace MediaBrowser.Common.Implementations.Serialization
throw new ArgumentNullException("file");
}
- using (Stream stream = _fileSystem.GetFileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read))
+ using (Stream stream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
{
SerializeToStream(obj, stream);
}
diff --git a/MediaBrowser.Common.Implementations/Serialization/XmlSerializer.cs b/Emby.Common.Implementations/Serialization/XmlSerializer.cs
index 77f65b0c7a..3583f998e5 100644
--- a/MediaBrowser.Common.Implementations/Serialization/XmlSerializer.cs
+++ b/Emby.Common.Implementations/Serialization/XmlSerializer.cs
@@ -4,20 +4,22 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Xml;
-using CommonIO;
+using System.Xml.Serialization;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
-namespace MediaBrowser.Common.Implementations.Serialization
+namespace Emby.Common.Implementations.Serialization
{
/// <summary>
/// Provides a wrapper around third party xml serialization.
/// </summary>
- public class XmlSerializer : IXmlSerializer
+ public class MyXmlSerializer : IXmlSerializer
{
- private readonly IFileSystem _fileSystem;
+ private readonly IFileSystem _fileSystem;
private readonly ILogger _logger;
- public XmlSerializer(IFileSystem fileSystem, ILogger logger)
+ public MyXmlSerializer(IFileSystem fileSystem, ILogger logger)
{
_fileSystem = fileSystem;
_logger = logger;
@@ -25,18 +27,18 @@ namespace MediaBrowser.Common.Implementations.Serialization
// Need to cache these
// http://dotnetcodebox.blogspot.com/2013/01/xmlserializer-class-may-result-in.html
- private readonly Dictionary<string, System.Xml.Serialization.XmlSerializer> _serializers =
- new Dictionary<string, System.Xml.Serialization.XmlSerializer>();
+ private readonly Dictionary<string, XmlSerializer> _serializers =
+ new Dictionary<string, XmlSerializer>();
- private System.Xml.Serialization.XmlSerializer GetSerializer(Type type)
+ private XmlSerializer GetSerializer(Type type)
{
var key = type.FullName;
lock (_serializers)
{
- System.Xml.Serialization.XmlSerializer serializer;
+ XmlSerializer serializer;
if (!_serializers.TryGetValue(key, out serializer))
{
- serializer = new System.Xml.Serialization.XmlSerializer(type);
+ serializer = new XmlSerializer(type);
_serializers[key] = serializer;
}
return serializer;
@@ -48,9 +50,8 @@ namespace MediaBrowser.Common.Implementations.Serialization
/// </summary>
/// <param name="obj">The obj.</param>
/// <param name="writer">The writer.</param>
- private void SerializeToWriter(object obj, XmlTextWriter writer)
+ private void SerializeToWriter(object obj, XmlWriter writer)
{
- writer.Formatting = Formatting.Indented;
var netSerializer = GetSerializer(obj.GetType());
netSerializer.Serialize(writer, obj);
}
@@ -63,7 +64,7 @@ namespace MediaBrowser.Common.Implementations.Serialization
/// <returns>System.Object.</returns>
public object DeserializeFromStream(Type type, Stream stream)
{
- using (var reader = new XmlTextReader(stream))
+ using (var reader = XmlReader.Create(stream))
{
var netSerializer = GetSerializer(type);
return netSerializer.Deserialize(reader);
@@ -77,10 +78,18 @@ namespace MediaBrowser.Common.Implementations.Serialization
/// <param name="stream">The stream.</param>
public void SerializeToStream(object obj, Stream stream)
{
- using (var writer = new XmlTextWriter(stream, null))
+#if NET46
+ using (var writer = new XmlTextWriter(stream, null))
{
+ writer.Formatting = Formatting.Indented;
SerializeToWriter(obj, writer);
}
+#else
+ using (var writer = XmlWriter.Create(stream))
+ {
+ SerializeToWriter(obj, writer);
+ }
+#endif
}
/// <summary>
diff --git a/Emby.Common.Implementations/TextEncoding/TextEncoding.cs b/Emby.Common.Implementations/TextEncoding/TextEncoding.cs
new file mode 100644
index 0000000000..254d352224
--- /dev/null
+++ b/Emby.Common.Implementations/TextEncoding/TextEncoding.cs
@@ -0,0 +1,43 @@
+using System.Text;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Text;
+
+namespace Emby.Common.Implementations.TextEncoding
+{
+ public class TextEncoding : ITextEncoding
+ {
+ private readonly IFileSystem _fileSystem;
+
+ public TextEncoding(IFileSystem fileSystem)
+ {
+ _fileSystem = fileSystem;
+ }
+
+ public Encoding GetASCIIEncoding()
+ {
+ return Encoding.ASCII;
+ }
+
+ public Encoding GetFileEncoding(string srcFile)
+ {
+ // *** Detect byte order mark if any - otherwise assume default
+ var buffer = new byte[5];
+
+ using (var file = _fileSystem.OpenRead(srcFile))
+ {
+ file.Read(buffer, 0, 5);
+ }
+
+ if (buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf)
+ return Encoding.UTF8;
+ if (buffer[0] == 0xfe && buffer[1] == 0xff)
+ return Encoding.Unicode;
+ if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0xfe && buffer[3] == 0xff)
+ return Encoding.UTF32;
+ if (buffer[0] == 0x2b && buffer[1] == 0x2f && buffer[2] == 0x76)
+ return Encoding.UTF7;
+
+ return null;
+ }
+ }
+}
diff --git a/Emby.Common.Implementations/Threading/CommonTimer.cs b/Emby.Common.Implementations/Threading/CommonTimer.cs
new file mode 100644
index 0000000000..8895f6798a
--- /dev/null
+++ b/Emby.Common.Implementations/Threading/CommonTimer.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Threading;
+
+namespace Emby.Common.Implementations.Threading
+{
+ public class CommonTimer : ITimer
+ {
+ private readonly Timer _timer;
+
+ public CommonTimer(Action<object> callback, object state, TimeSpan dueTime, TimeSpan period)
+ {
+ _timer = new Timer(new TimerCallback(callback), state, dueTime, period);
+ }
+
+ public CommonTimer(Action<object> callback, object state, int dueTimeMs, int periodMs)
+ {
+ _timer = new Timer(new TimerCallback(callback), state, dueTimeMs, periodMs);
+ }
+
+ public void Change(TimeSpan dueTime, TimeSpan period)
+ {
+ _timer.Change(dueTime, period);
+ }
+
+ public void Change(int dueTimeMs, int periodMs)
+ {
+ _timer.Change(dueTimeMs, periodMs);
+ }
+
+ public void Dispose()
+ {
+ _timer.Dispose();
+ }
+ }
+}
diff --git a/Emby.Common.Implementations/Threading/TimerFactory.cs b/Emby.Common.Implementations/Threading/TimerFactory.cs
new file mode 100644
index 0000000000..028dd09639
--- /dev/null
+++ b/Emby.Common.Implementations/Threading/TimerFactory.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Threading;
+
+namespace Emby.Common.Implementations.Threading
+{
+ public class TimerFactory : ITimerFactory
+ {
+ public ITimer Create(Action<object> callback, object state, TimeSpan dueTime, TimeSpan period)
+ {
+ return new CommonTimer(callback, state, dueTime, period);
+ }
+
+ public ITimer Create(Action<object> callback, object state, int dueTimeMs, int periodMs)
+ {
+ return new CommonTimer(callback, state, dueTimeMs, periodMs);
+ }
+ }
+}
diff --git a/Emby.Common.Implementations/Xml/XmlReaderSettingsFactory.cs b/Emby.Common.Implementations/Xml/XmlReaderSettingsFactory.cs
new file mode 100644
index 0000000000..806290cf44
--- /dev/null
+++ b/Emby.Common.Implementations/Xml/XmlReaderSettingsFactory.cs
@@ -0,0 +1,22 @@
+using System.Xml;
+using MediaBrowser.Model.Xml;
+
+namespace Emby.Common.Implementations.Xml
+{
+ public class XmlReaderSettingsFactory : IXmlReaderSettingsFactory
+ {
+ public XmlReaderSettings Create(bool enableValidation)
+ {
+ var settings = new XmlReaderSettings();
+
+ if (!enableValidation)
+ {
+#if NET46
+ settings.ValidationType = ValidationType.None;
+#endif
+ }
+
+ return settings;
+ }
+ }
+}
diff --git a/Emby.Common.Implementations/project.json b/Emby.Common.Implementations/project.json
new file mode 100644
index 0000000000..674101e8a7
--- /dev/null
+++ b/Emby.Common.Implementations/project.json
@@ -0,0 +1,71 @@
+{
+ "version": "1.0.0-*",
+
+ "dependencies": {
+
+ },
+
+ "frameworks": {
+ "net46": {
+ "frameworkAssemblies": {
+ "System.Collections": "4.0.0.0",
+ "System.IO": "4.0.0.0",
+ "System.Net": "4.0.0.0",
+ "System.Net.Http": "4.0.0.0",
+ "System.Net.Primitives": "4.0.0.0",
+ "System.Net.Http.WebRequest": "4.0.0.0",
+ "System.Reflection": "4.0.0.0",
+ "System.Runtime": "4.0.0.0",
+ "System.Runtime.Extensions": "4.0.0.0",
+ "System.Text.Encoding": "4.0.0.0",
+ "System.Threading": "4.0.0.0",
+ "System.Threading.Tasks": "4.0.0.0",
+ "System.Xml.ReaderWriter": "4.0.0"
+ },
+ "dependencies": {
+ "SimpleInjector": "3.2.4",
+ "ServiceStack.Text": "4.5.4",
+ "NLog": "4.4.0-betaV15",
+ "sharpcompress": "0.14.0",
+ "MediaBrowser.Model": {
+ "target": "project"
+ },
+ "MediaBrowser.Common": {
+ "target": "project"
+ }
+ }
+ },
+ "netstandard1.6": {
+ "imports": "dnxcore50",
+ "dependencies": {
+ "NETStandard.Library": "1.6.1",
+ "System.IO.FileSystem.DriveInfo": "4.3.0",
+ "System.Diagnostics.Process": "4.3.0",
+ "System.Threading.Timer": "4.3.0",
+ "System.Net.Requests": "4.3.0",
+ "System.Xml.ReaderWriter": "4.3.0",
+ "System.Xml.XmlSerializer": "4.3.0",
+ "System.Net.Http": "4.3.0",
+ "System.Net.Primitives": "4.3.0",
+ "System.Net.Sockets": "4.3.0",
+ "System.Net.NetworkInformation": "4.3.0",
+ "System.Net.NameResolution": "4.3.0",
+ "System.Runtime.InteropServices.RuntimeInformation": "4.3.0",
+ "System.Reflection": "4.3.0",
+ "System.Reflection.Primitives": "4.3.0",
+ "System.Runtime.Loader": "4.3.0",
+ "SimpleInjector": "3.2.4",
+ "ServiceStack.Text.Core": "1.0.27",
+ "NLog": "4.4.0-betaV15",
+ "sharpcompress": "0.14.0",
+ "System.AppDomain": "2.0.11",
+ "MediaBrowser.Model": {
+ "target": "project"
+ },
+ "MediaBrowser.Common": {
+ "target": "project"
+ }
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Dlna/Common/Argument.cs b/Emby.Dlna/Common/Argument.cs
index a3ff8ecc88..7e61c3d6d5 100644
--- a/MediaBrowser.Dlna/Common/Argument.cs
+++ b/Emby.Dlna/Common/Argument.cs
@@ -1,5 +1,5 @@

-namespace MediaBrowser.Dlna.Common
+namespace Emby.Dlna.Common
{
public class Argument
{
diff --git a/MediaBrowser.Dlna/Common/DeviceIcon.cs b/Emby.Dlna/Common/DeviceIcon.cs
index bec10dcc5a..27ae92d6f0 100644
--- a/MediaBrowser.Dlna/Common/DeviceIcon.cs
+++ b/Emby.Dlna/Common/DeviceIcon.cs
@@ -1,5 +1,5 @@

-namespace MediaBrowser.Dlna.Common
+namespace Emby.Dlna.Common
{
public class DeviceIcon
{
diff --git a/MediaBrowser.Dlna/Common/DeviceService.cs b/Emby.Dlna/Common/DeviceService.cs
index 8f8b175a42..0d91dbe76d 100644
--- a/MediaBrowser.Dlna/Common/DeviceService.cs
+++ b/Emby.Dlna/Common/DeviceService.cs
@@ -1,5 +1,5 @@

-namespace MediaBrowser.Dlna.Common
+namespace Emby.Dlna.Common
{
public class DeviceService
{
diff --git a/MediaBrowser.Dlna/Common/ServiceAction.cs b/Emby.Dlna/Common/ServiceAction.cs
index 7685e217e8..1bcc6a1d68 100644
--- a/MediaBrowser.Dlna/Common/ServiceAction.cs
+++ b/Emby.Dlna/Common/ServiceAction.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
-namespace MediaBrowser.Dlna.Common
+namespace Emby.Dlna.Common
{
public class ServiceAction
{
diff --git a/MediaBrowser.Dlna/Common/StateVariable.cs b/Emby.Dlna/Common/StateVariable.cs
index 21771e7b8e..7e0bc6ae8b 100644
--- a/MediaBrowser.Dlna/Common/StateVariable.cs
+++ b/Emby.Dlna/Common/StateVariable.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
-namespace MediaBrowser.Dlna.Common
+namespace Emby.Dlna.Common
{
public class StateVariable
{
diff --git a/MediaBrowser.Dlna/ConfigurationExtension.cs b/Emby.Dlna/ConfigurationExtension.cs
index 821e21ccfb..cec885e4a7 100644
--- a/MediaBrowser.Dlna/ConfigurationExtension.cs
+++ b/Emby.Dlna/ConfigurationExtension.cs
@@ -2,7 +2,7 @@
using MediaBrowser.Model.Configuration;
using System.Collections.Generic;
-namespace MediaBrowser.Dlna
+namespace Emby.Dlna
{
public static class ConfigurationExtension
{
diff --git a/MediaBrowser.Dlna/ConnectionManager/ConnectionManager.cs b/Emby.Dlna/ConnectionManager/ConnectionManager.cs
index 62cd3904dc..3f33f3ebf1 100644
--- a/MediaBrowser.Dlna/ConnectionManager/ConnectionManager.cs
+++ b/Emby.Dlna/ConnectionManager/ConnectionManager.cs
@@ -1,24 +1,27 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Dlna.Service;
+using Emby.Dlna.Service;
using MediaBrowser.Model.Logging;
using System.Collections.Generic;
+using MediaBrowser.Model.Xml;
-namespace MediaBrowser.Dlna.ConnectionManager
+namespace Emby.Dlna.ConnectionManager
{
public class ConnectionManager : BaseService, IConnectionManager
{
private readonly IDlnaManager _dlna;
private readonly ILogger _logger;
private readonly IServerConfigurationManager _config;
+ protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory;
- public ConnectionManager(IDlnaManager dlna, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient)
+ public ConnectionManager(IDlnaManager dlna, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
: base(logger, httpClient)
{
_dlna = dlna;
_config = config;
_logger = logger;
+ XmlReaderSettingsFactory = xmlReaderSettingsFactory;
}
public string GetServiceXml(IDictionary<string, string> headers)
@@ -31,7 +34,7 @@ namespace MediaBrowser.Dlna.ConnectionManager
var profile = _dlna.GetProfile(request.Headers) ??
_dlna.GetDefaultProfile();
- return new ControlHandler(_logger, profile, _config).ProcessControlRequest(request);
+ return new ControlHandler(_config, _logger, XmlReaderSettingsFactory, profile).ProcessControlRequest(request);
}
}
}
diff --git a/MediaBrowser.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs b/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs
index 4efa111591..2a415c58e0 100644
--- a/MediaBrowser.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs
+++ b/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs
@@ -1,8 +1,8 @@
-using MediaBrowser.Dlna.Common;
-using MediaBrowser.Dlna.Service;
+using Emby.Dlna.Common;
+using Emby.Dlna.Service;
using System.Collections.Generic;
-namespace MediaBrowser.Dlna.ConnectionManager
+namespace Emby.Dlna.ConnectionManager
{
public class ConnectionManagerXmlBuilder
{
diff --git a/MediaBrowser.Dlna/ConnectionManager/ControlHandler.cs b/Emby.Dlna/ConnectionManager/ControlHandler.cs
index 958d71a2b6..ae983c5e75 100644
--- a/MediaBrowser.Dlna/ConnectionManager/ControlHandler.cs
+++ b/Emby.Dlna/ConnectionManager/ControlHandler.cs
@@ -1,25 +1,20 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Dlna.Server;
-using MediaBrowser.Dlna.Service;
+using Emby.Dlna.Server;
+using Emby.Dlna.Service;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
+using MediaBrowser.Model.Xml;
-namespace MediaBrowser.Dlna.ConnectionManager
+namespace Emby.Dlna.ConnectionManager
{
public class ControlHandler : BaseControlHandler
{
private readonly DeviceProfile _profile;
- public ControlHandler(ILogger logger, DeviceProfile profile, IServerConfigurationManager config)
- : base(config, logger)
- {
- _profile = profile;
- }
-
- protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams)
+ protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams)
{
if (string.Equals(methodName, "GetProtocolInfo", StringComparison.OrdinalIgnoreCase))
{
@@ -31,11 +26,16 @@ namespace MediaBrowser.Dlna.ConnectionManager
private IEnumerable<KeyValuePair<string, string>> HandleGetProtocolInfo()
{
- return new Headers(true)
+ return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "Source", _profile.ProtocolInfo },
{ "Sink", "" }
};
}
+
+ public ControlHandler(IServerConfigurationManager config, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory, DeviceProfile profile) : base(config, logger, xmlReaderSettingsFactory)
+ {
+ _profile = profile;
+ }
}
}
diff --git a/MediaBrowser.Dlna/ConnectionManager/ServiceActionListBuilder.cs b/Emby.Dlna/ConnectionManager/ServiceActionListBuilder.cs
index 9dbd4e0e23..9b22b77734 100644
--- a/MediaBrowser.Dlna/ConnectionManager/ServiceActionListBuilder.cs
+++ b/Emby.Dlna/ConnectionManager/ServiceActionListBuilder.cs
@@ -1,7 +1,7 @@
-using MediaBrowser.Dlna.Common;
+using Emby.Dlna.Common;
using System.Collections.Generic;
-namespace MediaBrowser.Dlna.ConnectionManager
+namespace Emby.Dlna.ConnectionManager
{
public class ServiceActionListBuilder
{
diff --git a/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs b/Emby.Dlna/ContentDirectory/ContentDirectory.cs
index 093b37df3e..4a36a16eb6 100644
--- a/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs
+++ b/Emby.Dlna/ContentDirectory/ContentDirectory.cs
@@ -5,16 +5,17 @@ using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Localization;
-using MediaBrowser.Dlna.Service;
+using Emby.Dlna.Service;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Xml;
-namespace MediaBrowser.Dlna.ContentDirectory
+namespace Emby.Dlna.ContentDirectory
{
public class ContentDirectory : BaseService, IContentDirectory, IDisposable
{
@@ -29,6 +30,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
private readonly IMediaSourceManager _mediaSourceManager;
private readonly IUserViewManager _userViewManager;
private readonly Func<IMediaEncoder> _mediaEncoder;
+ protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory;
public ContentDirectory(IDlnaManager dlna,
IUserDataManager userDataManager,
@@ -37,7 +39,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
IServerConfigurationManager config,
IUserManager userManager,
ILogger logger,
- IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, Func<IMediaEncoder> mediaEncoder)
+ IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager, Func<IMediaEncoder> mediaEncoder, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
: base(logger, httpClient)
{
_dlna = dlna;
@@ -51,6 +53,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
_mediaSourceManager = mediaSourceManager;
_userViewManager = userViewManager;
_mediaEncoder = mediaEncoder;
+ XmlReaderSettingsFactory = xmlReaderSettingsFactory;
}
private int SystemUpdateId
@@ -93,7 +96,8 @@ namespace MediaBrowser.Dlna.ContentDirectory
_channelManager,
_mediaSourceManager,
_userViewManager,
- _mediaEncoder())
+ _mediaEncoder(),
+ XmlReaderSettingsFactory)
.ProcessControlRequest(request);
}
@@ -122,7 +126,8 @@ namespace MediaBrowser.Dlna.ContentDirectory
}
// No configuration so it's going to be pretty arbitrary
- return _userManager.Users.First();
+ return _userManager.Users.FirstOrDefault(i => i.Policy.IsAdministrator) ??
+ _userManager.Users.First();
}
public void Dispose()
diff --git a/MediaBrowser.Dlna/ContentDirectory/ContentDirectoryBrowser.cs b/Emby.Dlna/ContentDirectory/ContentDirectoryBrowser.cs
index 2618873669..2b421794ab 100644
--- a/MediaBrowser.Dlna/ContentDirectory/ContentDirectoryBrowser.cs
+++ b/Emby.Dlna/ContentDirectory/ContentDirectoryBrowser.cs
@@ -10,8 +10,9 @@ using System.Security;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using Emby.Dlna.Server;
-namespace MediaBrowser.Dlna.ContentDirectory
+namespace Emby.Dlna.ContentDirectory
{
public class ContentDirectoryBrowser
{
@@ -90,7 +91,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
request.ParentId = "1";
}
- builder.AppendFormat("<ObjectID>{0}</ObjectID>", SecurityElement.Escape(request.ParentId));
+ builder.AppendFormat("<ObjectID>{0}</ObjectID>", DescriptionXmlBuilder.Escape(request.ParentId));
builder.Append("<BrowseFlag>BrowseDirectChildren</BrowseFlag>");
//builder.Append("<BrowseFlag>BrowseMetadata</BrowseFlag>");
@@ -98,12 +99,12 @@ namespace MediaBrowser.Dlna.ContentDirectory
builder.Append("<Filter>*</Filter>");
request.StartIndex = request.StartIndex ?? 0;
- builder.AppendFormat("<StartingIndex>{0}</StartingIndex>", SecurityElement.Escape(request.StartIndex.Value.ToString(CultureInfo.InvariantCulture)));
+ 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>", SecurityElement.Escape(request.Limit.Value.ToString(CultureInfo.InvariantCulture)));
+ builder.AppendFormat("<RequestedCount>{0}</RequestedCount>", DescriptionXmlBuilder.Escape(request.Limit.Value.ToString(CultureInfo.InvariantCulture)));
}
builder.Append("<SortCriteria></SortCriteria>");
diff --git a/MediaBrowser.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs b/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs
index 0e5a2671c9..7a9bc18ad8 100644
--- a/MediaBrowser.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs
+++ b/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs
@@ -1,8 +1,8 @@
-using MediaBrowser.Dlna.Common;
-using MediaBrowser.Dlna.Service;
+using Emby.Dlna.Common;
+using Emby.Dlna.Service;
using System.Collections.Generic;
-namespace MediaBrowser.Dlna.ContentDirectory
+namespace Emby.Dlna.ContentDirectory
{
public class ContentDirectoryXmlBuilder
{
diff --git a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs
index 3d1e073f8e..bba8c53d9b 100644
--- a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs
+++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs
@@ -6,10 +6,9 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Localization;
-using MediaBrowser.Dlna.Didl;
-using MediaBrowser.Dlna.Server;
-using MediaBrowser.Dlna.Service;
+using Emby.Dlna.Didl;
+using Emby.Dlna.Server;
+using Emby.Dlna.Service;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Entities;
@@ -18,14 +17,17 @@ using MediaBrowser.Model.Querying;
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 System.Xml;
using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Xml;
-namespace MediaBrowser.Dlna.ContentDirectory
+namespace Emby.Dlna.ContentDirectory
{
public class ControlHandler : BaseControlHandler
{
@@ -35,7 +37,6 @@ namespace MediaBrowser.Dlna.ContentDirectory
private readonly IServerConfigurationManager _config;
private readonly User _user;
private readonly IUserViewManager _userViewManager;
- private readonly IMediaEncoder _mediaEncoder;
private const string NS_DC = "http://purl.org/dc/elements/1.1/";
private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
@@ -49,8 +50,8 @@ namespace MediaBrowser.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)
- : base(config, logger)
+ 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)
+ : base(config, logger, xmlReaderSettingsFactory)
{
_libraryManager = libraryManager;
_userDataManager = userDataManager;
@@ -58,14 +59,13 @@ namespace MediaBrowser.Dlna.ContentDirectory
_systemUpdateId = systemUpdateId;
_channelManager = channelManager;
_userViewManager = userViewManager;
- _mediaEncoder = mediaEncoder;
_profile = profile;
_config = config;
- _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, Logger, libraryManager, _mediaEncoder);
+ _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, Logger, libraryManager, mediaEncoder);
}
- protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams)
+ protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams)
{
var deviceId = "test";
@@ -98,6 +98,9 @@ namespace MediaBrowser.Dlna.ContentDirectory
if (string.Equals(methodName, "Search", StringComparison.OrdinalIgnoreCase))
return HandleSearch(methodParams, user, deviceId).Result;
+ if (string.Equals(methodName, "X_BrowseByLetter", StringComparison.OrdinalIgnoreCase))
+ return HandleX_BrowseByLetter(methodParams, user, deviceId).Result;
+
throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
}
@@ -118,17 +121,20 @@ namespace MediaBrowser.Dlna.ContentDirectory
_userDataManager.SaveUserData(user.Id, item, userdata, UserDataSaveReason.TogglePlayed,
CancellationToken.None);
- return new Headers();
+ return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
private IEnumerable<KeyValuePair<string, string>> HandleGetSearchCapabilities()
{
- return new Headers(true) { { "SearchCaps", "res@resolution,res@size,res@duration,dc:title,dc:creator,upnp:actor,upnp:artist,upnp:genre,upnp:album,dc:date,upnp:class,@id,@refID,@protocolInfo,upnp:author,dc:description,pv:avKeywords" } };
+ return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
+ {
+ { "SearchCaps", "res@resolution,res@size,res@duration,dc:title,dc:creator,upnp:actor,upnp:artist,upnp:genre,upnp:album,dc:date,upnp:class,@id,@refID,@protocolInfo,upnp:author,dc:description,pv:avKeywords" }
+ };
}
private IEnumerable<KeyValuePair<string, string>> HandleGetSortCapabilities()
{
- return new Headers(true)
+ return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "SortCaps", "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating" }
};
@@ -136,7 +142,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
private IEnumerable<KeyValuePair<string, string>> HandleGetSortExtensionCapabilities()
{
- return new Headers(true)
+ return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "SortExtensionCaps", "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating" }
};
@@ -144,14 +150,14 @@ namespace MediaBrowser.Dlna.ContentDirectory
private IEnumerable<KeyValuePair<string, string>> HandleGetSystemUpdateID()
{
- var headers = new Headers(true);
+ var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
headers.Add("Id", _systemUpdateId.ToString(_usCulture));
return headers;
}
private IEnumerable<KeyValuePair<string, string>> HandleGetFeatureList()
{
- return new Headers(true)
+ return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "FeatureList", GetFeatureListXml() }
};
@@ -159,7 +165,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
private IEnumerable<KeyValuePair<string, string>> HandleXGetFeatureList()
{
- return new Headers(true)
+ return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "FeatureList", GetFeatureListXml() }
};
@@ -183,12 +189,24 @@ namespace MediaBrowser.Dlna.ContentDirectory
return builder.ToString();
}
- private async Task<IEnumerable<KeyValuePair<string, string>>> HandleBrowse(Headers sparams, User user, string deviceId)
+ public string GetValueOrDefault(IDictionary<string, string> sparams, string key, string defaultValue)
+ {
+ string val;
+
+ if (sparams.TryGetValue(key, out val))
+ {
+ return val;
+ }
+
+ return defaultValue;
+ }
+
+ private async Task<IEnumerable<KeyValuePair<string, string>>> HandleBrowse(IDictionary<string, string> sparams, User user, string deviceId)
{
var id = sparams["ObjectID"];
var flag = sparams["BrowseFlag"];
- var filter = new Filter(sparams.GetValueOrDefault("Filter", "*"));
- var sortCriteria = new SortCriteria(sparams.GetValueOrDefault("SortCriteria", ""));
+ var filter = new Filter(GetValueOrDefault(sparams, "Filter", "*"));
+ var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", ""));
var provided = 0;
@@ -209,80 +227,102 @@ namespace MediaBrowser.Dlna.ContentDirectory
start = startVal;
}
- //var root = GetItem(id) as IMediaFolder;
- var result = new XmlDocument();
-
- var didl = result.CreateElement(string.Empty, "DIDL-Lite", NS_DIDL);
- didl.SetAttribute("xmlns:dc", NS_DC);
- didl.SetAttribute("xmlns:dlna", NS_DLNA);
- didl.SetAttribute("xmlns:upnp", NS_UPNP);
- //didl.SetAttribute("xmlns:sec", NS_SEC);
- result.AppendChild(didl);
+ var settings = new XmlWriterSettings
+ {
+ Encoding = Encoding.UTF8,
+ CloseOutput = false,
+ OmitXmlDeclaration = true,
+ ConformanceLevel = ConformanceLevel.Fragment
+ };
- var serverItem = GetItemFromObjectId(id, user);
- var item = serverItem.Item;
+ StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
int totalCount;
- if (string.Equals(flag, "BrowseMetadata"))
+ using (XmlWriter writer = XmlWriter.Create(builder, settings))
{
- totalCount = 1;
+ //writer.WriteStartDocument();
- if (item.IsFolder || serverItem.StubType.HasValue)
- {
- var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount).ConfigureAwait(false));
+ writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
- result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, item, serverItem.StubType, null, childrenResult.TotalRecordCount, filter, id));
- }
- else
- {
- result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(_config.GetDlnaConfiguration(), result, item, null, null, deviceId, filter));
- }
+ 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);
- provided++;
- }
- else
- {
- var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount).ConfigureAwait(false));
- totalCount = childrenResult.TotalRecordCount;
+ DidlBuilder.WriteXmlRootAttributes(_profile, writer);
- provided = childrenResult.Items.Length;
+ var serverItem = GetItemFromObjectId(id, user);
+ var item = serverItem.Item;
- foreach (var i in childrenResult.Items)
+ if (string.Equals(flag, "BrowseMetadata"))
{
- var childItem = i.Item;
- var displayStubType = i.StubType;
+ totalCount = 1;
- if (childItem.IsFolder || displayStubType.HasValue)
+ if (item.IsFolder || serverItem.StubType.HasValue)
{
- var childCount = (await GetUserItems(childItem, displayStubType, user, sortCriteria, null, 0).ConfigureAwait(false))
- .TotalRecordCount;
+ var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount).ConfigureAwait(false));
- result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, childItem, displayStubType, item, childCount, filter));
+ _didlBuilder.WriteFolderElement(writer, item, serverItem.StubType, null, childrenResult.TotalRecordCount, filter, id);
}
else
{
- result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(_config.GetDlnaConfiguration(), result, childItem, item, serverItem.StubType, deviceId, filter));
+ _didlBuilder.WriteItemElement(_config.GetDlnaConfiguration(), writer, item, null, null, deviceId, filter);
}
+
+ provided++;
}
+ else
+ {
+ var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount).ConfigureAwait(false));
+ totalCount = childrenResult.TotalRecordCount;
+
+ provided = childrenResult.Items.Length;
+
+ foreach (var i in childrenResult.Items)
+ {
+ var childItem = i.Item;
+ var displayStubType = i.StubType;
+
+ if (childItem.IsFolder || displayStubType.HasValue)
+ {
+ var childCount = (await GetUserItems(childItem, displayStubType, user, sortCriteria, null, 0).ConfigureAwait(false))
+ .TotalRecordCount;
+
+ _didlBuilder.WriteFolderElement(writer, childItem, displayStubType, item, childCount, filter);
+ }
+ else
+ {
+ _didlBuilder.WriteItemElement(_config.GetDlnaConfiguration(), writer, childItem, item, serverItem.StubType, deviceId, filter);
+ }
+ }
+ }
+ writer.WriteFullEndElement();
+ //writer.WriteEndDocument();
}
- var resXML = result.OuterXml;
+ var resXML = builder.ToString();
return new List<KeyValuePair<string, string>>
- {
- new KeyValuePair<string,string>("Result", resXML),
- new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
- new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
- new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture))
- };
+ {
+ new KeyValuePair<string,string>("Result", resXML),
+ new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
+ new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
+ new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture))
+ };
+ }
+
+ private Task<IEnumerable<KeyValuePair<string, string>>> HandleX_BrowseByLetter(IDictionary<string, string> sparams, User user, string deviceId)
+ {
+ // TODO: Implement this method
+ return HandleSearch(sparams, user, deviceId);
}
- private async Task<IEnumerable<KeyValuePair<string, string>>> HandleSearch(Headers sparams, User user, string deviceId)
+ private async Task<IEnumerable<KeyValuePair<string, string>>> HandleSearch(IDictionary<string, string> sparams, User user, string deviceId)
{
- var searchCriteria = new SearchCriteria(sparams.GetValueOrDefault("SearchCriteria", ""));
- var sortCriteria = new SortCriteria(sparams.GetValueOrDefault("SortCriteria", ""));
- var filter = new Filter(sparams.GetValueOrDefault("Filter", "*"));
+ var searchCriteria = new SearchCriteria(GetValueOrDefault(sparams, "SearchCriteria", ""));
+ var sortCriteria = new SortCriteria(GetValueOrDefault(sparams, "SortCriteria", ""));
+ var filter = new Filter(GetValueOrDefault(sparams, "Filter", "*"));
// sort example: dc:title, dc:date
@@ -303,55 +343,69 @@ namespace MediaBrowser.Dlna.ContentDirectory
start = startVal;
}
- //var root = GetItem(id) as IMediaFolder;
- var result = new XmlDocument();
+ var settings = new XmlWriterSettings
+ {
+ Encoding = Encoding.UTF8,
+ CloseOutput = false,
+ OmitXmlDeclaration = true,
+ ConformanceLevel = ConformanceLevel.Fragment
+ };
- var didl = result.CreateElement(string.Empty, "DIDL-Lite", NS_DIDL);
- didl.SetAttribute("xmlns:dc", NS_DC);
- didl.SetAttribute("xmlns:dlna", NS_DLNA);
- didl.SetAttribute("xmlns:upnp", NS_UPNP);
+ StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
+ int totalCount = 0;
+ int provided = 0;
- foreach (var att in _profile.XmlRootAttributes)
+ using (XmlWriter writer = XmlWriter.Create(builder, settings))
{
- didl.SetAttribute(att.Name, att.Value);
- }
+ //writer.WriteStartDocument();
- result.AppendChild(didl);
+ writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
- var serverItem = GetItemFromObjectId(sparams["ContainerID"], user);
+ writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
+ writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
+ writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
+ //didl.SetAttribute("xmlns:sec", NS_SEC);
- var item = serverItem.Item;
+ DidlBuilder.WriteXmlRootAttributes(_profile, writer);
- var childrenResult = (await GetChildrenSorted(item, user, searchCriteria, sortCriteria, start, requestedCount).ConfigureAwait(false));
+ var serverItem = GetItemFromObjectId(sparams["ContainerID"], user);
- var totalCount = childrenResult.TotalRecordCount;
+ var item = serverItem.Item;
- var provided = childrenResult.Items.Length;
+ var childrenResult = (await GetChildrenSorted(item, user, searchCriteria, sortCriteria, start, requestedCount).ConfigureAwait(false));
- foreach (var i in childrenResult.Items)
- {
- if (i.IsFolder)
- {
- var childCount = (await GetChildrenSorted(i, user, searchCriteria, sortCriteria, null, 0).ConfigureAwait(false))
- .TotalRecordCount;
+ totalCount = childrenResult.TotalRecordCount;
- result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, i, null, item, childCount, filter));
- }
- else
+ provided = childrenResult.Items.Length;
+
+ foreach (var i in childrenResult.Items)
{
- result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(_config.GetDlnaConfiguration(), result, i, item, serverItem.StubType, deviceId, filter));
+ if (i.IsFolder)
+ {
+ var childCount = (await GetChildrenSorted(i, user, searchCriteria, sortCriteria, null, 0).ConfigureAwait(false))
+ .TotalRecordCount;
+
+ _didlBuilder.WriteFolderElement(writer, i, null, item, childCount, filter);
+ }
+ else
+ {
+ _didlBuilder.WriteItemElement(_config.GetDlnaConfiguration(), writer, i, item, serverItem.StubType, deviceId, filter);
+ }
}
+
+ writer.WriteFullEndElement();
+ //writer.WriteEndDocument();
}
- var resXML = result.OuterXml;
+ var resXML = builder.ToString();
return new List<KeyValuePair<string, string>>
- {
- new KeyValuePair<string,string>("Result", resXML),
- new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
- new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
- new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture))
- };
+ {
+ new KeyValuePair<string,string>("Result", resXML),
+ new KeyValuePair<string,string>("NumberReturned", provided.ToString(_usCulture)),
+ new KeyValuePair<string,string>("TotalMatches", totalCount.ToString(_usCulture)),
+ new KeyValuePair<string,string>("UpdateID", _systemUpdateId.ToString(_usCulture))
+ };
}
private Task<QueryResult<BaseItem>> GetChildrenSorted(BaseItem item, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit)
@@ -479,7 +533,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
{
var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
{
- Person = person.Name,
+ PersonIds = new[] { person.Id.ToString("N") },
IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name, typeof(Trailer).Name },
SortBy = new[] { ItemSortBy.SortName },
Limit = limit,
@@ -508,20 +562,6 @@ namespace MediaBrowser.Dlna.ContentDirectory
return result;
}
- private bool EnablePeopleDisplay(BaseItem item)
- {
- if (_libraryManager.GetPeopleNames(new InternalPeopleQuery
- {
- ItemId = item.Id
-
- }).Count > 0)
- {
- return item is Movie;
- }
-
- return false;
- }
-
private ServerItem GetItemFromObjectId(string id, User user)
{
return DidlBuilder.IsIdRoot(id)
diff --git a/MediaBrowser.Dlna/ContentDirectory/ServiceActionListBuilder.cs b/Emby.Dlna/ContentDirectory/ServiceActionListBuilder.cs
index cd8119048e..8e3821e7c8 100644
--- a/MediaBrowser.Dlna/ContentDirectory/ServiceActionListBuilder.cs
+++ b/Emby.Dlna/ContentDirectory/ServiceActionListBuilder.cs
@@ -1,7 +1,7 @@
-using MediaBrowser.Dlna.Common;
+using Emby.Dlna.Common;
using System.Collections.Generic;
-namespace MediaBrowser.Dlna.ContentDirectory
+namespace Emby.Dlna.ContentDirectory
{
public class ServiceActionListBuilder
{
diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs
index a5091bf01e..3dcdaf2efc 100644
--- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs
+++ b/Emby.Dlna/Didl/DidlBuilder.cs
@@ -6,9 +6,8 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Playlists;
-using MediaBrowser.Dlna.ContentDirectory;
+using Emby.Dlna.ContentDirectory;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities;
@@ -18,11 +17,13 @@ using System;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Text;
using System.Xml;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Globalization;
-namespace MediaBrowser.Dlna.Didl
+namespace Emby.Dlna.Didl
{
public class DidlBuilder
{
@@ -62,50 +63,79 @@ namespace MediaBrowser.Dlna.Didl
public string GetItemDidl(DlnaOptions options, BaseItem item, BaseItem context, string deviceId, Filter filter, StreamInfo streamInfo)
{
- var result = new XmlDocument();
+ var settings = new XmlWriterSettings
+ {
+ Encoding = Encoding.UTF8,
+ CloseOutput = false,
+ OmitXmlDeclaration = true,
+ ConformanceLevel = ConformanceLevel.Fragment
+ };
- var didl = result.CreateElement(string.Empty, "DIDL-Lite", NS_DIDL);
- didl.SetAttribute("xmlns:dc", NS_DC);
- didl.SetAttribute("xmlns:dlna", NS_DLNA);
- didl.SetAttribute("xmlns:upnp", NS_UPNP);
- //didl.SetAttribute("xmlns:sec", NS_SEC);
+ StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
- foreach (var att in _profile.XmlRootAttributes)
+ using (XmlWriter writer = XmlWriter.Create(builder, settings))
{
- didl.SetAttribute(att.Name, att.Value);
- }
+ //writer.WriteStartDocument();
+
+ writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
+
+ writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
+ writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
+ writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
+ //didl.SetAttribute("xmlns:sec", NS_SEC);
+
+ WriteXmlRootAttributes(_profile, writer);
- result.AppendChild(didl);
+ WriteItemElement(options, writer, item, context, null, deviceId, filter, streamInfo);
- result.DocumentElement.AppendChild(GetItemElement(options, result, item, context, null, deviceId, filter, streamInfo));
+ writer.WriteFullEndElement();
+ //writer.WriteEndDocument();
+ }
- return result.DocumentElement.OuterXml;
+ return builder.ToString();
}
- public XmlElement GetItemElement(DlnaOptions options, XmlDocument doc, BaseItem item, BaseItem context, StubType? contextStubType, string deviceId, Filter filter, StreamInfo streamInfo = null)
+ public static void WriteXmlRootAttributes(DeviceProfile profile, XmlWriter writer)
+ {
+ foreach (var att in profile.XmlRootAttributes)
+ {
+ var parts = att.Name.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
+ if (parts.Length == 2)
+ {
+ writer.WriteAttributeString(parts[0], parts[1], null, att.Value);
+ }
+ else
+ {
+ writer.WriteAttributeString(att.Name, att.Value);
+ }
+ }
+ }
+
+ public void WriteItemElement(DlnaOptions options, XmlWriter writer, BaseItem item, BaseItem context, StubType? contextStubType, string deviceId, Filter filter, StreamInfo streamInfo = null)
{
var clientId = GetClientId(item, null);
- var element = doc.CreateElement(string.Empty, "item", NS_DIDL);
- element.SetAttribute("restricted", "1");
- element.SetAttribute("id", clientId);
+ writer.WriteStartElement(string.Empty, "item", NS_DIDL);
+
+ writer.WriteAttributeString("restricted", "1");
+ writer.WriteAttributeString("id", clientId);
if (context != null)
{
- element.SetAttribute("parentID", GetClientId(context, contextStubType));
+ writer.WriteAttributeString("parentID", GetClientId(context, contextStubType));
}
else
{
var parent = item.DisplayParentId;
if (parent.HasValue)
{
- element.SetAttribute("parentID", GetClientId(parent.Value, null));
+ writer.WriteAttributeString("parentID", GetClientId(parent.Value, null));
}
}
- //AddBookmarkInfo(item, user, element);
+ AddGeneralProperties(item, null, context, writer, filter);
- AddGeneralProperties(item, null, context, element, filter);
+ //AddBookmarkInfo(item, user, element);
// refID?
// storeAttribute(itemNode, object, ClassProperties.REF_ID, false);
@@ -116,17 +146,16 @@ namespace MediaBrowser.Dlna.Didl
{
if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
{
- AddAudioResource(options, element, hasMediaSources, deviceId, filter, streamInfo);
+ AddAudioResource(options, writer, hasMediaSources, deviceId, filter, streamInfo);
}
else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{
- AddVideoResource(options, element, hasMediaSources, deviceId, filter, streamInfo);
+ AddVideoResource(options, writer, hasMediaSources, deviceId, filter, streamInfo);
}
}
- AddCover(item, context, null, element);
-
- return element;
+ AddCover(item, context, null, writer);
+ writer.WriteFullEndElement();
}
private ILogger GetStreamBuilderLogger(DlnaOptions options)
@@ -139,7 +168,7 @@ namespace MediaBrowser.Dlna.Didl
return new NullLogger();
}
- private void AddVideoResource(DlnaOptions options, XmlElement container, IHasMediaSources video, string deviceId, Filter filter, StreamInfo streamInfo = null)
+ private void AddVideoResource(DlnaOptions options, XmlWriter writer, IHasMediaSources video, string deviceId, Filter filter, StreamInfo streamInfo = null)
{
if (streamInfo == null)
{
@@ -182,24 +211,25 @@ namespace MediaBrowser.Dlna.Didl
foreach (var contentFeature in contentFeatureList)
{
- AddVideoResource(container, video, deviceId, filter, contentFeature, streamInfo);
+ AddVideoResource(writer, video, deviceId, filter, contentFeature, streamInfo);
}
- foreach (var subtitle in streamInfo.GetSubtitleProfiles(false, _serverAddress, _accessToken))
+ var subtitleProfiles = streamInfo.GetSubtitleProfiles(false, _serverAddress, _accessToken)
+ .Where(subtitle => subtitle.DeliveryMethod == SubtitleDeliveryMethod.External)
+ .ToList();
+
+ foreach (var subtitle in subtitleProfiles)
{
- if (subtitle.DeliveryMethod == SubtitleDeliveryMethod.External)
- {
- var subtitleAdded = AddSubtitleElement(container, subtitle);
+ var subtitleAdded = AddSubtitleElement(writer, subtitle);
- if (subtitleAdded && _profile.EnableSingleSubtitleLimit)
- {
- break;
- }
+ if (subtitleAdded && _profile.EnableSingleSubtitleLimit)
+ {
+ break;
}
}
}
- private bool AddSubtitleElement(XmlElement container, SubtitleStreamInfo info)
+ private bool AddSubtitleElement(XmlWriter writer, SubtitleStreamInfo info)
{
var subtitleProfile = _profile.SubtitleProfiles
.FirstOrDefault(i => string.Equals(info.Format, i.Format, StringComparison.OrdinalIgnoreCase) && i.Method == SubtitleDeliveryMethod.External);
@@ -216,52 +246,45 @@ namespace MediaBrowser.Dlna.Didl
// <sec:CaptionInfoEx sec:type="srt">http://192.168.1.3:9999/video.srt</sec:CaptionInfoEx>
// <sec:CaptionInfo sec:type="srt">http://192.168.1.3:9999/video.srt</sec:CaptionInfo>
- var res = container.OwnerDocument.CreateElement("CaptionInfoEx", "sec");
-
- res.InnerText = info.Url;
+ writer.WriteStartElement("sec", "CaptionInfoEx", null);
+ writer.WriteAttributeString("sec", "type", null, info.Format.ToLower());
- //// TODO: attribute needs SEC:
- res.SetAttribute("type", "sec", info.Format.ToLower());
- container.AppendChild(res);
+ writer.WriteString(info.Url);
+ writer.WriteFullEndElement();
}
else if (string.Equals(subtitleMode, "smi", StringComparison.OrdinalIgnoreCase))
{
- var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL);
-
- res.InnerText = info.Url;
+ writer.WriteStartElement(string.Empty, "res", NS_DIDL);
- res.SetAttribute("protocolInfo", "http-get:*:smi/caption:*");
+ writer.WriteAttributeString("protocolInfo", "http-get:*:smi/caption:*");
- container.AppendChild(res);
+ writer.WriteString(info.Url);
+ writer.WriteFullEndElement();
}
else
{
- var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL);
-
- res.InnerText = info.Url;
-
+ writer.WriteStartElement(string.Empty, "res", NS_DIDL);
var protocolInfo = string.Format("http-get:*:text/{0}:*", info.Format.ToLower());
- res.SetAttribute("protocolInfo", protocolInfo);
+ writer.WriteAttributeString("protocolInfo", protocolInfo);
- container.AppendChild(res);
+ writer.WriteString(info.Url);
+ writer.WriteFullEndElement();
}
return true;
}
- private void AddVideoResource(XmlElement container, IHasMediaSources video, string deviceId, Filter filter, string contentFeatures, StreamInfo streamInfo)
+ private void AddVideoResource(XmlWriter writer, IHasMediaSources video, string deviceId, Filter filter, string contentFeatures, StreamInfo streamInfo)
{
- var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL);
+ writer.WriteStartElement(string.Empty, "res", NS_DIDL);
var url = streamInfo.ToDlnaUrl(_serverAddress, _accessToken);
- res.InnerText = url;
-
var mediaSource = streamInfo.MediaSource;
if (mediaSource.RunTimeTicks.HasValue)
{
- res.SetAttribute("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", _usCulture));
+ writer.WriteAttributeString("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", _usCulture));
}
if (filter.Contains("res@size"))
@@ -272,7 +295,7 @@ namespace MediaBrowser.Dlna.Didl
if (size.HasValue)
{
- res.SetAttribute("size", size.Value.ToString(_usCulture));
+ writer.WriteAttributeString("size", size.Value.ToString(_usCulture));
}
}
}
@@ -286,25 +309,25 @@ namespace MediaBrowser.Dlna.Didl
if (targetChannels.HasValue)
{
- res.SetAttribute("nrAudioChannels", targetChannels.Value.ToString(_usCulture));
+ writer.WriteAttributeString("nrAudioChannels", targetChannels.Value.ToString(_usCulture));
}
if (filter.Contains("res@resolution"))
{
if (targetWidth.HasValue && targetHeight.HasValue)
{
- res.SetAttribute("resolution", string.Format("{0}x{1}", targetWidth.Value, targetHeight.Value));
+ writer.WriteAttributeString("resolution", string.Format("{0}x{1}", targetWidth.Value, targetHeight.Value));
}
}
if (targetSampleRate.HasValue)
{
- res.SetAttribute("sampleFrequency", targetSampleRate.Value.ToString(_usCulture));
+ writer.WriteAttributeString("sampleFrequency", targetSampleRate.Value.ToString(_usCulture));
}
if (totalBitrate.HasValue)
{
- res.SetAttribute("bitrate", totalBitrate.Value.ToString(_usCulture));
+ writer.WriteAttributeString("bitrate", totalBitrate.Value.ToString(_usCulture));
}
var mediaProfile = _profile.GetVideoMediaProfile(streamInfo.Container,
@@ -332,13 +355,15 @@ namespace MediaBrowser.Dlna.Didl
? MimeTypes.GetMimeType(filename)
: mediaProfile.MimeType;
- res.SetAttribute("protocolInfo", String.Format(
+ writer.WriteAttributeString("protocolInfo", String.Format(
"http-get:*:{0}:{1}",
mimeType,
contentFeatures
));
- container.AppendChild(res);
+ writer.WriteString(url);
+
+ writer.WriteFullEndElement();
}
private string GetDisplayName(BaseItem item, StubType? itemStubType, BaseItem context)
@@ -368,11 +393,11 @@ namespace MediaBrowser.Dlna.Didl
if (item.IndexNumber.HasValue)
{
- var number = item.IndexNumber.Value.ToString("00").ToString(CultureInfo.InvariantCulture);
+ var number = item.IndexNumber.Value.ToString("00", CultureInfo.InvariantCulture);
if (episode.IndexNumberEnd.HasValue)
{
- number += "-" + episode.IndexNumberEnd.Value.ToString("00").ToString(CultureInfo.InvariantCulture);
+ number += "-" + episode.IndexNumberEnd.Value.ToString("00", CultureInfo.InvariantCulture);
}
return number + " - " + item.Name;
@@ -382,32 +407,30 @@ namespace MediaBrowser.Dlna.Didl
return item.Name;
}
- private void AddAudioResource(DlnaOptions options, XmlElement container, IHasMediaSources audio, string deviceId, Filter filter, StreamInfo streamInfo = null)
+ private void AddAudioResource(DlnaOptions options, XmlWriter writer, IHasMediaSources audio, string deviceId, Filter filter, StreamInfo streamInfo = null)
{
- var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL);
+ writer.WriteStartElement(string.Empty, "res", NS_DIDL);
if (streamInfo == null)
{
var sources = _mediaSourceManager.GetStaticMediaSources(audio, true, _user).ToList();
streamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger(options)).BuildAudioItem(new AudioOptions
- {
- ItemId = GetClientId(audio),
- MediaSources = sources,
- Profile = _profile,
- DeviceId = deviceId
- });
+ {
+ ItemId = GetClientId(audio),
+ MediaSources = sources,
+ Profile = _profile,
+ DeviceId = deviceId
+ });
}
var url = streamInfo.ToDlnaUrl(_serverAddress, _accessToken);
- res.InnerText = url;
-
var mediaSource = streamInfo.MediaSource;
if (mediaSource.RunTimeTicks.HasValue)
{
- res.SetAttribute("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", _usCulture));
+ writer.WriteAttributeString("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", _usCulture));
}
if (filter.Contains("res@size"))
@@ -418,7 +441,7 @@ namespace MediaBrowser.Dlna.Didl
if (size.HasValue)
{
- res.SetAttribute("size", size.Value.ToString(_usCulture));
+ writer.WriteAttributeString("size", size.Value.ToString(_usCulture));
}
}
}
@@ -429,17 +452,17 @@ namespace MediaBrowser.Dlna.Didl
if (targetChannels.HasValue)
{
- res.SetAttribute("nrAudioChannels", targetChannels.Value.ToString(_usCulture));
+ writer.WriteAttributeString("nrAudioChannels", targetChannels.Value.ToString(_usCulture));
}
if (targetSampleRate.HasValue)
{
- res.SetAttribute("sampleFrequency", targetSampleRate.Value.ToString(_usCulture));
+ writer.WriteAttributeString("sampleFrequency", targetSampleRate.Value.ToString(_usCulture));
}
if (targetAudioBitrate.HasValue)
{
- res.SetAttribute("bitrate", targetAudioBitrate.Value.ToString(_usCulture));
+ writer.WriteAttributeString("bitrate", targetAudioBitrate.Value.ToString(_usCulture));
}
var mediaProfile = _profile.GetAudioMediaProfile(streamInfo.Container,
@@ -462,13 +485,15 @@ namespace MediaBrowser.Dlna.Didl
streamInfo.RunTimeTicks,
streamInfo.TranscodeSeekInfo);
- res.SetAttribute("protocolInfo", String.Format(
+ writer.WriteAttributeString("protocolInfo", String.Format(
"http-get:*:{0}:{1}",
mimeType,
contentFeatures
));
- container.AppendChild(res);
+ writer.WriteString(url);
+
+ writer.WriteFullEndElement();
}
public static bool IsIdRoot(string id)
@@ -486,47 +511,48 @@ namespace MediaBrowser.Dlna.Didl
return false;
}
- public XmlElement GetFolderElement(XmlDocument doc, BaseItem folder, StubType? stubType, BaseItem context, int childCount, Filter filter, string requestedId = null)
+ public void WriteFolderElement(XmlWriter writer, BaseItem folder, StubType? stubType, BaseItem context, int childCount, Filter filter, string requestedId = null)
{
- var container = doc.CreateElement(string.Empty, "container", NS_DIDL);
- container.SetAttribute("restricted", "0");
- container.SetAttribute("searchable", "1");
- container.SetAttribute("childCount", childCount.ToString(_usCulture));
+ writer.WriteStartElement(string.Empty, "container", NS_DIDL);
+
+ writer.WriteAttributeString("restricted", "0");
+ writer.WriteAttributeString("searchable", "1");
+ writer.WriteAttributeString("childCount", childCount.ToString(_usCulture));
var clientId = GetClientId(folder, stubType);
if (string.Equals(requestedId, "0"))
{
- container.SetAttribute("id", "0");
- container.SetAttribute("parentID", "-1");
+ writer.WriteAttributeString("id", "0");
+ writer.WriteAttributeString("parentID", "-1");
}
else
{
- container.SetAttribute("id", clientId);
+ writer.WriteAttributeString("id", clientId);
if (context != null)
{
- container.SetAttribute("parentID", GetClientId(context, null));
+ writer.WriteAttributeString("parentID", GetClientId(context, null));
}
else
{
var parent = folder.DisplayParentId;
if (!parent.HasValue)
{
- container.SetAttribute("parentID", "0");
+ writer.WriteAttributeString("parentID", "0");
}
else
{
- container.SetAttribute("parentID", GetClientId(parent.Value, null));
+ writer.WriteAttributeString("parentID", GetClientId(parent.Value, null));
}
}
}
- AddCommonFields(folder, stubType, null, container, filter);
+ AddGeneralProperties(folder, stubType, context, writer, filter);
- AddCover(folder, context, stubType, container);
+ AddCover(folder, context, stubType, writer);
- return container;
+ writer.WriteFullEndElement();
}
//private void AddBookmarkInfo(BaseItem item, User user, XmlElement element)
@@ -544,27 +570,22 @@ namespace MediaBrowser.Dlna.Didl
/// <summary>
/// Adds fields used by both items and folders
/// </summary>
- /// <param name="item">The item.</param>
- /// <param name="itemStubType">Type of the item stub.</param>
- /// <param name="context">The context.</param>
- /// <param name="element">The element.</param>
- /// <param name="filter">The filter.</param>
- private void AddCommonFields(BaseItem item, StubType? itemStubType, BaseItem context, XmlElement element, Filter filter)
+ private void AddCommonFields(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter)
{
// Don't filter on dc:title because not all devices will include it in the filter
// MediaMonkey for example won't display content without a title
//if (filter.Contains("dc:title"))
{
- AddValue(element, "dc", "title", GetDisplayName(item, itemStubType, context), NS_DC);
+ AddValue(writer, "dc", "title", GetDisplayName(item, itemStubType, context), NS_DC);
}
- element.AppendChild(CreateObjectClass(element.OwnerDocument, item, itemStubType));
+ WriteObjectClass(writer, item, itemStubType);
if (filter.Contains("dc:date"))
{
if (item.PremiereDate.HasValue)
{
- AddValue(element, "dc", "date", item.PremiereDate.Value.ToString("o"), NS_DC);
+ AddValue(writer, "dc", "date", item.PremiereDate.Value.ToString("o"), NS_DC);
}
}
@@ -572,13 +593,13 @@ namespace MediaBrowser.Dlna.Didl
{
foreach (var genre in item.Genres)
{
- AddValue(element, "upnp", "genre", genre, NS_UPNP);
+ AddValue(writer, "upnp", "genre", genre, NS_UPNP);
}
}
foreach (var studio in item.Studios)
{
- AddValue(element, "upnp", "publisher", studio, NS_UPNP);
+ AddValue(writer, "upnp", "publisher", studio, NS_UPNP);
}
if (filter.Contains("dc:description"))
@@ -592,14 +613,14 @@ namespace MediaBrowser.Dlna.Didl
if (!string.IsNullOrWhiteSpace(desc))
{
- AddValue(element, "dc", "description", desc, NS_DC);
+ AddValue(writer, "dc", "description", desc, NS_DC);
}
}
if (filter.Contains("upnp:longDescription"))
{
if (!string.IsNullOrWhiteSpace(item.Overview))
{
- AddValue(element, "upnp", "longDescription", item.Overview, NS_UPNP);
+ AddValue(writer, "upnp", "longDescription", item.Overview, NS_UPNP);
}
}
@@ -607,23 +628,23 @@ namespace MediaBrowser.Dlna.Didl
{
if (filter.Contains("dc:rating"))
{
- AddValue(element, "dc", "rating", item.OfficialRating, NS_DC);
+ AddValue(writer, "dc", "rating", item.OfficialRating, NS_DC);
}
if (filter.Contains("upnp:rating"))
{
- AddValue(element, "upnp", "rating", item.OfficialRating, NS_UPNP);
+ AddValue(writer, "upnp", "rating", item.OfficialRating, NS_UPNP);
}
}
- AddPeople(item, element);
+ AddPeople(item, writer);
}
- private XmlElement CreateObjectClass(XmlDocument result, BaseItem item, StubType? stubType)
+ private void WriteObjectClass(XmlWriter writer, BaseItem item, StubType? stubType)
{
// More types here
// http://oss.linn.co.uk/repos/Public/LibUpnpCil/DidlLite/UpnpAv/Test/TestDidlLite.cs
- var objectClass = result.CreateElement("upnp", "class", NS_UPNP);
+ writer.WriteStartElement("upnp", "class", NS_UPNP);
if (item.IsFolder || stubType.HasValue)
{
@@ -653,48 +674,48 @@ namespace MediaBrowser.Dlna.Didl
}
}
- objectClass.InnerText = classType ?? "object.container.storageFolder";
+ writer.WriteString(classType ?? "object.container.storageFolder");
}
else if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
{
- objectClass.InnerText = "object.item.audioItem.musicTrack";
+ writer.WriteString("object.item.audioItem.musicTrack");
}
else if (string.Equals(item.MediaType, MediaType.Photo, StringComparison.OrdinalIgnoreCase))
{
- objectClass.InnerText = "object.item.imageItem.photo";
+ writer.WriteString("object.item.imageItem.photo");
}
else if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{
if (!_profile.RequiresPlainVideoItems && item is Movie)
{
- objectClass.InnerText = "object.item.videoItem.movie";
+ writer.WriteString("object.item.videoItem.movie");
}
else if (!_profile.RequiresPlainVideoItems && item is MusicVideo)
{
- objectClass.InnerText = "object.item.videoItem.musicVideoClip";
+ writer.WriteString("object.item.videoItem.musicVideoClip");
}
else
{
- objectClass.InnerText = "object.item.videoItem";
+ writer.WriteString("object.item.videoItem");
}
}
else if (item is MusicGenre)
{
- objectClass.InnerText = _profile.RequiresPlainFolders ? "object.container.storageFolder" : "object.container.genre.musicGenre";
+ writer.WriteString(_profile.RequiresPlainFolders ? "object.container.storageFolder" : "object.container.genre.musicGenre");
}
else if (item is Genre || item is GameGenre)
{
- objectClass.InnerText = _profile.RequiresPlainFolders ? "object.container.storageFolder" : "object.container.genre";
+ writer.WriteString(_profile.RequiresPlainFolders ? "object.container.storageFolder" : "object.container.genre");
}
else
{
- objectClass.InnerText = "object.item";
+ writer.WriteString("object.item");
}
- return objectClass;
+ writer.WriteFullEndElement();
}
- private void AddPeople(BaseItem item, XmlElement element)
+ private void AddPeople(BaseItem item, XmlWriter writer)
{
var types = new[]
{
@@ -718,7 +739,7 @@ namespace MediaBrowser.Dlna.Didl
var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase))
?? PersonType.Actor;
- AddValue(element, "upnp", type.ToLower(), actor.Name, NS_UPNP);
+ AddValue(writer, "upnp", type.ToLower(), actor.Name, NS_UPNP);
index++;
@@ -729,9 +750,9 @@ namespace MediaBrowser.Dlna.Didl
}
}
- private void AddGeneralProperties(BaseItem item, StubType? itemStubType, BaseItem context, XmlElement element, Filter filter)
+ private void AddGeneralProperties(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter)
{
- AddCommonFields(item, itemStubType, context, element, filter);
+ AddCommonFields(item, itemStubType, context, writer, filter);
var audio = item as Audio;
@@ -739,17 +760,17 @@ namespace MediaBrowser.Dlna.Didl
{
foreach (var artist in audio.Artists)
{
- AddValue(element, "upnp", "artist", artist, NS_UPNP);
+ AddValue(writer, "upnp", "artist", artist, NS_UPNP);
}
if (!string.IsNullOrEmpty(audio.Album))
{
- AddValue(element, "upnp", "album", audio.Album, NS_UPNP);
+ AddValue(writer, "upnp", "album", audio.Album, NS_UPNP);
}
foreach (var artist in audio.AlbumArtists)
{
- AddAlbumArtist(element, artist);
+ AddAlbumArtist(writer, artist);
}
}
@@ -759,12 +780,12 @@ namespace MediaBrowser.Dlna.Didl
{
foreach (var artist in album.AlbumArtists)
{
- AddAlbumArtist(element, artist);
- AddValue(element, "upnp", "artist", artist, NS_UPNP);
+ AddAlbumArtist(writer, artist);
+ AddValue(writer, "upnp", "artist", artist, NS_UPNP);
}
foreach (var artist in album.Artists)
{
- AddValue(element, "upnp", "artist", artist, NS_UPNP);
+ AddValue(writer, "upnp", "artist", artist, NS_UPNP);
}
}
@@ -774,37 +795,37 @@ namespace MediaBrowser.Dlna.Didl
{
foreach (var artist in musicVideo.Artists)
{
- AddValue(element, "upnp", "artist", artist, NS_UPNP);
- AddAlbumArtist(element, artist);
+ AddValue(writer, "upnp", "artist", artist, NS_UPNP);
+ AddAlbumArtist(writer, artist);
}
if (!string.IsNullOrEmpty(musicVideo.Album))
{
- AddValue(element, "upnp", "album", musicVideo.Album, NS_UPNP);
+ AddValue(writer, "upnp", "album", musicVideo.Album, NS_UPNP);
}
}
if (item.IndexNumber.HasValue)
{
- AddValue(element, "upnp", "originalTrackNumber", item.IndexNumber.Value.ToString(_usCulture), NS_UPNP);
+ AddValue(writer, "upnp", "originalTrackNumber", item.IndexNumber.Value.ToString(_usCulture), NS_UPNP);
if (item is Episode)
{
- AddValue(element, "upnp", "episodeNumber", item.IndexNumber.Value.ToString(_usCulture), NS_UPNP);
+ AddValue(writer, "upnp", "episodeNumber", item.IndexNumber.Value.ToString(_usCulture), NS_UPNP);
}
}
}
- private void AddAlbumArtist(XmlElement elem, string name)
+ private void AddAlbumArtist(XmlWriter writer, string name)
{
try
{
- var newNode = elem.OwnerDocument.CreateElement("upnp", "artist", NS_UPNP);
- newNode.InnerText = name;
+ writer.WriteStartElement("upnp", "artist", NS_UPNP);
+ writer.WriteAttributeString("role", "AlbumArtist");
- newNode.SetAttribute("role", "AlbumArtist");
+ writer.WriteString(name);
- elem.AppendChild(newNode);
+ writer.WriteFullEndElement();
}
catch (XmlException)
{
@@ -812,13 +833,11 @@ namespace MediaBrowser.Dlna.Didl
}
}
- private void AddValue(XmlElement elem, string prefix, string name, string value, string namespaceUri)
+ private void AddValue(XmlWriter writer, string prefix, string name, string value, string namespaceUri)
{
try
{
- var date = elem.OwnerDocument.CreateElement(prefix, name, namespaceUri);
- date.InnerText = value;
- elem.AppendChild(date);
+ writer.WriteElementString(prefix, name, namespaceUri, value);
}
catch (XmlException)
{
@@ -826,11 +845,11 @@ namespace MediaBrowser.Dlna.Didl
}
}
- private void AddCover(BaseItem item, BaseItem context, StubType? stubType, XmlElement element)
+ private void AddCover(BaseItem item, BaseItem context, StubType? stubType, XmlWriter writer)
{
if (stubType.HasValue && stubType.Value == StubType.People)
{
- AddEmbeddedImageAsCover("people", element);
+ AddEmbeddedImageAsCover("people", writer);
return;
}
@@ -860,8 +879,6 @@ namespace MediaBrowser.Dlna.Didl
return;
}
- var result = element.OwnerDocument;
-
var playbackPercentage = 0;
var unplayedCount = 0;
@@ -891,18 +908,14 @@ namespace MediaBrowser.Dlna.Didl
var albumartUrlInfo = GetImageUrl(imageInfo, _profile.MaxAlbumArtWidth, _profile.MaxAlbumArtHeight, playbackPercentage, unplayedCount, "jpg");
- var icon = result.CreateElement("upnp", "albumArtURI", NS_UPNP);
- var profile = result.CreateAttribute("dlna", "profileID", NS_DLNA);
- profile.InnerText = _profile.AlbumArtPn;
- icon.SetAttributeNode(profile);
- icon.InnerText = albumartUrlInfo.Url;
- element.AppendChild(icon);
+ writer.WriteStartElement("upnp", "albumArtURI", NS_UPNP);
+ writer.WriteAttributeString("dlna", "profileID", NS_DLNA, _profile.AlbumArtPn);
+ writer.WriteString(albumartUrlInfo.Url);
+ writer.WriteFullEndElement();
// TOOD: Remove these default values
var iconUrlInfo = GetImageUrl(imageInfo, _profile.MaxIconWidth ?? 48, _profile.MaxIconHeight ?? 48, playbackPercentage, unplayedCount, "jpg");
- icon = result.CreateElement("upnp", "icon", NS_UPNP);
- icon.InnerText = iconUrlInfo.Url;
- element.AppendChild(icon);
+ writer.WriteElementString("upnp", "icon", NS_UPNP, iconUrlInfo.Url);
if (!_profile.EnableAlbumArtInDidl)
{
@@ -916,36 +929,30 @@ namespace MediaBrowser.Dlna.Didl
}
}
- AddImageResElement(item, element, 160, 160, playbackPercentage, unplayedCount, "jpg", "JPEG_TN");
+ AddImageResElement(item, writer, 160, 160, playbackPercentage, unplayedCount, "jpg", "JPEG_TN");
if (!_profile.EnableSingleAlbumArtLimit)
{
- AddImageResElement(item, element, 4096, 4096, playbackPercentage, unplayedCount, "jpg", "JPEG_LRG");
- AddImageResElement(item, element, 1024, 768, playbackPercentage, unplayedCount, "jpg", "JPEG_MED");
- AddImageResElement(item, element, 640, 480, playbackPercentage, unplayedCount, "jpg", "JPEG_SM");
- AddImageResElement(item, element, 4096, 4096, playbackPercentage, unplayedCount, "png", "PNG_LRG");
- AddImageResElement(item, element, 160, 160, playbackPercentage, unplayedCount, "png", "PNG_TN");
+ 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");
}
}
- private void AddEmbeddedImageAsCover(string name, XmlElement element)
+ private void AddEmbeddedImageAsCover(string name, XmlWriter writer)
{
- var result = element.OwnerDocument;
-
- var icon = result.CreateElement("upnp", "albumArtURI", NS_UPNP);
- var profile = result.CreateAttribute("dlna", "profileID", NS_DLNA);
- profile.InnerText = _profile.AlbumArtPn;
- icon.SetAttributeNode(profile);
- icon.InnerText = _serverAddress + "/Dlna/icons/people480.jpg";
- element.AppendChild(icon);
-
- icon = result.CreateElement("upnp", "icon", NS_UPNP);
- icon.InnerText = _serverAddress + "/Dlna/icons/people48.jpg";
- element.AppendChild(icon);
+ writer.WriteStartElement("upnp", "albumArtURI", NS_UPNP);
+ writer.WriteAttributeString("dlna", "profileID", NS_DLNA, _profile.AlbumArtPn);
+ writer.WriteString(_serverAddress + "/Dlna/icons/people480.jpg");
+ writer.WriteFullEndElement();
+
+ writer.WriteElementString("upnp", "icon", NS_UPNP, _serverAddress + "/Dlna/icons/people48.jpg");
}
private void AddImageResElement(BaseItem item,
- XmlElement element,
+ XmlWriter writer,
int maxWidth,
int maxHeight,
int playbackPercentage,
@@ -960,13 +967,9 @@ namespace MediaBrowser.Dlna.Didl
return;
}
- var result = element.OwnerDocument;
-
var albumartUrlInfo = GetImageUrl(imageInfo, maxWidth, maxHeight, playbackPercentage, unplayedCount, format);
- var res = result.CreateElement(string.Empty, "res", NS_DIDL);
-
- res.InnerText = albumartUrlInfo.Url;
+ writer.WriteStartElement(string.Empty, "res", NS_DIDL);
var width = albumartUrlInfo.Width;
var height = albumartUrlInfo.Height;
@@ -974,7 +977,7 @@ namespace MediaBrowser.Dlna.Didl
var contentFeatures = new ContentFeatureBuilder(_profile)
.BuildImageHeader(format, width, height, imageInfo.IsDirectStream, org_Pn);
- res.SetAttribute("protocolInfo", String.Format(
+ writer.WriteAttributeString("protocolInfo", String.Format(
"http-get:*:{0}:{1}",
MimeTypes.GetMimeType("file." + format),
contentFeatures
@@ -982,10 +985,12 @@ namespace MediaBrowser.Dlna.Didl
if (width.HasValue && height.HasValue)
{
- res.SetAttribute("resolution", string.Format("{0}x{1}", width.Value, height.Value));
+ writer.WriteAttributeString("resolution", string.Format("{0}x{1}", width.Value, height.Value));
}
- element.AppendChild(res);
+ writer.WriteString(albumartUrlInfo.Url);
+
+ writer.WriteFullEndElement();
}
private ImageDownloadInfo GetImageInfo(BaseItem item)
diff --git a/MediaBrowser.Dlna/Didl/Filter.cs b/Emby.Dlna/Didl/Filter.cs
index c980a2a2e9..5e9aeb530a 100644
--- a/MediaBrowser.Dlna/Didl/Filter.cs
+++ b/Emby.Dlna/Didl/Filter.cs
@@ -3,7 +3,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
-namespace MediaBrowser.Dlna.Didl
+namespace Emby.Dlna.Didl
{
public class Filter
{
diff --git a/Emby.Dlna/Didl/StringWriterWithEncoding.cs b/Emby.Dlna/Didl/StringWriterWithEncoding.cs
new file mode 100644
index 0000000000..052d6610b1
--- /dev/null
+++ b/Emby.Dlna/Didl/StringWriterWithEncoding.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Emby.Dlna.Didl
+{
+ public class StringWriterWithEncoding : StringWriter
+ {
+ private readonly Encoding _encoding;
+
+ public StringWriterWithEncoding()
+ {
+ }
+
+ public StringWriterWithEncoding(IFormatProvider formatProvider)
+ : base(formatProvider)
+ {
+ }
+
+ public StringWriterWithEncoding(StringBuilder sb)
+ : base(sb)
+ {
+ }
+
+ public StringWriterWithEncoding(StringBuilder sb, IFormatProvider formatProvider)
+ : base(sb, formatProvider)
+ {
+ }
+
+
+ public StringWriterWithEncoding(Encoding encoding)
+ {
+ _encoding = encoding;
+ }
+
+ public StringWriterWithEncoding(IFormatProvider formatProvider, Encoding encoding)
+ : base(formatProvider)
+ {
+ _encoding = encoding;
+ }
+
+ public StringWriterWithEncoding(StringBuilder sb, Encoding encoding)
+ : base(sb)
+ {
+ _encoding = encoding;
+ }
+
+ public StringWriterWithEncoding(StringBuilder sb, IFormatProvider formatProvider, Encoding encoding)
+ : base(sb, formatProvider)
+ {
+ _encoding = encoding;
+ }
+
+ public override Encoding Encoding
+ {
+ get { return (null == _encoding) ? base.Encoding : _encoding; }
+ }
+ }
+}
diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs
index aae157e7a8..4daaa50464 100644
--- a/MediaBrowser.Dlna/DlnaManager.cs
+++ b/Emby.Dlna/DlnaManager.cs
@@ -4,8 +4,8 @@ using MediaBrowser.Controller;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Dlna.Profiles;
-using MediaBrowser.Dlna.Server;
+using Emby.Dlna.Profiles;
+using Emby.Dlna.Server;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Logging;
@@ -16,9 +16,10 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
-using CommonIO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Reflection;
-namespace MediaBrowser.Dlna
+namespace Emby.Dlna
{
public class DlnaManager : IDlnaManager
{
@@ -28,6 +29,7 @@ namespace MediaBrowser.Dlna
private readonly ILogger _logger;
private readonly IJsonSerializer _jsonSerializer;
private readonly IServerApplicationHost _appHost;
+ private readonly IAssemblyInfo _assemblyInfo;
private readonly Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>> _profiles = new Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>>(StringComparer.Ordinal);
@@ -35,7 +37,7 @@ namespace MediaBrowser.Dlna
IFileSystem fileSystem,
IApplicationPaths appPaths,
ILogger logger,
- IJsonSerializer jsonSerializer, IServerApplicationHost appHost)
+ IJsonSerializer jsonSerializer, IServerApplicationHost appHost, IAssemblyInfo assemblyInfo)
{
_xmlSerializer = xmlSerializer;
_fileSystem = fileSystem;
@@ -43,6 +45,7 @@ namespace MediaBrowser.Dlna
_logger = logger;
_jsonSerializer = jsonSerializer;
_appHost = appHost;
+ _assemblyInfo = assemblyInfo;
}
public void InitProfiles()
@@ -235,6 +238,12 @@ namespace MediaBrowser.Dlna
private bool IsMatch(IDictionary<string, string> headers, HttpHeaderInfo header)
{
+ // Handle invalid user setup
+ if (string.IsNullOrWhiteSpace(header.Name))
+ {
+ return false;
+ }
+
string value;
if (headers.TryGetValue(header.Name, out value))
@@ -277,19 +286,29 @@ namespace MediaBrowser.Dlna
{
try
{
- return _fileSystem.GetFiles(path)
+ var allFiles = _fileSystem.GetFiles(path)
+ .ToList();
+
+ var xmlFies = allFiles
.Where(i => string.Equals(i.Extension, ".xml", StringComparison.OrdinalIgnoreCase))
- .Select(i => ParseProfileXmlFile(i.FullName, type))
+ .ToList();
+
+ var parseFiles = new List<FileSystemMetadata>();
+
+ parseFiles.AddRange(xmlFies);
+
+ return parseFiles
+ .Select(i => ParseProfileFile(i.FullName, type))
.Where(i => i != null)
.ToList();
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
return new List<DeviceProfile>();
}
}
- private DeviceProfile ParseProfileXmlFile(string path, DeviceProfileType type)
+ private DeviceProfile ParseProfileFile(string path, DeviceProfileType type)
{
lock (_profiles)
{
@@ -301,7 +320,18 @@ namespace MediaBrowser.Dlna
try
{
- var profile = (DeviceProfile)_xmlSerializer.DeserializeFromFile(typeof(DeviceProfile), path);
+ DeviceProfile profile;
+
+ if (string.Equals(Path.GetExtension(path), ".xml", StringComparison.OrdinalIgnoreCase))
+ {
+ var tempProfile = (DeviceProfile)_xmlSerializer.DeserializeFromFile(typeof(DeviceProfile), path);
+
+ profile = ReserializeProfile(tempProfile);
+ }
+ else
+ {
+ profile = (DeviceProfile)_jsonSerializer.DeserializeFromFile(typeof(DeviceProfile), path);
+ }
profile.Id = path.ToLower().GetMD5().ToString("N");
profile.ProfileType = type;
@@ -312,7 +342,7 @@ namespace MediaBrowser.Dlna
}
catch (Exception ex)
{
- _logger.ErrorException("Error parsing profile xml: {0}", ex, path);
+ _logger.ErrorException("Error parsing profile file: {0}", ex, path);
return null;
}
@@ -328,7 +358,7 @@ namespace MediaBrowser.Dlna
var info = GetProfileInfosInternal().First(i => string.Equals(i.Info.Id, id, StringComparison.OrdinalIgnoreCase));
- return ParseProfileXmlFile(info.Path, info.Info.Type);
+ return ParseProfileFile(info.Path, info.Info.Type);
}
private IEnumerable<InternalProfileInfo> GetProfileInfosInternal()
@@ -348,21 +378,6 @@ namespace MediaBrowser.Dlna
return GetProfileInfosInternal().Select(i => i.Info);
}
- private IEnumerable<InternalProfileInfo> GetProfileInfos(string path, DeviceProfileType type)
- {
- try
- {
- return _fileSystem.GetFiles(path)
- .Where(i => string.Equals(i.Extension, ".xml", StringComparison.OrdinalIgnoreCase))
- .Select(i => GetInternalProfileInfo(i, type))
- .ToList();
- }
- catch (DirectoryNotFoundException)
- {
- return new List<InternalProfileInfo>();
- }
- }
-
private InternalProfileInfo GetInternalProfileInfo(FileSystemMetadata file, DeviceProfileType type)
{
return new InternalProfileInfo
@@ -380,12 +395,11 @@ namespace MediaBrowser.Dlna
private void ExtractSystemProfiles()
{
- var assembly = GetType().Assembly;
var namespaceName = GetType().Namespace + ".Profiles.Xml.";
var systemProfilesPath = SystemProfilesPath;
- foreach (var name in assembly.GetManifestResourceNames()
+ foreach (var name in _assemblyInfo.GetManifestResourceNames(GetType())
.Where(i => i.StartsWith(namespaceName))
.ToList())
{
@@ -393,15 +407,15 @@ namespace MediaBrowser.Dlna
var path = Path.Combine(systemProfilesPath, filename);
- using (var stream = assembly.GetManifestResourceStream(name))
+ using (var stream = _assemblyInfo.GetManifestResourceStream(GetType(), name))
{
- var fileInfo = new FileInfo(path);
+ var fileInfo = _fileSystem.GetFileInfo(path);
if (!fileInfo.Exists || fileInfo.Length != stream.Length)
{
_fileSystem.CreateDirectory(systemProfilesPath);
- using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
+ using (var fileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
{
stream.CopyTo(fileStream);
}
@@ -478,6 +492,11 @@ namespace MediaBrowser.Dlna
{
_profiles[path] = new Tuple<InternalProfileInfo, DeviceProfile>(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile);
}
+ SerializeToXml(profile, path);
+ }
+
+ internal void SerializeToXml(DeviceProfile profile, string path)
+ {
_xmlSerializer.SerializeToFile(profile, path);
}
@@ -521,10 +540,12 @@ namespace MediaBrowser.Dlna
? ImageFormat.Png
: ImageFormat.Jpg;
+ var resource = GetType().Namespace + ".Images." + filename.ToLower();
+
return new ImageStream
{
Format = format,
- Stream = GetType().Assembly.GetManifestResourceStream("MediaBrowser.Dlna.Images." + filename.ToLower())
+ Stream = _assemblyInfo.GetManifestResourceStream(GetType(), resource)
};
}
}
@@ -532,14 +553,16 @@ namespace MediaBrowser.Dlna
class DlnaProfileEntryPoint : IServerEntryPoint
{
private readonly IApplicationPaths _appPaths;
- private readonly IXmlSerializer _xmlSerializer;
+ private readonly IJsonSerializer _jsonSerializer;
private readonly IFileSystem _fileSystem;
+ private readonly IXmlSerializer _xmlSerializer;
- public DlnaProfileEntryPoint(IApplicationPaths appPaths, IXmlSerializer xmlSerializer, IFileSystem fileSystem)
+ public DlnaProfileEntryPoint(IApplicationPaths appPaths, IFileSystem fileSystem, IJsonSerializer jsonSerializer, IXmlSerializer xmlSerializer)
{
_appPaths = appPaths;
- _xmlSerializer = xmlSerializer;
_fileSystem = fileSystem;
+ _jsonSerializer = jsonSerializer;
+ _xmlSerializer = xmlSerializer;
}
public void Run()
diff --git a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj
index ebffe6c575..c83aaecab2 100644
--- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
+++ b/Emby.Dlna/Emby.Dlna.csproj
@@ -1,20 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<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>{734098EB-6DC1-4DD0-A1CA-3140DCD2737C}</ProjectGuid>
+ <ProjectGuid>{805844AB-E92F-45E6-9D99-4F6D48D129A5}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>MediaBrowser.Dlna</RootNamespace>
- <AssemblyName>MediaBrowser.Dlna</AssemblyName>
+ <RootNamespace>Emby.Dlna</RootNamespace>
+ <AssemblyName>Emby.Dlna</AssemblyName>
+ <DefaultLanguage>en-US</DefaultLanguage>
<FileAlignment>512</FileAlignment>
- <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>
- <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
- <TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -24,62 +24,35 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
- <TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
- <DebugType>none</DebugType>
+ <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
- <TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
</PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release Mono|AnyCPU' ">
- <Optimize>false</Optimize>
- <OutputPath>bin\Release Mono</OutputPath>
- <WarningLevel>4</WarningLevel>
- </PropertyGroup>
- <ItemGroup>
- <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
- </Reference>
- <Reference Include="MoreLinq">
- <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
- </Reference>
- <Reference Include="Patterns.Logging">
- <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
- </Reference>
- <Reference Include="Rssdp.NetFx40">
- <HintPath>..\ThirdParty\rssdp\Rssdp.NetFx40.dll</HintPath>
- </Reference>
- <Reference Include="Rssdp.Portable">
- <HintPath>..\ThirdParty\rssdp\Rssdp.Portable.dll</HintPath>
- </Reference>
- <Reference Include="System" />
- <Reference Include="System.Core" />
- <Reference Include="System.Net.Http" />
- <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="Channels\DlnaChannelFactory.cs" />
+ <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="Common\Argument.cs" />
<Compile Include="Eventing\EventManager.cs" />
<Compile Include="Eventing\EventSubscription.cs" />
<Compile Include="Main\DlnaEntryPoint.cs" />
@@ -88,76 +61,74 @@
<Compile Include="MediaReceiverRegistrar\MediaReceiverRegistrarXmlBuilder.cs" />
<Compile Include="MediaReceiverRegistrar\ServiceActionListBuilder.cs" />
<Compile Include="PlayTo\CurrentIdEventArgs.cs" />
- <Compile Include="PlayTo\Device.cs">
- <SubType>Code</SubType>
- </Compile>
- <Compile Include="PlayTo\PlaybackStartEventArgs.cs" />
+ <Compile Include="PlayTo\Device.cs" />
<Compile Include="PlayTo\DeviceInfo.cs" />
- <Compile Include="Common\DeviceService.cs" />
- <Compile Include="Didl\DidlBuilder.cs" />
- <Compile Include="PlayTo\PlayToController.cs" />
- <Compile Include="Profiles\BubbleUpnpProfile.cs" />
- <Compile Include="Profiles\DefaultProfile.cs" />
- <Compile Include="Profiles\DirectTvProfile.cs" />
- <Compile Include="Profiles\DishHopperJoeyProfile.cs" />
- <Compile Include="Profiles\KodiProfile.cs" />
- <Compile Include="Profiles\PopcornHourProfile.cs" />
- <Compile Include="Profiles\SonyBlurayPlayer2016.cs" />
- <Compile Include="Profiles\SonyBlurayPlayer2015.cs" />
- <Compile Include="Profiles\SonyBlurayPlayer2014.cs" />
- <Compile Include="Profiles\SonyBravia2014Profile.cs" />
- <Compile Include="Profiles\SonyPs4Profile.cs" />
- <Compile Include="Profiles\VlcProfile.cs" />
- <Compile Include="Ssdp\Extensions.cs" />
<Compile Include="PlayTo\PlaybackProgressEventArgs.cs" />
+ <Compile Include="PlayTo\PlaybackStartEventArgs.cs" />
<Compile Include="PlayTo\PlaybackStoppedEventArgs.cs" />
- <Compile Include="PlayTo\PlaylistItem.cs">
- <SubType>Code</SubType>
- </Compile>
+ <Compile Include="PlayTo\PlaylistItem.cs" />
<Compile Include="PlayTo\PlaylistItemFactory.cs" />
+ <Compile Include="PlayTo\PlayToController.cs" />
<Compile Include="PlayTo\PlayToManager.cs" />
- <Compile Include="Common\ServiceAction.cs" />
- <Compile Include="PlayTo\TRANSPORTSTATE.cs" />
- <Compile Include="PlayTo\uParserObject.cs" />
- <Compile Include="Profiles\Foobar2000Profile.cs" />
- <Compile Include="Profiles\MediaMonkeyProfile.cs" />
- <Compile Include="ContentDirectory\ContentDirectory.cs" />
- <Compile Include="ContentDirectory\ControlHandler.cs" />
- <Compile Include="ContentDirectory\ServiceActionListBuilder.cs" />
- <Compile Include="ContentDirectory\ContentDirectoryXmlBuilder.cs" />
- <Compile Include="Service\BaseControlHandler.cs" />
- <Compile Include="Service\BaseService.cs" />
- <Compile Include="Service\ControlErrorHandler.cs" />
- <Compile Include="Service\ServiceXmlBuilder.cs" />
- <Compile Include="Server\DescriptionXmlBuilder.cs" />
- <Compile Include="Ssdp\DeviceDiscovery.cs" />
<Compile Include="PlayTo\SsdpHttpClient.cs" />
- <Compile Include="Common\StateVariable.cs" />
<Compile Include="PlayTo\TransportCommands.cs" />
+ <Compile Include="PlayTo\TRANSPORTSTATE.cs" />
<Compile Include="PlayTo\TransportStateEventArgs.cs" />
<Compile Include="PlayTo\uBaseObject.cs" />
- <Compile Include="PlayTo\UpnpContainer.cs" />
- <Compile Include="Common\DeviceIcon.cs" />
<Compile Include="PlayTo\uParser.cs" />
+ <Compile Include="PlayTo\uParserObject.cs" />
+ <Compile Include="PlayTo\UpnpContainer.cs" />
<Compile Include="PlayTo\uPnpNamespaces.cs" />
+ <Compile Include="Profiles\BubbleUpnpProfile.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\KodiProfile.cs" />
<Compile Include="Profiles\LgTvProfile.cs" />
<Compile Include="Profiles\LinksysDMA2100Profile.cs" />
+ <Compile Include="Profiles\MediaMonkeyProfile.cs" />
<Compile Include="Profiles\PanasonicVieraProfile.cs" />
+ <Compile Include="Profiles\PopcornHourProfile.cs" />
<Compile Include="Profiles\SamsungSmartTvProfile.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\VlcProfile.cs" />
<Compile Include="Profiles\WdtvLiveProfile.cs" />
<Compile Include="Profiles\Xbox360Profile.cs" />
<Compile Include="Profiles\XboxOneProfile.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="Server\Headers.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" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Images\logo120.jpg" />
+ <EmbeddedResource Include="Images\logo120.png" />
+ <EmbeddedResource Include="Images\logo240.jpg" />
+ <EmbeddedResource Include="Images\logo240.png" />
+ <EmbeddedResource Include="Images\logo48.jpg" />
+ <EmbeddedResource Include="Images\logo48.png" />
+ <EmbeddedResource Include="Images\people48.jpg" />
+ <EmbeddedResource Include="Images\people48.png" />
+ <EmbeddedResource Include="Images\people480.jpg" />
+ <EmbeddedResource Include="Images\people480.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
@@ -172,90 +143,44 @@
<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 />
<ItemGroup>
+ <EmbeddedResource Include="Profiles\Xml\BubbleUPnp.xml" />
+ <EmbeddedResource Include="Profiles\Xml\Default.xml" />
<EmbeddedResource Include="Profiles\Xml\Denon AVR.xml" />
+ <EmbeddedResource Include="Profiles\Xml\DirecTV HD-DVR.xml" />
+ <EmbeddedResource Include="Profiles\Xml\Dish Hopper-Joey.xml" />
<EmbeddedResource Include="Profiles\Xml\foobar2000.xml" />
+ <EmbeddedResource Include="Profiles\Xml\Kodi.xml" />
<EmbeddedResource Include="Profiles\Xml\LG Smart TV.xml" />
<EmbeddedResource Include="Profiles\Xml\Linksys DMA2100.xml" />
+ <EmbeddedResource Include="Profiles\Xml\MediaMonkey.xml" />
<EmbeddedResource Include="Profiles\Xml\Panasonic Viera.xml" />
+ <EmbeddedResource Include="Profiles\Xml\Popcorn Hour.xml" />
<EmbeddedResource Include="Profiles\Xml\Samsung Smart TV.xml" />
<EmbeddedResource Include="Profiles\Xml\Sony Blu-ray Player 2013.xml" />
+ <EmbeddedResource Include="Profiles\Xml\Sony Blu-ray Player 2014.xml" />
+ <EmbeddedResource Include="Profiles\Xml\Sony Blu-ray Player 2015.xml" />
+ <EmbeddedResource Include="Profiles\Xml\Sony Blu-ray Player 2016.xml" />
<EmbeddedResource Include="Profiles\Xml\Sony Blu-ray Player.xml" />
<EmbeddedResource Include="Profiles\Xml\Sony Bravia %282010%29.xml" />
<EmbeddedResource Include="Profiles\Xml\Sony Bravia %282011%29.xml" />
<EmbeddedResource Include="Profiles\Xml\Sony Bravia %282012%29.xml" />
- <EmbeddedResource Include="Profiles\Xml\Sony Bravia %282013%29.xml">
- <SubType>Designer</SubType>
- </EmbeddedResource>
+ <EmbeddedResource Include="Profiles\Xml\Sony Bravia %282013%29.xml" />
+ <EmbeddedResource Include="Profiles\Xml\Sony Bravia %282014%29.xml" />
<EmbeddedResource Include="Profiles\Xml\Sony PlayStation 3.xml" />
+ <EmbeddedResource Include="Profiles\Xml\Sony PlayStation 4.xml" />
+ <EmbeddedResource Include="Profiles\Xml\Vlc.xml" />
<EmbeddedResource Include="Profiles\Xml\WDTV Live.xml" />
- <EmbeddedResource Include="Profiles\Xml\Xbox 360.xml">
- <SubType>Designer</SubType>
- </EmbeddedResource>
+ <EmbeddedResource Include="Profiles\Xml\Xbox 360.xml" />
<EmbeddedResource Include="Profiles\Xml\Xbox One.xml" />
</ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Profiles\Xml\Default.xml">
- <SubType>Designer</SubType>
- </EmbeddedResource>
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Images\logo120.jpg" />
- <EmbeddedResource Include="Images\logo120.png" />
- <EmbeddedResource Include="Images\logo48.jpg" />
- <EmbeddedResource Include="Images\logo48.png" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Profiles\Xml\MediaMonkey.xml" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Images\logo240.jpg" />
- <EmbeddedResource Include="Images\logo240.png" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Profiles\Xml\DirecTV HD-DVR.xml" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Profiles\Xml\Dish Hopper-Joey.xml" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Profiles\Xml\Popcorn Hour.xml" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Images\people48.jpg" />
- <EmbeddedResource Include="Images\people48.png" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Images\people480.jpg" />
- <EmbeddedResource Include="Images\people480.png" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Profiles\Xml\BubbleUPnp.xml" />
- <EmbeddedResource Include="Profiles\Xml\Vlc.xml" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Profiles\Xml\Sony PlayStation 4.xml" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Profiles\Xml\Kodi.xml">
- <SubType>Designer</SubType>
- </EmbeddedResource>
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Profiles\Xml\Sony Bravia %282014%29.xml">
- <SubType>Designer</SubType>
- </EmbeddedResource>
- </ItemGroup>
- <ItemGroup>
- <None Include="packages.config" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Profiles\Xml\Sony Blu-ray Player 2014.xml" />
- <EmbeddedResource Include="Profiles\Xml\Sony Blu-ray Player 2015.xml" />
- <EmbeddedResource Include="Profiles\Xml\Sony Blu-ray Player 2016.xml" />
- </ItemGroup>
- <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <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">
diff --git a/MediaBrowser.Dlna/Eventing/EventManager.cs b/Emby.Dlna/Eventing/EventManager.cs
index 51c8d2d919..cf2c8d9956 100644
--- a/MediaBrowser.Dlna/Eventing/EventManager.cs
+++ b/Emby.Dlna/Eventing/EventManager.cs
@@ -10,7 +10,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
-namespace MediaBrowser.Dlna.Eventing
+namespace Emby.Dlna.Eventing
{
public class EventManager : IEventManager
{
diff --git a/MediaBrowser.Dlna/Eventing/EventSubscription.cs b/Emby.Dlna/Eventing/EventSubscription.cs
index bd4cbdd55d..adb042d6c4 100644
--- a/MediaBrowser.Dlna/Eventing/EventSubscription.cs
+++ b/Emby.Dlna/Eventing/EventSubscription.cs
@@ -1,6 +1,6 @@
using System;
-namespace MediaBrowser.Dlna.Eventing
+namespace Emby.Dlna.Eventing
{
public class EventSubscription
{
diff --git a/MediaBrowser.Dlna/Images/logo120.jpg b/Emby.Dlna/Images/logo120.jpg
index bbf2df4699..bbf2df4699 100644
--- a/MediaBrowser.Dlna/Images/logo120.jpg
+++ b/Emby.Dlna/Images/logo120.jpg
Binary files differ
diff --git a/MediaBrowser.Dlna/Images/logo120.png b/Emby.Dlna/Images/logo120.png
index 90e5222f26..90e5222f26 100644
--- a/MediaBrowser.Dlna/Images/logo120.png
+++ b/Emby.Dlna/Images/logo120.png
Binary files differ
diff --git a/MediaBrowser.Dlna/Images/logo240.jpg b/Emby.Dlna/Images/logo240.jpg
index 8ff0b69bf9..8ff0b69bf9 100644
--- a/MediaBrowser.Dlna/Images/logo240.jpg
+++ b/Emby.Dlna/Images/logo240.jpg
Binary files differ
diff --git a/MediaBrowser.Dlna/Images/logo240.png b/Emby.Dlna/Images/logo240.png
index 014363e00d..014363e00d 100644
--- a/MediaBrowser.Dlna/Images/logo240.png
+++ b/Emby.Dlna/Images/logo240.png
Binary files differ
diff --git a/MediaBrowser.Dlna/Images/logo48.jpg b/Emby.Dlna/Images/logo48.jpg
index 00045065b4..00045065b4 100644
--- a/MediaBrowser.Dlna/Images/logo48.jpg
+++ b/Emby.Dlna/Images/logo48.jpg
Binary files differ
diff --git a/MediaBrowser.Dlna/Images/logo48.png b/Emby.Dlna/Images/logo48.png
index 606b2ab80d..606b2ab80d 100644
--- a/MediaBrowser.Dlna/Images/logo48.png
+++ b/Emby.Dlna/Images/logo48.png
Binary files differ
diff --git a/MediaBrowser.Dlna/Images/people48.jpg b/Emby.Dlna/Images/people48.jpg
index 06f49a8a2d..06f49a8a2d 100644
--- a/MediaBrowser.Dlna/Images/people48.jpg
+++ b/Emby.Dlna/Images/people48.jpg
Binary files differ
diff --git a/MediaBrowser.Dlna/Images/people48.png b/Emby.Dlna/Images/people48.png
index 7f846373f8..7f846373f8 100644
--- a/MediaBrowser.Dlna/Images/people48.png
+++ b/Emby.Dlna/Images/people48.png
Binary files differ
diff --git a/MediaBrowser.Dlna/Images/people480.jpg b/Emby.Dlna/Images/people480.jpg
index 5b78216349..5b78216349 100644
--- a/MediaBrowser.Dlna/Images/people480.jpg
+++ b/Emby.Dlna/Images/people480.jpg
Binary files differ
diff --git a/MediaBrowser.Dlna/Images/people480.png b/Emby.Dlna/Images/people480.png
index 6d9a668d4e..6d9a668d4e 100644
--- a/MediaBrowser.Dlna/Images/people480.png
+++ b/Emby.Dlna/Images/people480.png
Binary files differ
diff --git a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs
index 277f80b478..16108522c3 100644
--- a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs
+++ b/Emby.Dlna/Main/DlnaEntryPoint.cs
@@ -6,11 +6,10 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
-using MediaBrowser.Dlna.PlayTo;
-using MediaBrowser.Dlna.Ssdp;
+using Emby.Dlna.PlayTo;
+using Emby.Dlna.Ssdp;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
@@ -18,17 +17,21 @@ using System.Linq;
using System.Net;
using System.Threading.Tasks;
using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.System;
+using MediaBrowser.Model.Threading;
using Rssdp;
using Rssdp.Infrastructure;
-namespace MediaBrowser.Dlna.Main
+namespace Emby.Dlna.Main
{
public class DlnaEntryPoint : IServerEntryPoint
{
private readonly IServerConfigurationManager _config;
private readonly ILogger _logger;
private readonly IServerApplicationHost _appHost;
- private readonly INetworkManager _network;
private PlayToManager _manager;
private readonly ISessionManager _sessionManager;
@@ -48,10 +51,16 @@ namespace MediaBrowser.Dlna.Main
private bool _dlnaServerStarted;
private SsdpDevicePublisher _Publisher;
+ private readonly ITimerFactory _timerFactory;
+ private readonly ISocketFactory _socketFactory;
+ private readonly IEnvironmentInfo _environmentInfo;
+ private readonly INetworkManager _networkManager;
+
+ private ISsdpCommunicationsServer _communicationsServer;
+
public DlnaEntryPoint(IServerConfigurationManager config,
ILogManager logManager,
IServerApplicationHost appHost,
- INetworkManager network,
ISessionManager sessionManager,
IHttpClient httpClient,
ILibraryManager libraryManager,
@@ -61,11 +70,10 @@ namespace MediaBrowser.Dlna.Main
IUserDataManager userDataManager,
ILocalizationManager localization,
IMediaSourceManager mediaSourceManager,
- IDeviceDiscovery deviceDiscovery, IMediaEncoder mediaEncoder)
+ IDeviceDiscovery deviceDiscovery, IMediaEncoder mediaEncoder, ISocketFactory socketFactory, ITimerFactory timerFactory, IEnvironmentInfo environmentInfo, INetworkManager networkManager)
{
_config = config;
_appHost = appHost;
- _network = network;
_sessionManager = sessionManager;
_httpClient = httpClient;
_libraryManager = libraryManager;
@@ -77,6 +85,10 @@ namespace MediaBrowser.Dlna.Main
_mediaSourceManager = mediaSourceManager;
_deviceDiscovery = deviceDiscovery;
_mediaEncoder = mediaEncoder;
+ _socketFactory = socketFactory;
+ _timerFactory = timerFactory;
+ _environmentInfo = environmentInfo;
+ _networkManager = networkManager;
_logger = logManager.GetLogger("Dlna");
}
@@ -144,10 +156,20 @@ namespace MediaBrowser.Dlna.Main
{
try
{
- StartPublishing();
+ if (_communicationsServer == null)
+ {
+ var enableMultiSocketBinding = _environmentInfo.OperatingSystem == OperatingSystem.Windows;
+
+ _communicationsServer = new SsdpCommunicationsServer(_socketFactory, _networkManager, _logger, enableMultiSocketBinding)
+ {
+ IsShared = true
+ };
+ }
+
+ StartPublishing(_communicationsServer);
_ssdpHandlerStarted = true;
- StartDeviceDiscovery();
+ StartDeviceDiscovery(_communicationsServer);
}
catch (Exception ex)
{
@@ -157,20 +179,20 @@ namespace MediaBrowser.Dlna.Main
private void LogMessage(string msg)
{
- //_logger.Debug(msg);
+ _logger.Debug(msg);
}
- private void StartPublishing()
+ private void StartPublishing(ISsdpCommunicationsServer communicationsServer)
{
SsdpDevicePublisherBase.LogFunction = LogMessage;
- _Publisher = new SsdpDevicePublisher();
+ _Publisher = new SsdpDevicePublisher(communicationsServer, _timerFactory, _environmentInfo.OperatingSystemName, _environmentInfo.OperatingSystemVersion);
}
- private void StartDeviceDiscovery()
+ private void StartDeviceDiscovery(ISsdpCommunicationsServer communicationsServer)
{
try
{
- ((DeviceDiscovery)_deviceDiscovery).Start();
+ ((DeviceDiscovery)_deviceDiscovery).Start(communicationsServer);
}
catch (Exception ex)
{
@@ -232,6 +254,8 @@ namespace MediaBrowser.Dlna.Main
var addresses = (await _appHost.GetLocalIpAddresses().ConfigureAwait(false)).ToList();
+ var udn = CreateUuid(_appHost.SystemId);
+
foreach (var address in addresses)
{
//if (IPAddress.IsLoopback(address))
@@ -240,13 +264,9 @@ namespace MediaBrowser.Dlna.Main
// continue;
//}
- var addressString = address.ToString();
-
- var udn = CreateUuid(addressString);
-
var fullService = "urn:schemas-upnp-org:device:MediaServer:1";
- _logger.Info("Registering publisher for {0} on {1}", fullService, addressString);
+ _logger.Info("Registering publisher for {0} on {1}", fullService, address.ToString());
var descriptorUri = "/dlna/" + udn + "/description.xml";
var uri = new Uri(_appHost.GetLocalApiUrl(address) + descriptorUri);
@@ -269,7 +289,7 @@ namespace MediaBrowser.Dlna.Main
{
"urn:schemas-upnp-org:service:ContentDirectory:1",
"urn:schemas-upnp-org:service:ConnectionManager:1",
- "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1"
+ //"urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1"
};
foreach (var subDevice in embeddedDevices)
@@ -291,7 +311,12 @@ namespace MediaBrowser.Dlna.Main
private string CreateUuid(string text)
{
- return text.GetMD5().ToString("N");
+ Guid guid;
+ if (!Guid.TryParse(text, out guid))
+ {
+ guid = text.GetMD5();
+ }
+ return guid.ToString("N");
}
private void SetProperies(SsdpDevice device, string fullDeviceType)
@@ -327,7 +352,8 @@ namespace MediaBrowser.Dlna.Main
_userDataManager,
_localization,
_mediaSourceManager,
- _mediaEncoder);
+ _mediaEncoder,
+ _timerFactory);
_manager.Start();
}
@@ -362,6 +388,12 @@ namespace MediaBrowser.Dlna.Main
DisposeDlnaServer();
DisposePlayToManager();
DisposeSsdpHandler();
+
+ if (_communicationsServer != null)
+ {
+ _communicationsServer.Dispose();
+ _communicationsServer = null;
+ }
}
public void DisposeDlnaServer()
diff --git a/MediaBrowser.Dlna/MediaReceiverRegistrar/ControlHandler.cs b/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs
index d1f701711a..daf46b1061 100644
--- a/MediaBrowser.Dlna/MediaReceiverRegistrar/ControlHandler.cs
+++ b/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs
@@ -1,20 +1,17 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Dlna.Server;
-using MediaBrowser.Dlna.Service;
+using Emby.Dlna.Server;
+using Emby.Dlna.Service;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
+using MediaBrowser.Model.Xml;
-namespace MediaBrowser.Dlna.MediaReceiverRegistrar
+namespace Emby.Dlna.MediaReceiverRegistrar
{
public class ControlHandler : BaseControlHandler
{
- public ControlHandler(IServerConfigurationManager config, ILogger logger) : base(config, logger)
- {
- }
-
- protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams)
+ protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams)
{
if (string.Equals(methodName, "IsAuthorized", StringComparison.OrdinalIgnoreCase))
return HandleIsAuthorized();
@@ -26,7 +23,7 @@ namespace MediaBrowser.Dlna.MediaReceiverRegistrar
private IEnumerable<KeyValuePair<string, string>> HandleIsAuthorized()
{
- return new Headers(true)
+ return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "Result", "1" }
};
@@ -34,10 +31,14 @@ namespace MediaBrowser.Dlna.MediaReceiverRegistrar
private IEnumerable<KeyValuePair<string, string>> HandleIsValidated()
{
- return new Headers(true)
+ return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "Result", "1" }
};
}
+
+ public ControlHandler(IServerConfigurationManager config, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(config, logger, xmlReaderSettingsFactory)
+ {
+ }
}
}
diff --git a/MediaBrowser.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs
index a3b2bcee0c..365354efd9 100644
--- a/MediaBrowser.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs
+++ b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs
@@ -1,21 +1,24 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Dlna.Service;
+using Emby.Dlna.Service;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
+using MediaBrowser.Model.Xml;
-namespace MediaBrowser.Dlna.MediaReceiverRegistrar
+namespace Emby.Dlna.MediaReceiverRegistrar
{
public class MediaReceiverRegistrar : BaseService, IMediaReceiverRegistrar, IDisposable
{
private readonly IServerConfigurationManager _config;
+ protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory;
- public MediaReceiverRegistrar(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config)
+ public MediaReceiverRegistrar(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
: base(logger, httpClient)
{
_config = config;
+ XmlReaderSettingsFactory = xmlReaderSettingsFactory;
}
public string GetServiceXml(IDictionary<string, string> headers)
@@ -27,7 +30,7 @@ namespace MediaBrowser.Dlna.MediaReceiverRegistrar
{
return new ControlHandler(
_config,
- Logger)
+ Logger, XmlReaderSettingsFactory)
.ProcessControlRequest(request);
}
diff --git a/MediaBrowser.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs
index cb1fdcecbf..bc4bee7c92 100644
--- a/MediaBrowser.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs
+++ b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs
@@ -1,8 +1,8 @@
-using MediaBrowser.Dlna.Common;
-using MediaBrowser.Dlna.Service;
+using Emby.Dlna.Common;
+using Emby.Dlna.Service;
using System.Collections.Generic;
-namespace MediaBrowser.Dlna.MediaReceiverRegistrar
+namespace Emby.Dlna.MediaReceiverRegistrar
{
public class MediaReceiverRegistrarXmlBuilder
{
diff --git a/MediaBrowser.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs b/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs
index 7e19805db3..691ba3061a 100644
--- a/MediaBrowser.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs
+++ b/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs
@@ -1,7 +1,7 @@
-using MediaBrowser.Dlna.Common;
+using Emby.Dlna.Common;
using System.Collections.Generic;
-namespace MediaBrowser.Dlna.MediaReceiverRegistrar
+namespace Emby.Dlna.MediaReceiverRegistrar
{
public class ServiceActionListBuilder
{
diff --git a/MediaBrowser.Dlna/PlayTo/CurrentIdEventArgs.cs b/Emby.Dlna/PlayTo/CurrentIdEventArgs.cs
index c34293e52b..99aa50bd91 100644
--- a/MediaBrowser.Dlna/PlayTo/CurrentIdEventArgs.cs
+++ b/Emby.Dlna/PlayTo/CurrentIdEventArgs.cs
@@ -1,6 +1,6 @@
using System;
-namespace MediaBrowser.Dlna.PlayTo
+namespace Emby.Dlna.PlayTo
{
public class CurrentIdEventArgs : EventArgs
{
diff --git a/MediaBrowser.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs
index b656bc66ec..f6be479898 100644
--- a/MediaBrowser.Dlna/PlayTo/Device.cs
+++ b/Emby.Dlna/PlayTo/Device.cs
@@ -1,8 +1,9 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Dlna.Common;
-using MediaBrowser.Dlna.Ssdp;
+using Emby.Dlna.Common;
+using Emby.Dlna.Ssdp;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Net;
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -12,17 +13,16 @@ using System.Security;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
+using Emby.Dlna.Server;
+using MediaBrowser.Model.Threading;
-namespace MediaBrowser.Dlna.PlayTo
+namespace Emby.Dlna.PlayTo
{
public class Device : IDisposable
{
- const string ServiceAvtransportType = "urn:schemas-upnp-org:service:AVTransport:1";
- const string ServiceRenderingType = "urn:schemas-upnp-org:service:RenderingControl:1";
-
#region Fields & Properties
- private Timer _timer;
+ private ITimer _timer;
public DeviceInfo Properties { get; set; }
@@ -94,12 +94,15 @@ namespace MediaBrowser.Dlna.PlayTo
public DateTime DateLastActivity { get; private set; }
public Action OnDeviceUnavailable { get; set; }
- public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger, IServerConfigurationManager config)
+ private readonly ITimerFactory _timerFactory;
+
+ public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger, IServerConfigurationManager config, ITimerFactory timerFactory)
{
Properties = deviceProperties;
_httpClient = httpClient;
_logger = logger;
_config = config;
+ _timerFactory = timerFactory;
}
private int GetPlaybackTimerIntervalMs()
@@ -114,7 +117,7 @@ namespace MediaBrowser.Dlna.PlayTo
public void Start()
{
- _timer = new Timer(TimerCallback, null, GetPlaybackTimerIntervalMs(), GetInactiveTimerIntervalMs());
+ _timer = _timerFactory.Create(TimerCallback, null, GetPlaybackTimerIntervalMs(), GetInactiveTimerIntervalMs());
_timerActive = false;
}
@@ -248,13 +251,29 @@ namespace MediaBrowser.Dlna.PlayTo
}
}
+ private DeviceService GetServiceRenderingControl()
+ {
+ var services = Properties.Services;
+
+ return services.FirstOrDefault(s => string.Equals(s.ServiceType, "urn:schemas-upnp-org:service:RenderingControl:1", StringComparison.OrdinalIgnoreCase)) ??
+ services.FirstOrDefault(s => (s.ServiceType ?? string.Empty).StartsWith("urn:schemas-upnp-org:service:RenderingControl", StringComparison.OrdinalIgnoreCase));
+ }
+
+ private DeviceService GetAvTransportService()
+ {
+ var services = Properties.Services;
+
+ return services.FirstOrDefault(s => string.Equals(s.ServiceType, "urn:schemas-upnp-org:service:AVTransport:1", StringComparison.OrdinalIgnoreCase)) ??
+ services.FirstOrDefault(s => (s.ServiceType ?? string.Empty).StartsWith("urn:schemas-upnp-org:service:AVTransport", StringComparison.OrdinalIgnoreCase));
+ }
+
private async Task<bool> SetMute(bool mute)
{
var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetMute");
if (command == null)
return false;
- var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceRenderingType);
+ var service = GetServiceRenderingControl();
if (service == null)
{
@@ -281,7 +300,7 @@ namespace MediaBrowser.Dlna.PlayTo
if (command == null)
return;
- var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceRenderingType);
+ var service = GetServiceRenderingControl();
if (service == null)
{
@@ -302,7 +321,7 @@ namespace MediaBrowser.Dlna.PlayTo
if (command == null)
return;
- var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType);
+ var service = GetAvTransportService();
if (service == null)
{
@@ -327,7 +346,7 @@ namespace MediaBrowser.Dlna.PlayTo
{"CurrentURIMetaData", CreateDidlMeta(metaData)}
};
- var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType);
+ var service = GetAvTransportService();
if (service == null)
{
@@ -358,7 +377,7 @@ namespace MediaBrowser.Dlna.PlayTo
if (string.IsNullOrEmpty(value))
return String.Empty;
- return SecurityElement.Escape(value);
+ return DescriptionXmlBuilder.Escape(value);
}
public async Task SetPlay()
@@ -367,7 +386,7 @@ namespace MediaBrowser.Dlna.PlayTo
if (command == null)
return;
- var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType);
+ var service = GetAvTransportService();
if (service == null)
{
@@ -384,7 +403,7 @@ namespace MediaBrowser.Dlna.PlayTo
if (command == null)
return;
- var service = Properties.Services.First(s => s.ServiceType == ServiceAvtransportType);
+ var service = GetAvTransportService();
await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, 1))
.ConfigureAwait(false);
@@ -396,7 +415,7 @@ namespace MediaBrowser.Dlna.PlayTo
if (command == null)
return;
- var service = Properties.Services.First(s => s.ServiceType == ServiceAvtransportType);
+ var service = GetAvTransportService();
await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, 1))
.ConfigureAwait(false);
@@ -469,7 +488,7 @@ namespace MediaBrowser.Dlna.PlayTo
}
}
}
- catch (WebException ex)
+ catch (HttpException ex)
{
if (_disposed)
return;
@@ -515,7 +534,7 @@ namespace MediaBrowser.Dlna.PlayTo
if (command == null)
return;
- var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceRenderingType);
+ var service = GetServiceRenderingControl();
if (service == null)
{
@@ -548,7 +567,7 @@ namespace MediaBrowser.Dlna.PlayTo
if (command == null)
return;
- var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceRenderingType);
+ var service = GetServiceRenderingControl();
if (service == null)
{
@@ -573,7 +592,7 @@ namespace MediaBrowser.Dlna.PlayTo
if (command == null)
return null;
- var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType);
+ var service = GetAvTransportService();
if (service == null)
return null;
@@ -607,7 +626,7 @@ namespace MediaBrowser.Dlna.PlayTo
if (command == null)
return null;
- var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType);
+ var service = GetAvTransportService();
if (service == null)
{
@@ -638,7 +657,7 @@ namespace MediaBrowser.Dlna.PlayTo
if (command == null)
return new Tuple<bool, uBaseObject>(false, null);
- var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType);
+ var service = GetAvTransportService();
if (service == null)
{
@@ -691,7 +710,7 @@ namespace MediaBrowser.Dlna.PlayTo
}
XElement uPnpResponse;
-
+
// Handle different variations sent back by devices
try
{
@@ -774,28 +793,28 @@ namespace MediaBrowser.Dlna.PlayTo
private async Task GetAVProtocolAsync()
{
- var avService = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType);
+ var avService = GetAvTransportService();
if (avService == null)
return;
string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
var httpClient = new SsdpHttpClient(_httpClient, _config);
- var document = await httpClient.GetDataAsync(url);
+ var document = await httpClient.GetDataAsync(url).ConfigureAwait(false);
AvCommands = TransportCommands.Create(document);
}
private async Task GetRenderingProtocolAsync()
{
- var avService = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceRenderingType);
+ var avService = GetServiceRenderingControl();
if (avService == null)
return;
string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
var httpClient = new SsdpHttpClient(_httpClient, _config);
- var document = await httpClient.GetDataAsync(url);
+ var document = await httpClient.GetDataAsync(url).ConfigureAwait(false);
RendererCommands = TransportCommands.Create(document);
}
@@ -828,7 +847,7 @@ namespace MediaBrowser.Dlna.PlayTo
set;
}
- public static async Task<Device> CreateuPnpDeviceAsync(Uri url, IHttpClient httpClient, IServerConfigurationManager config, ILogger logger)
+ public static async Task<Device> CreateuPnpDeviceAsync(Uri url, IHttpClient httpClient, IServerConfigurationManager config, ILogger logger, ITimerFactory timerFactory)
{
var ssdpHttpClient = new SsdpHttpClient(httpClient, config);
@@ -893,8 +912,6 @@ namespace MediaBrowser.Dlna.PlayTo
deviceProperties.Icon = CreateIcon(icon);
}
- var isRenderer = false;
-
foreach (var services in document.Descendants(uPnpNamespaces.ud.GetName("serviceList")))
{
if (services == null)
@@ -912,17 +929,13 @@ namespace MediaBrowser.Dlna.PlayTo
if (service != null)
{
deviceProperties.Services.Add(service);
- if (service.ServiceType == ServiceAvtransportType)
- {
- isRenderer = true;
- }
}
}
}
- var device = new Device(deviceProperties, httpClient, logger, config);
+ var device = new Device(deviceProperties, httpClient, logger, config, timerFactory);
- if (isRenderer)
+ if (device.GetAvTransportService() != null)
{
await device.GetRenderingProtocolAsync().ConfigureAwait(false);
await device.GetAVProtocolAsync().ConfigureAwait(false);
diff --git a/MediaBrowser.Dlna/PlayTo/DeviceInfo.cs b/Emby.Dlna/PlayTo/DeviceInfo.cs
index 24852466c2..d293bcea14 100644
--- a/MediaBrowser.Dlna/PlayTo/DeviceInfo.cs
+++ b/Emby.Dlna/PlayTo/DeviceInfo.cs
@@ -1,8 +1,8 @@
-using MediaBrowser.Dlna.Common;
+using Emby.Dlna.Common;
using MediaBrowser.Model.Dlna;
using System.Collections.Generic;
-namespace MediaBrowser.Dlna.PlayTo
+namespace Emby.Dlna.PlayTo
{
public class DeviceInfo
{
diff --git a/MediaBrowser.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs
index f6b24e6157..7dff8bda13 100644
--- a/MediaBrowser.Dlna/PlayTo/PlayToController.cs
+++ b/Emby.Dlna/PlayTo/PlayToController.cs
@@ -2,9 +2,8 @@
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Session;
-using MediaBrowser.Dlna.Didl;
+using Emby.Dlna.Didl;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@@ -20,8 +19,9 @@ using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Events;
+using MediaBrowser.Model.Globalization;
-namespace MediaBrowser.Dlna.PlayTo
+namespace Emby.Dlna.PlayTo
{
public class PlayToController : ISessionController, IDisposable
{
diff --git a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs
index 6d6986f017..a93f7da14e 100644
--- a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs
+++ b/Emby.Dlna/PlayTo/PlayToManager.cs
@@ -4,7 +4,6 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Session;
@@ -14,9 +13,13 @@ using System.Linq;
using System.Net;
using System.Threading.Tasks;
using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Events;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Threading;
-namespace MediaBrowser.Dlna.PlayTo
+namespace Emby.Dlna.PlayTo
{
class PlayToManager : IDisposable
{
@@ -36,11 +39,12 @@ namespace MediaBrowser.Dlna.PlayTo
private readonly IDeviceDiscovery _deviceDiscovery;
private readonly IMediaSourceManager _mediaSourceManager;
private readonly IMediaEncoder _mediaEncoder;
+ private readonly ITimerFactory _timerFactory;
private readonly List<string> _nonRendererUrls = new List<string>();
private DateTime _lastRendererClear;
- public PlayToManager(ILogger logger, ISessionManager sessionManager, ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IImageProcessor imageProcessor, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient, IServerConfigurationManager config, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder)
+ public PlayToManager(ILogger logger, ISessionManager sessionManager, ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IImageProcessor imageProcessor, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient, IServerConfigurationManager config, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, ITimerFactory timerFactory)
{
_logger = logger;
_sessionManager = sessionManager;
@@ -56,6 +60,7 @@ namespace MediaBrowser.Dlna.PlayTo
_localization = localization;
_mediaSourceManager = mediaSourceManager;
_mediaEncoder = mediaEncoder;
+ _timerFactory = timerFactory;
}
public void Start()
@@ -105,7 +110,7 @@ namespace MediaBrowser.Dlna.PlayTo
var uri = info.Location;
_logger.Debug("Attempting to create PlayToController from location {0}", location);
- var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger).ConfigureAwait(false);
+ var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger, _timerFactory).ConfigureAwait(false);
if (device.RendererCommands == null)
{
@@ -124,7 +129,16 @@ namespace MediaBrowser.Dlna.PlayTo
if (controller == null)
{
- var serverAddress = await GetServerAddress(info.LocalEndPoint == null ? null : info.LocalEndPoint.Address).ConfigureAwait(false);
+ string serverAddress;
+ if (info.LocalIpAddress == null)
+ {
+ serverAddress = await GetServerAddress(null).ConfigureAwait(false);
+ }
+ else
+ {
+ serverAddress = await GetServerAddress(info.LocalIpAddress).ConfigureAwait(false);
+ }
+
string accessToken = null;
sessionInfo.SessionController = controller = new PlayToController(sessionInfo,
@@ -176,14 +190,14 @@ namespace MediaBrowser.Dlna.PlayTo
}
}
- private Task<string> GetServerAddress(IPAddress localIp)
+ private Task<string> GetServerAddress(IpAddressInfo address)
{
- if (localIp == null)
+ if (address == null)
{
return _appHost.GetLocalApiUrl();
}
- return Task.FromResult(_appHost.GetLocalApiUrl(localIp));
+ return Task.FromResult(_appHost.GetLocalApiUrl(address));
}
public void Dispose()
diff --git a/MediaBrowser.Dlna/PlayTo/PlaybackProgressEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs
index 104697166c..b89f7a8645 100644
--- a/MediaBrowser.Dlna/PlayTo/PlaybackProgressEventArgs.cs
+++ b/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs
@@ -1,6 +1,6 @@
using System;
-namespace MediaBrowser.Dlna.PlayTo
+namespace Emby.Dlna.PlayTo
{
public class PlaybackProgressEventArgs : EventArgs
{
diff --git a/MediaBrowser.Dlna/PlayTo/PlaybackStartEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs
index 772eba55b1..17d2540a50 100644
--- a/MediaBrowser.Dlna/PlayTo/PlaybackStartEventArgs.cs
+++ b/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs
@@ -1,6 +1,6 @@
using System;
-namespace MediaBrowser.Dlna.PlayTo
+namespace Emby.Dlna.PlayTo
{
public class PlaybackStartEventArgs : EventArgs
{
diff --git a/MediaBrowser.Dlna/PlayTo/PlaybackStoppedEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs
index 5cf89a5bb1..847c33ff78 100644
--- a/MediaBrowser.Dlna/PlayTo/PlaybackStoppedEventArgs.cs
+++ b/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs
@@ -1,6 +1,6 @@
using System;
-namespace MediaBrowser.Dlna.PlayTo
+namespace Emby.Dlna.PlayTo
{
public class PlaybackStoppedEventArgs : EventArgs
{
diff --git a/MediaBrowser.Dlna/PlayTo/PlaylistItem.cs b/Emby.Dlna/PlayTo/PlaylistItem.cs
index e1f14bfa2c..b60e6a6fb6 100644
--- a/MediaBrowser.Dlna/PlayTo/PlaylistItem.cs
+++ b/Emby.Dlna/PlayTo/PlaylistItem.cs
@@ -1,6 +1,6 @@
using MediaBrowser.Model.Dlna;
-namespace MediaBrowser.Dlna.PlayTo
+namespace Emby.Dlna.PlayTo
{
public class PlaylistItem
{
diff --git a/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs b/Emby.Dlna/PlayTo/PlaylistItemFactory.cs
index 317ec09699..3eb2bc1d5c 100644
--- a/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs
+++ b/Emby.Dlna/PlayTo/PlaylistItemFactory.cs
@@ -6,7 +6,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
-namespace MediaBrowser.Dlna.PlayTo
+namespace Emby.Dlna.PlayTo
{
public class PlaylistItemFactory
{
diff --git a/MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs b/Emby.Dlna/PlayTo/SsdpHttpClient.cs
index a1eb19d9aa..1aa671b8ff 100644
--- a/MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs
+++ b/Emby.Dlna/PlayTo/SsdpHttpClient.cs
@@ -1,6 +1,6 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Dlna.Common;
+using Emby.Dlna.Common;
using System;
using System.Globalization;
using System.IO;
@@ -8,7 +8,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
-namespace MediaBrowser.Dlna.PlayTo
+namespace Emby.Dlna.PlayTo
{
public class SsdpHttpClient
{
diff --git a/MediaBrowser.Dlna/PlayTo/TRANSPORTSTATE.cs b/Emby.Dlna/PlayTo/TRANSPORTSTATE.cs
index d58c4413c0..93d306a178 100644
--- a/MediaBrowser.Dlna/PlayTo/TRANSPORTSTATE.cs
+++ b/Emby.Dlna/PlayTo/TRANSPORTSTATE.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Dlna.PlayTo
+namespace Emby.Dlna.PlayTo
{
public enum TRANSPORTSTATE
{
diff --git a/MediaBrowser.Dlna/PlayTo/TransportCommands.cs b/Emby.Dlna/PlayTo/TransportCommands.cs
index c49767cfbb..d7d44573c7 100644
--- a/MediaBrowser.Dlna/PlayTo/TransportCommands.cs
+++ b/Emby.Dlna/PlayTo/TransportCommands.cs
@@ -1,11 +1,11 @@
using System;
-using MediaBrowser.Dlna.Common;
+using Emby.Dlna.Common;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
-using MediaBrowser.Dlna.Ssdp;
+using Emby.Dlna.Ssdp;
-namespace MediaBrowser.Dlna.PlayTo
+namespace Emby.Dlna.PlayTo
{
public class TransportCommands
{
diff --git a/MediaBrowser.Dlna/PlayTo/TransportStateEventArgs.cs b/Emby.Dlna/PlayTo/TransportStateEventArgs.cs
index 3e9aad8aef..c6a96f58c1 100644
--- a/MediaBrowser.Dlna/PlayTo/TransportStateEventArgs.cs
+++ b/Emby.Dlna/PlayTo/TransportStateEventArgs.cs
@@ -1,6 +1,6 @@
using System;
-namespace MediaBrowser.Dlna.PlayTo
+namespace Emby.Dlna.PlayTo
{
public class TransportStateEventArgs : EventArgs
{
diff --git a/MediaBrowser.Dlna/PlayTo/UpnpContainer.cs b/Emby.Dlna/PlayTo/UpnpContainer.cs
index e044d6b303..5bfc56bff9 100644
--- a/MediaBrowser.Dlna/PlayTo/UpnpContainer.cs
+++ b/Emby.Dlna/PlayTo/UpnpContainer.cs
@@ -1,8 +1,8 @@
using System;
using System.Xml.Linq;
-using MediaBrowser.Dlna.Ssdp;
+using Emby.Dlna.Ssdp;
-namespace MediaBrowser.Dlna.PlayTo
+namespace Emby.Dlna.PlayTo
{
public class UpnpContainer : uBaseObject
{
diff --git a/MediaBrowser.Dlna/PlayTo/uBaseObject.cs b/Emby.Dlna/PlayTo/uBaseObject.cs
index 7903941c85..1de46317eb 100644
--- a/MediaBrowser.Dlna/PlayTo/uBaseObject.cs
+++ b/Emby.Dlna/PlayTo/uBaseObject.cs
@@ -1,6 +1,6 @@
using System;
-namespace MediaBrowser.Dlna.PlayTo
+namespace Emby.Dlna.PlayTo
{
public class uBaseObject
{
@@ -38,17 +38,17 @@ namespace MediaBrowser.Dlna.PlayTo
{
var classType = UpnpClass ?? string.Empty;
- if (classType.IndexOf(Model.Entities.MediaType.Audio, StringComparison.Ordinal) != -1)
+ if (classType.IndexOf(MediaBrowser.Model.Entities.MediaType.Audio, StringComparison.Ordinal) != -1)
{
- return Model.Entities.MediaType.Audio;
+ return MediaBrowser.Model.Entities.MediaType.Audio;
}
- if (classType.IndexOf(Model.Entities.MediaType.Video, StringComparison.Ordinal) != -1)
+ if (classType.IndexOf(MediaBrowser.Model.Entities.MediaType.Video, StringComparison.Ordinal) != -1)
{
- return Model.Entities.MediaType.Video;
+ return MediaBrowser.Model.Entities.MediaType.Video;
}
if (classType.IndexOf("image", StringComparison.Ordinal) != -1)
{
- return Model.Entities.MediaType.Photo;
+ return MediaBrowser.Model.Entities.MediaType.Photo;
}
return null;
diff --git a/MediaBrowser.Dlna/PlayTo/uParser.cs b/Emby.Dlna/PlayTo/uParser.cs
index 838ddfc317..5caf83a9a6 100644
--- a/MediaBrowser.Dlna/PlayTo/uParser.cs
+++ b/Emby.Dlna/PlayTo/uParser.cs
@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
-namespace MediaBrowser.Dlna.PlayTo
+namespace Emby.Dlna.PlayTo
{
public class uParser
{
diff --git a/MediaBrowser.Dlna/PlayTo/uParserObject.cs b/Emby.Dlna/PlayTo/uParserObject.cs
index 265ef7f8d9..4e75adf1f1 100644
--- a/MediaBrowser.Dlna/PlayTo/uParserObject.cs
+++ b/Emby.Dlna/PlayTo/uParserObject.cs
@@ -1,6 +1,6 @@
using System.Xml.Linq;
-namespace MediaBrowser.Dlna.PlayTo
+namespace Emby.Dlna.PlayTo
{
public class uParserObject
{
diff --git a/MediaBrowser.Dlna/PlayTo/uPnpNamespaces.cs b/Emby.Dlna/PlayTo/uPnpNamespaces.cs
index d44bdceaa8..81acb5e414 100644
--- a/MediaBrowser.Dlna/PlayTo/uPnpNamespaces.cs
+++ b/Emby.Dlna/PlayTo/uPnpNamespaces.cs
@@ -1,6 +1,6 @@
using System.Xml.Linq;
-namespace MediaBrowser.Dlna.PlayTo
+namespace Emby.Dlna.PlayTo
{
public class uPnpNamespaces
{
diff --git a/MediaBrowser.Dlna/Profiles/BubbleUpnpProfile.cs b/Emby.Dlna/Profiles/BubbleUpnpProfile.cs
index 8f3ad82ba4..b551bff2ae 100644
--- a/MediaBrowser.Dlna/Profiles/BubbleUpnpProfile.cs
+++ b/Emby.Dlna/Profiles/BubbleUpnpProfile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class BubbleUpnpProfile : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/DefaultProfile.cs b/Emby.Dlna/Profiles/DefaultProfile.cs
index 48325d0d79..ff30f2e158 100644
--- a/MediaBrowser.Dlna/Profiles/DefaultProfile.cs
+++ b/Emby.Dlna/Profiles/DefaultProfile.cs
@@ -2,7 +2,7 @@
using System.Linq;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class DefaultProfile : DeviceProfile
@@ -30,8 +30,8 @@ namespace MediaBrowser.Dlna.Profiles
MaxIconWidth = 48;
MaxIconHeight = 48;
- MaxStreamingBitrate = 20000000;
- MaxStaticBitrate = 20000000;
+ MaxStreamingBitrate = 24000000;
+ MaxStaticBitrate = 24000000;
MusicStreamingTranscodingBitrate = 192000;
EnableAlbumArtInDidl = false;
diff --git a/MediaBrowser.Dlna/Profiles/DenonAvrProfile.cs b/Emby.Dlna/Profiles/DenonAvrProfile.cs
index fb498c4ce4..eed2449891 100644
--- a/MediaBrowser.Dlna/Profiles/DenonAvrProfile.cs
+++ b/Emby.Dlna/Profiles/DenonAvrProfile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class DenonAvrProfile : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/DirectTvProfile.cs b/Emby.Dlna/Profiles/DirectTvProfile.cs
index c2a007a31a..153d552045 100644
--- a/MediaBrowser.Dlna/Profiles/DirectTvProfile.cs
+++ b/Emby.Dlna/Profiles/DirectTvProfile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class DirectTvProfile : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/DishHopperJoeyProfile.cs b/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs
index bd7b42d5d2..9f31377101 100644
--- a/MediaBrowser.Dlna/Profiles/DishHopperJoeyProfile.cs
+++ b/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class DishHopperJoeyProfile : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/Foobar2000Profile.cs b/Emby.Dlna/Profiles/Foobar2000Profile.cs
index 2c1919c00e..915c490484 100644
--- a/MediaBrowser.Dlna/Profiles/Foobar2000Profile.cs
+++ b/Emby.Dlna/Profiles/Foobar2000Profile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class Foobar2000Profile : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/KodiProfile.cs b/Emby.Dlna/Profiles/KodiProfile.cs
index 5e1ac57608..dbcac6652c 100644
--- a/MediaBrowser.Dlna/Profiles/KodiProfile.cs
+++ b/Emby.Dlna/Profiles/KodiProfile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class KodiProfile : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/LgTvProfile.cs b/Emby.Dlna/Profiles/LgTvProfile.cs
index c98dd04659..faaf63b314 100644
--- a/MediaBrowser.Dlna/Profiles/LgTvProfile.cs
+++ b/Emby.Dlna/Profiles/LgTvProfile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class LgTvProfile : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/LinksysDMA2100Profile.cs b/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs
index 2488cf5423..4a4ecdc589 100644
--- a/MediaBrowser.Dlna/Profiles/LinksysDMA2100Profile.cs
+++ b/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs
@@ -1,7 +1,7 @@
using System.Xml.Serialization;
using MediaBrowser.Model.Dlna;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class LinksysDMA2100Profile : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/MediaMonkeyProfile.cs b/Emby.Dlna/Profiles/MediaMonkeyProfile.cs
index eef847852d..66bde1045b 100644
--- a/MediaBrowser.Dlna/Profiles/MediaMonkeyProfile.cs
+++ b/Emby.Dlna/Profiles/MediaMonkeyProfile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class MediaMonkeyProfile : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs b/Emby.Dlna/Profiles/PanasonicVieraProfile.cs
index 5edf3afbfe..f3d7f59512 100644
--- a/MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs
+++ b/Emby.Dlna/Profiles/PanasonicVieraProfile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class PanasonicVieraProfile : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/PopcornHourProfile.cs b/Emby.Dlna/Profiles/PopcornHourProfile.cs
index 0e1210afbb..0095c80a28 100644
--- a/MediaBrowser.Dlna/Profiles/PopcornHourProfile.cs
+++ b/Emby.Dlna/Profiles/PopcornHourProfile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class PopcornHourProfile : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs b/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs
index aae520d6f0..5acdde327b 100644
--- a/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs
+++ b/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class SamsungSmartTvProfile : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs
index fefb961171..ac7f56b46f 100644
--- a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013.cs
+++ b/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class SonyBlurayPlayer2013 : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2014.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs
index 4f2ff3ad15..961ff30f28 100644
--- a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2014.cs
+++ b/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class SonyBlurayPlayer2014 : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2015.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs
index 57cd5dad67..2573121b17 100644
--- a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2015.cs
+++ b/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class SonyBlurayPlayer2015 : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2016.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs
index f504820d14..1ffed3d62c 100644
--- a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2016.cs
+++ b/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class SonyBlurayPlayer2016 : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayerProfile.cs b/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs
index f6cc036377..d1305d4246 100644
--- a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayerProfile.cs
+++ b/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class SonyBlurayPlayerProfile : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/SonyBravia2010Profile.cs b/Emby.Dlna/Profiles/SonyBravia2010Profile.cs
index a7f74b3697..5185503711 100644
--- a/MediaBrowser.Dlna/Profiles/SonyBravia2010Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2010Profile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class SonyBravia2010Profile : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/SonyBravia2011Profile.cs b/Emby.Dlna/Profiles/SonyBravia2011Profile.cs
index fa258dd600..c21022aa3a 100644
--- a/MediaBrowser.Dlna/Profiles/SonyBravia2011Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2011Profile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class SonyBravia2011Profile : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/SonyBravia2012Profile.cs b/Emby.Dlna/Profiles/SonyBravia2012Profile.cs
index a35cfc0dfa..1bbd40e914 100644
--- a/MediaBrowser.Dlna/Profiles/SonyBravia2012Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2012Profile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class SonyBravia2012Profile : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/SonyBravia2013Profile.cs b/Emby.Dlna/Profiles/SonyBravia2013Profile.cs
index 16ff5dac5f..019bbafcb4 100644
--- a/MediaBrowser.Dlna/Profiles/SonyBravia2013Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2013Profile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class SonyBravia2013Profile : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/SonyBravia2014Profile.cs b/Emby.Dlna/Profiles/SonyBravia2014Profile.cs
index 02dbc88abe..910786b839 100644
--- a/MediaBrowser.Dlna/Profiles/SonyBravia2014Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2014Profile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class SonyBravia2014Profile : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/SonyPs3Profile.cs b/Emby.Dlna/Profiles/SonyPs3Profile.cs
index 6ad2b3fca2..001ef2bd8e 100644
--- a/MediaBrowser.Dlna/Profiles/SonyPs3Profile.cs
+++ b/Emby.Dlna/Profiles/SonyPs3Profile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class SonyPs3Profile : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/SonyPs4Profile.cs b/Emby.Dlna/Profiles/SonyPs4Profile.cs
index dd974d252d..44649911dc 100644
--- a/MediaBrowser.Dlna/Profiles/SonyPs4Profile.cs
+++ b/Emby.Dlna/Profiles/SonyPs4Profile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class SonyPs4Profile : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/VlcProfile.cs b/Emby.Dlna/Profiles/VlcProfile.cs
index 09d290f3ae..2cd0e5cae7 100644
--- a/MediaBrowser.Dlna/Profiles/VlcProfile.cs
+++ b/Emby.Dlna/Profiles/VlcProfile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class VlcProfile : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs b/Emby.Dlna/Profiles/WdtvLiveProfile.cs
index 5f9e30318f..e524816afe 100644
--- a/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs
+++ b/Emby.Dlna/Profiles/WdtvLiveProfile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class WdtvLiveProfile : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/Xbox360Profile.cs b/Emby.Dlna/Profiles/Xbox360Profile.cs
index 5a3e7b7c05..9f0130856f 100644
--- a/MediaBrowser.Dlna/Profiles/Xbox360Profile.cs
+++ b/Emby.Dlna/Profiles/Xbox360Profile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
/// <summary>
/// Good info on xbox 360 requirements: https://code.google.com/p/jems/wiki/XBox360Notes
diff --git a/MediaBrowser.Dlna/Profiles/XboxOneProfile.cs b/Emby.Dlna/Profiles/XboxOneProfile.cs
index 367aa744b8..370534a676 100644
--- a/MediaBrowser.Dlna/Profiles/XboxOneProfile.cs
+++ b/Emby.Dlna/Profiles/XboxOneProfile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Xml.Serialization;
-namespace MediaBrowser.Dlna.Profiles
+namespace Emby.Dlna.Profiles
{
[XmlRoot("Profile")]
public class XboxOneProfile : DefaultProfile
diff --git a/MediaBrowser.Dlna/Profiles/Xml/BubbleUPnp.xml b/Emby.Dlna/Profiles/Xml/BubbleUPnp.xml
index 6ee4fe658e..25f981ee06 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/BubbleUPnp.xml
+++ b/Emby.Dlna/Profiles/Xml/BubbleUPnp.xml
@@ -22,8 +22,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -40,9 +40,9 @@
<DirectPlayProfile container="" type="Photo" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles />
<CodecProfiles />
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Default.xml b/Emby.Dlna/Profiles/Xml/Default.xml
index c13224f37a..81209c1f68 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Default.xml
+++ b/Emby.Dlna/Profiles/Xml/Default.xml
@@ -16,8 +16,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -33,9 +33,9 @@
<DirectPlayProfile container="mp3,wma,aac,wav" type="Audio" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles />
<CodecProfiles />
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml b/Emby.Dlna/Profiles/Xml/Denon AVR.xml
index ea6736d4c1..cdf09df0e9 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml
+++ b/Emby.Dlna/Profiles/Xml/Denon AVR.xml
@@ -21,8 +21,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -37,9 +37,9 @@
<DirectPlayProfile container="mp3,flac,m4a,wma" type="Audio" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles />
<CodecProfiles />
diff --git a/MediaBrowser.Dlna/Profiles/Xml/DirecTV HD-DVR.xml b/Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml
index b5dde8d5c0..b8afed60e8 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/DirecTV HD-DVR.xml
+++ b/Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml
@@ -22,8 +22,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -39,8 +39,8 @@
<DirectPlayProfile container="jpeg,jpg" type="Photo" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mpeg" type="Video" videoCodec="mpeg2video" audioCodec="mp2" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mpeg" type="Video" videoCodec="mpeg2video" audioCodec="mp2" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles />
<CodecProfiles>
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Dish Hopper-Joey.xml b/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml
index daf34b89b9..3ad2a01295 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Dish Hopper-Joey.xml
+++ b/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml
@@ -23,8 +23,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -43,9 +43,9 @@
<DirectPlayProfile container="jpeg" type="Photo" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="mp4" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="mp4" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles />
<CodecProfiles>
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Kodi.xml b/Emby.Dlna/Profiles/Xml/Kodi.xml
index f1083f5e24..58f9e8e235 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Kodi.xml
+++ b/Emby.Dlna/Profiles/Xml/Kodi.xml
@@ -23,7 +23,7 @@
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
<MaxStreamingBitrate>100000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>1280000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -40,9 +40,9 @@
<DirectPlayProfile container="" type="Photo" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles />
<CodecProfiles />
diff --git a/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml b/Emby.Dlna/Profiles/Xml/LG Smart TV.xml
index 945383bdcd..3a185e7331 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml
+++ b/Emby.Dlna/Profiles/Xml/LG Smart TV.xml
@@ -22,8 +22,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -42,9 +42,9 @@
<DirectPlayProfile container="jpeg" type="Photo" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3,aac,mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3,aac,mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles>
<ContainerProfile type="Photo">
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml b/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml
index 195863668f..5723f144bf 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml
+++ b/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml
@@ -20,8 +20,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -37,9 +37,9 @@
<DirectPlayProfile container="avi,mp4,mkv,ts" type="Video" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles />
<CodecProfiles />
diff --git a/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml b/Emby.Dlna/Profiles/Xml/MediaMonkey.xml
index 9518fc4b02..929f6898e4 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml
+++ b/Emby.Dlna/Profiles/Xml/MediaMonkey.xml
@@ -22,8 +22,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -43,9 +43,9 @@
<DirectPlayProfile container="ogg" audioCodec="vorbis" type="Audio" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles />
<CodecProfiles />
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml b/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml
index d26346ff63..3e3049d302 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml
+++ b/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml
@@ -23,8 +23,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -50,9 +50,9 @@
<DirectPlayProfile container="jpeg" type="Photo" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles>
<ContainerProfile type="Photo">
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Popcorn Hour.xml b/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml
index 68fea1733c..bc73d23716 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Popcorn Hour.xml
+++ b/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml
@@ -16,8 +16,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -38,9 +38,9 @@
<DirectPlayProfile container="jpeg,gif,bmp,png" type="Photo" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="mp4" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="mp4" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles />
<CodecProfiles>
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml b/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml
index 1918c02976..31210fac14 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml
+++ b/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml
@@ -22,8 +22,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -50,9 +50,9 @@
<DirectPlayProfile container="jpeg" type="Photo" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="true" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="true" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles>
<ContainerProfile type="Photo">
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml
index f8b583b50f..2bf57d4260 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml
@@ -26,8 +26,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -52,9 +52,9 @@
<DirectPlayProfile container="jpeg,png,gif" type="Photo" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="mkv" type="Video" videoCodec="h264" audioCodec="ac3,aac,mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="mkv" type="Video" videoCodec="h264" audioCodec="ac3,aac,mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles>
<ContainerProfile type="Photo">
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml
index eaa37c6205..703edc35ae 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml
@@ -26,8 +26,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -52,9 +52,9 @@
<DirectPlayProfile container="jpeg,png,gif" type="Photo" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="mkv" type="Video" videoCodec="h264" audioCodec="ac3,aac,mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="mkv" type="Video" videoCodec="h264" audioCodec="ac3,aac,mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles>
<ContainerProfile type="Photo">
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml
index 368e892ff2..69f4b73c64 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml
@@ -24,8 +24,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -50,9 +50,9 @@
<DirectPlayProfile container="jpeg,png,gif" type="Photo" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="mkv" type="Video" videoCodec="h264" audioCodec="ac3,aac,mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="mkv" type="Video" videoCodec="h264" audioCodec="ac3,aac,mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles>
<ContainerProfile type="Photo">
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml
index 9ec096b7f4..a24cfab895 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml
@@ -24,8 +24,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -50,9 +50,9 @@
<DirectPlayProfile container="jpeg,png,gif" type="Photo" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="mkv" type="Video" videoCodec="h264" audioCodec="ac3,aac,mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="mkv" type="Video" videoCodec="h264" audioCodec="ac3,aac,mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles>
<ContainerProfile type="Photo">
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml
index 08266d9438..7ce41dea0a 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml
@@ -24,8 +24,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -47,9 +47,9 @@
<DirectPlayProfile container="jpeg" type="Photo" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="ts" type="Video" videoCodec="mpeg2video" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="mpeg2video" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles>
<ContainerProfile type="Photo">
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2010).xml
index e633f1a89d..600d76ea71 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Bravia (2010).xml
@@ -23,8 +23,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -45,9 +45,9 @@
<DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles>
<ContainerProfile type="Photo">
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml
index 07379c8d5c..232896c8a7 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml
@@ -23,8 +23,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -48,9 +48,9 @@
<DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" type="Audio" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles>
<ContainerProfile type="Photo">
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml
index 61870d7e78..5cf1c5eadb 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml
@@ -23,8 +23,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -50,9 +50,9 @@
<DirectPlayProfile container="jpeg" type="Photo" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles>
<ContainerProfile type="Photo">
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml
index 21fc0ff563..f009d6f11b 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml
@@ -23,8 +23,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -55,9 +55,9 @@
<DirectPlayProfile container="jpeg" type="Photo" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles>
<ContainerProfile type="Photo">
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2014).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml
index bfa304b098..f996652a8c 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2014).xml
+++ b/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml
@@ -23,8 +23,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -55,9 +55,9 @@
<DirectPlayProfile container="jpeg" type="Photo" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles>
<ContainerProfile type="Photo">
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml b/Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml
index 295f6fddb4..3608c343be 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml
+++ b/Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml
@@ -23,8 +23,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -45,9 +45,9 @@
<DirectPlayProfile container="jpeg,png,gif,bmp,tiff" type="Photo" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3,aac,mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3,aac,mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles>
<ContainerProfile type="Photo">
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 4.xml b/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml
index 8f05e8eac4..498f9966fb 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 4.xml
+++ b/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml
@@ -23,8 +23,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -45,9 +45,9 @@
<DirectPlayProfile container="jpeg,png,gif,bmp,tiff" type="Photo" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles>
<ContainerProfile type="Photo">
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Vlc.xml b/Emby.Dlna/Profiles/Xml/Vlc.xml
index 8628e7f30b..3a5b7927bd 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Vlc.xml
+++ b/Emby.Dlna/Profiles/Xml/Vlc.xml
@@ -22,8 +22,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -40,9 +40,9 @@
<DirectPlayProfile container="" type="Photo" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles />
<CodecProfiles />
diff --git a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml b/Emby.Dlna/Profiles/Xml/WDTV Live.xml
index ebd4eb9b58..bf3531a307 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml
+++ b/Emby.Dlna/Profiles/Xml/WDTV Live.xml
@@ -23,8 +23,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -51,9 +51,9 @@
<DirectPlayProfile container="jpeg,png,gif,bmp,tiff" type="Photo" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles>
<ContainerProfile type="Photo">
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml b/Emby.Dlna/Profiles/Xml/Xbox 360.xml
index cd6968bd04..2f0fee8c18 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml
+++ b/Emby.Dlna/Profiles/Xml/Xbox 360.xml
@@ -24,8 +24,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -46,9 +46,9 @@
<DirectPlayProfile container="jpeg" type="Photo" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="asf" type="Video" videoCodec="wmv2" audioCodec="wmav2" estimateContentLength="true" enableMpegtsM2TsMode="false" transcodeSeekInfo="Bytes" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="asf" type="Video" videoCodec="wmv2" audioCodec="wmav2" estimateContentLength="true" enableMpegtsM2TsMode="false" transcodeSeekInfo="Bytes" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles>
<ContainerProfile type="Video" container="mp4,mov">
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml b/Emby.Dlna/Profiles/Xml/Xbox One.xml
index f442e3d66f..e47c308e43 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml
+++ b/Emby.Dlna/Profiles/Xml/Xbox One.xml
@@ -23,8 +23,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -46,9 +46,9 @@
<DirectPlayProfile container="jpeg" type="Photo" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" videoCodec="jpeg" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" videoCodec="jpeg" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles>
<ContainerProfile type="Video" container="mp4,mov">
diff --git a/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml b/Emby.Dlna/Profiles/Xml/foobar2000.xml
index 53187db6dc..51f649e36a 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml
+++ b/Emby.Dlna/Profiles/Xml/foobar2000.xml
@@ -22,8 +22,8 @@
<MaxAlbumArtHeight>480</MaxAlbumArtHeight>
<MaxIconWidth>48</MaxIconWidth>
<MaxIconHeight>48</MaxIconHeight>
- <MaxStreamingBitrate>20000000</MaxStreamingBitrate>
- <MaxStaticBitrate>20000000</MaxStaticBitrate>
+ <MaxStreamingBitrate>24000000</MaxStreamingBitrate>
+ <MaxStaticBitrate>24000000</MaxStaticBitrate>
<MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
<MaxStaticMusicBitrate xsi:nil="true" />
<XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -43,9 +43,9 @@
<DirectPlayProfile container="ogg" audioCodec="vorbis" type="Audio" />
</DirectPlayProfiles>
<TranscodingProfiles>
- <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
- <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+ <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
+ <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" enableSplittingOnNonKeyFrames="false" />
</TranscodingProfiles>
<ContainerProfiles />
<CodecProfiles />
diff --git a/Emby.Dlna/Properties/AssemblyInfo.cs b/Emby.Dlna/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..6f924f9e96
--- /dev/null
+++ b/Emby.Dlna/Properties/AssemblyInfo.cs
@@ -0,0 +1,30 @@
+using System.Resources;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Emby.Dlna2")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Emby.Dlna2")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[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
+//
+// 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/MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs b/Emby.Dlna/Server/DescriptionXmlBuilder.cs
index 37006915c1..2a4a5792fa 100644
--- a/MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs
+++ b/Emby.Dlna/Server/DescriptionXmlBuilder.cs
@@ -1,4 +1,4 @@
-using MediaBrowser.Dlna.Common;
+using Emby.Dlna.Common;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Extensions;
using System;
@@ -8,7 +8,7 @@ using System.Linq;
using System.Security;
using System.Text;
-namespace MediaBrowser.Dlna.Server
+namespace Emby.Dlna.Server
{
public class DescriptionXmlBuilder
{
@@ -94,45 +94,118 @@ namespace MediaBrowser.Dlna.Server
builder.Append("</device>");
}
+ private static readonly char[] s_escapeChars = new char[]
+ {
+ '<',
+ '>',
+ '"',
+ '\'',
+ '&'
+ };
+
+ private static readonly string[] s_escapeStringPairs = new string[]
+{
+ "<",
+ "&lt;",
+ ">",
+ "&gt;",
+ "\"",
+ "&quot;",
+ "'",
+ "&apos;",
+ "&",
+ "&amp;"
+};
+
+ private static string GetEscapeSequence(char c)
+ {
+ int num = s_escapeStringPairs.Length;
+ for (int i = 0; i < num; i += 2)
+ {
+ string text = s_escapeStringPairs[i];
+ string result = s_escapeStringPairs[i + 1];
+ if (text[0] == c)
+ {
+ return result;
+ }
+ }
+ return c.ToString();
+ }
+
+ /// <summary>Replaces invalid XML characters in a string with their valid XML equivalent.</summary>
+ /// <returns>The input string with invalid characters replaced.</returns>
+ /// <param name="str">The string within which to escape invalid characters. </param>
+ public static string Escape(string str)
+ {
+ if (str == null)
+ {
+ return null;
+ }
+ StringBuilder stringBuilder = null;
+ int length = str.Length;
+ int num = 0;
+ while (true)
+ {
+ int num2 = str.IndexOfAny(s_escapeChars, num);
+ if (num2 == -1)
+ {
+ break;
+ }
+ if (stringBuilder == null)
+ {
+ stringBuilder = new StringBuilder();
+ }
+ stringBuilder.Append(str, num, num2 - num);
+ stringBuilder.Append(GetEscapeSequence(str[num2]));
+ num = num2 + 1;
+ }
+ if (stringBuilder == null)
+ {
+ return str;
+ }
+ stringBuilder.Append(str, num, length - num);
+ return stringBuilder.ToString();
+ }
+
private void AppendDeviceProperties(StringBuilder builder)
{
builder.Append("<deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>");
- builder.Append("<dlna:X_DLNACAP>" + SecurityElement.Escape(_profile.XDlnaCap ?? string.Empty) + "</dlna:X_DLNACAP>");
+ builder.Append("<dlna:X_DLNACAP>" + Escape(_profile.XDlnaCap ?? string.Empty) + "</dlna:X_DLNACAP>");
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\">" + SecurityElement.Escape(_profile.XDlnaDoc ?? string.Empty) + "</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("<friendlyName>" + SecurityElement.Escape(GetFriendlyName()) + "</friendlyName>");
- builder.Append("<manufacturer>" + SecurityElement.Escape(_profile.Manufacturer ?? string.Empty) + "</manufacturer>");
- builder.Append("<manufacturerURL>" + SecurityElement.Escape(_profile.ManufacturerUrl ?? string.Empty) + "</manufacturerURL>");
+ builder.Append("<friendlyName>" + Escape(GetFriendlyName()) + "</friendlyName>");
+ builder.Append("<manufacturer>" + Escape(_profile.Manufacturer ?? string.Empty) + "</manufacturer>");
+ builder.Append("<manufacturerURL>" + Escape(_profile.ManufacturerUrl ?? string.Empty) + "</manufacturerURL>");
- builder.Append("<modelDescription>" + SecurityElement.Escape(_profile.ModelDescription ?? string.Empty) + "</modelDescription>");
- builder.Append("<modelName>" + SecurityElement.Escape(_profile.ModelName ?? string.Empty) + "</modelName>");
+ builder.Append("<modelDescription>" + Escape(_profile.ModelDescription ?? string.Empty) + "</modelDescription>");
+ builder.Append("<modelName>" + Escape(_profile.ModelName ?? string.Empty) + "</modelName>");
- builder.Append("<modelNumber>" + SecurityElement.Escape(_profile.ModelNumber ?? string.Empty) + "</modelNumber>");
- builder.Append("<modelURL>" + SecurityElement.Escape(_profile.ModelUrl ?? string.Empty) + "</modelURL>");
+ builder.Append("<modelNumber>" + Escape(_profile.ModelNumber ?? string.Empty) + "</modelNumber>");
+ builder.Append("<modelURL>" + Escape(_profile.ModelUrl ?? string.Empty) + "</modelURL>");
if (string.IsNullOrWhiteSpace(_profile.SerialNumber))
{
- builder.Append("<serialNumber>" + SecurityElement.Escape(_serverId) + "</serialNumber>");
+ builder.Append("<serialNumber>" + Escape(_serverId) + "</serialNumber>");
}
else
{
- builder.Append("<serialNumber>" + SecurityElement.Escape(_profile.SerialNumber) + "</serialNumber>");
+ builder.Append("<serialNumber>" + Escape(_profile.SerialNumber) + "</serialNumber>");
}
- builder.Append("<UDN>uuid:" + SecurityElement.Escape(_serverUdn) + "</UDN>");
- builder.Append("<presentationURL>" + SecurityElement.Escape(_serverAddress) + "</presentationURL>");
+ builder.Append("<UDN>uuid:" + Escape(_serverUdn) + "</UDN>");
+ builder.Append("<presentationURL>" + Escape(_serverAddress) + "</presentationURL>");
if (!EnableAbsoluteUrls)
{
- //builder.Append("<URLBase>" + SecurityElement.Escape(_serverAddress) + "</URLBase>");
+ //builder.Append("<URLBase>" + Escape(_serverAddress) + "</URLBase>");
}
if (!string.IsNullOrWhiteSpace(_profile.SonyAggregationFlags))
{
- builder.Append("<av:aggregationFlags xmlns:av=\"urn:schemas-sony-com:av\">" + SecurityElement.Escape(_profile.SonyAggregationFlags) + "</av:aggregationFlags>");
+ builder.Append("<av:aggregationFlags xmlns:av=\"urn:schemas-sony-com:av\">" + Escape(_profile.SonyAggregationFlags) + "</av:aggregationFlags>");
}
}
@@ -143,7 +216,17 @@ namespace MediaBrowser.Dlna.Server
return "Emby - " + _serverName;
}
- var characters = _serverName.Where(c => (char.IsLetterOrDigit(c) || c == '-')).ToArray();
+ var characterList = new List<char>();
+
+ foreach (var c in _serverName)
+ {
+ if (char.IsLetterOrDigit(c) || c == '-')
+ {
+ characterList.Add(c);
+ }
+ }
+
+ var characters = characterList.ToArray();
var serverName = new string(characters);
@@ -160,10 +243,10 @@ namespace MediaBrowser.Dlna.Server
{
builder.Append("<icon>");
- builder.Append("<mimetype>" + SecurityElement.Escape(icon.MimeType ?? string.Empty) + "</mimetype>");
- builder.Append("<width>" + SecurityElement.Escape(icon.Width.ToString(_usCulture)) + "</width>");
- builder.Append("<height>" + SecurityElement.Escape(icon.Height.ToString(_usCulture)) + "</height>");
- builder.Append("<depth>" + SecurityElement.Escape(icon.Depth ?? string.Empty) + "</depth>");
+ builder.Append("<mimetype>" + Escape(icon.MimeType ?? string.Empty) + "</mimetype>");
+ builder.Append("<width>" + Escape(icon.Width.ToString(_usCulture)) + "</width>");
+ builder.Append("<height>" + Escape(icon.Height.ToString(_usCulture)) + "</height>");
+ builder.Append("<depth>" + Escape(icon.Depth ?? string.Empty) + "</depth>");
builder.Append("<url>" + BuildUrl(icon.Url) + "</url>");
builder.Append("</icon>");
@@ -180,8 +263,8 @@ namespace MediaBrowser.Dlna.Server
{
builder.Append("<service>");
- builder.Append("<serviceType>" + SecurityElement.Escape(service.ServiceType ?? string.Empty) + "</serviceType>");
- builder.Append("<serviceId>" + SecurityElement.Escape(service.ServiceId ?? string.Empty) + "</serviceId>");
+ builder.Append("<serviceType>" + Escape(service.ServiceType ?? string.Empty) + "</serviceType>");
+ builder.Append("<serviceId>" + Escape(service.ServiceId ?? string.Empty) + "</serviceId>");
builder.Append("<SCPDURL>" + BuildUrl(service.ScpdUrl) + "</SCPDURL>");
builder.Append("<controlURL>" + BuildUrl(service.ControlUrl) + "</controlURL>");
builder.Append("<eventSubURL>" + BuildUrl(service.EventSubUrl) + "</eventSubURL>");
@@ -208,7 +291,7 @@ namespace MediaBrowser.Dlna.Server
url = _serverAddress.TrimEnd('/') + url;
}
- return SecurityElement.Escape(url);
+ return Escape(url);
}
private IEnumerable<DeviceIcon> GetIcons()
diff --git a/MediaBrowser.Dlna/Server/UpnpDevice.cs b/Emby.Dlna/Server/UpnpDevice.cs
index 355a35c012..46f3d1c833 100644
--- a/MediaBrowser.Dlna/Server/UpnpDevice.cs
+++ b/Emby.Dlna/Server/UpnpDevice.cs
@@ -1,7 +1,8 @@
using System;
using System.Net;
+using MediaBrowser.Model.Net;
-namespace MediaBrowser.Dlna.Server
+namespace Emby.Dlna.Server
{
public sealed class UpnpDevice
{
@@ -9,9 +10,9 @@ namespace MediaBrowser.Dlna.Server
public readonly string Type;
public readonly string USN;
public readonly string Uuid;
- public readonly IPAddress Address;
+ public readonly IpAddressInfo Address;
- public UpnpDevice(string aUuid, string aType, Uri aDescriptor, IPAddress address)
+ public UpnpDevice(string aUuid, string aType, Uri aDescriptor, IpAddressInfo address)
{
Uuid = aUuid;
Type = aType;
diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs
new file mode 100644
index 0000000000..3092589c12
--- /dev/null
+++ b/Emby.Dlna/Service/BaseControlHandler.cs
@@ -0,0 +1,260 @@
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Dlna;
+using Emby.Dlna.Server;
+using MediaBrowser.Model.Logging;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Xml;
+using Emby.Dlna.Didl;
+using MediaBrowser.Controller.Extensions;
+using MediaBrowser.Model.Xml;
+
+namespace Emby.Dlna.Service
+{
+ public abstract class BaseControlHandler
+ {
+ private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
+
+ protected readonly IServerConfigurationManager Config;
+ protected readonly ILogger Logger;
+ protected readonly IXmlReaderSettingsFactory XmlReaderSettingsFactory;
+
+ protected BaseControlHandler(IServerConfigurationManager config, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
+ {
+ Config = config;
+ Logger = logger;
+ XmlReaderSettingsFactory = xmlReaderSettingsFactory;
+ }
+
+ public ControlResponse ProcessControlRequest(ControlRequest request)
+ {
+ try
+ {
+ var enableDebugLogging = Config.GetDlnaConfiguration().EnableDebugLog;
+
+ if (enableDebugLogging)
+ {
+ LogRequest(request);
+ }
+
+ var response = ProcessControlRequestInternal(request);
+
+ if (enableDebugLogging)
+ {
+ LogResponse(response);
+ }
+
+ return response;
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error processing control request", ex);
+
+ return new ControlErrorHandler().GetResponse(ex);
+ }
+ }
+
+ private ControlResponse ProcessControlRequestInternal(ControlRequest request)
+ {
+ ControlRequestInfo requestInfo = null;
+
+ using (var streamReader = new StreamReader(request.InputXml))
+ {
+ var readerSettings = XmlReaderSettingsFactory.Create(false);
+
+ readerSettings.CheckCharacters = false;
+ readerSettings.IgnoreProcessingInstructions = true;
+ readerSettings.IgnoreComments = true;
+
+ using (var reader = XmlReader.Create(streamReader, readerSettings))
+ {
+ requestInfo = ParseRequest(reader);
+ }
+ }
+
+ Logger.Debug("Received control request {0}", requestInfo.LocalName);
+
+ var result = GetResult(requestInfo.LocalName, requestInfo.Headers);
+
+ var settings = new XmlWriterSettings
+ {
+ Encoding = Encoding.UTF8,
+ CloseOutput = false
+ };
+
+ StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
+
+ using (XmlWriter writer = XmlWriter.Create(builder, settings))
+ {
+ writer.WriteStartDocument(true);
+
+ writer.WriteStartElement("SOAP-ENV", "Envelope", NS_SOAPENV);
+ writer.WriteAttributeString(string.Empty, "encodingStyle", NS_SOAPENV, "http://schemas.xmlsoap.org/soap/encoding/");
+
+ writer.WriteStartElement("SOAP-ENV", "Body", NS_SOAPENV);
+ writer.WriteStartElement("u", requestInfo.LocalName + "Response", requestInfo.NamespaceURI);
+ foreach (var i in result)
+ {
+ writer.WriteStartElement(i.Key);
+ writer.WriteString(i.Value);
+ writer.WriteFullEndElement();
+ }
+ writer.WriteFullEndElement();
+ writer.WriteFullEndElement();
+
+ writer.WriteFullEndElement();
+ writer.WriteEndDocument();
+ }
+
+ var xml = builder.ToString().Replace("xmlns:m=", "xmlns:u=");
+
+ var controlResponse = new ControlResponse
+ {
+ Xml = xml,
+ IsSuccessful = true
+ };
+
+ //Logger.Debug(xml);
+
+ controlResponse.Headers.Add("EXT", string.Empty);
+
+ return controlResponse;
+ }
+
+ private ControlRequestInfo ParseRequest(XmlReader reader)
+ {
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.LocalName)
+ {
+ case "Body":
+ {
+ if (!reader.IsEmptyElement)
+ {
+ using (var subReader = reader.ReadSubtree())
+ {
+ return ParseBodyTag(subReader);
+ }
+ }
+ else
+ {
+ reader.Read();
+ }
+ break;
+ }
+ default:
+ {
+ reader.Skip();
+ break;
+ }
+ }
+ }
+ else
+ {
+ reader.Read();
+ }
+ }
+
+ return new ControlRequestInfo();
+ }
+
+ private ControlRequestInfo ParseBodyTag(XmlReader reader)
+ {
+ var result = new ControlRequestInfo();
+
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ result.LocalName = reader.LocalName;
+ result.NamespaceURI = reader.NamespaceURI;
+
+ if (!reader.IsEmptyElement)
+ {
+ using (var subReader = reader.ReadSubtree())
+ {
+ ParseFirstBodyChild(subReader, result.Headers);
+ return result;
+ }
+ }
+ else
+ {
+ reader.Read();
+ }
+ }
+ else
+ {
+ reader.Read();
+ }
+ }
+
+ return result;
+ }
+
+ private void ParseFirstBodyChild(XmlReader reader, IDictionary<string,string> headers)
+ {
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ // TODO: Should we be doing this here, or should it be handled earlier when decoding the request?
+ headers[reader.LocalName.RemoveDiacritics()] = reader.ReadElementContentAsString();
+ }
+ else
+ {
+ reader.Read();
+ }
+ }
+ }
+
+ private class ControlRequestInfo
+ {
+ public string LocalName;
+ public string NamespaceURI;
+ public IDictionary<string, string> Headers = new Dictionary<string,string>(StringComparer.OrdinalIgnoreCase);
+ }
+
+ protected abstract IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, IDictionary<string, string> methodParams);
+
+ private void LogRequest(ControlRequest request)
+ {
+ var builder = new StringBuilder();
+
+ var headers = string.Join(", ", request.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray());
+ builder.AppendFormat("Headers: {0}", headers);
+ builder.AppendLine();
+ //builder.Append(request.InputXml);
+
+ Logger.LogMultiline("Control request", LogSeverity.Debug, builder);
+ }
+
+ private void LogResponse(ControlResponse response)
+ {
+ var builder = new StringBuilder();
+
+ var headers = string.Join(", ", response.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray());
+ builder.AppendFormat("Headers: {0}", headers);
+ builder.AppendLine();
+ builder.Append(response.Xml);
+
+ Logger.LogMultiline("Control response", LogSeverity.Debug, builder);
+ }
+ }
+}
diff --git a/MediaBrowser.Dlna/Service/BaseService.cs b/Emby.Dlna/Service/BaseService.cs
index aeea7b8f34..574d749588 100644
--- a/MediaBrowser.Dlna/Service/BaseService.cs
+++ b/Emby.Dlna/Service/BaseService.cs
@@ -1,9 +1,9 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Dlna.Eventing;
+using Emby.Dlna.Eventing;
using MediaBrowser.Model.Logging;
-namespace MediaBrowser.Dlna.Service
+namespace Emby.Dlna.Service
{
public class BaseService : IEventManager
{
diff --git a/Emby.Dlna/Service/ControlErrorHandler.cs b/Emby.Dlna/Service/ControlErrorHandler.cs
new file mode 100644
index 0000000000..a3cd77f0fb
--- /dev/null
+++ b/Emby.Dlna/Service/ControlErrorHandler.cs
@@ -0,0 +1,55 @@
+using MediaBrowser.Controller.Dlna;
+using System;
+using System.IO;
+using System.Text;
+using System.Xml;
+using Emby.Dlna.Didl;
+
+namespace Emby.Dlna.Service
+{
+ public class ControlErrorHandler
+ {
+ private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
+
+ public ControlResponse GetResponse(Exception ex)
+ {
+ var settings = new XmlWriterSettings
+ {
+ Encoding = Encoding.UTF8,
+ CloseOutput = false
+ };
+
+ StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
+
+ using (XmlWriter writer = XmlWriter.Create(builder, settings))
+ {
+ writer.WriteStartDocument(true);
+
+ writer.WriteStartElement("SOAP-ENV", "Envelope", NS_SOAPENV);
+ writer.WriteAttributeString(string.Empty, "encodingStyle", NS_SOAPENV, "http://schemas.xmlsoap.org/soap/encoding/");
+
+ writer.WriteStartElement("SOAP-ENV", "Body", NS_SOAPENV);
+ writer.WriteStartElement("SOAP-ENV", "Fault", NS_SOAPENV);
+
+ writer.WriteElementString("faultcode", "500");
+ writer.WriteElementString("faultstring", ex.Message);
+
+ writer.WriteStartElement("detail");
+ writer.WriteRaw("<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\"><errorCode>401</errorCode><errorDescription>Invalid Action</errorDescription></UPnPError>");
+ writer.WriteFullEndElement();
+
+ writer.WriteFullEndElement();
+ writer.WriteFullEndElement();
+
+ writer.WriteFullEndElement();
+ writer.WriteEndDocument();
+ }
+
+ return new ControlResponse
+ {
+ Xml = builder.ToString(),
+ IsSuccessful = false
+ };
+ }
+ }
+}
diff --git a/MediaBrowser.Dlna/Service/ServiceXmlBuilder.cs b/Emby.Dlna/Service/ServiceXmlBuilder.cs
index fae604f85c..08eb804033 100644
--- a/MediaBrowser.Dlna/Service/ServiceXmlBuilder.cs
+++ b/Emby.Dlna/Service/ServiceXmlBuilder.cs
@@ -1,9 +1,10 @@
-using MediaBrowser.Dlna.Common;
+using Emby.Dlna.Common;
using System.Collections.Generic;
using System.Security;
using System.Text;
+using Emby.Dlna.Server;
-namespace MediaBrowser.Dlna.Service
+namespace Emby.Dlna.Service
{
public class ServiceXmlBuilder
{
@@ -35,7 +36,7 @@ namespace MediaBrowser.Dlna.Service
{
builder.Append("<action>");
- builder.Append("<name>" + SecurityElement.Escape(item.Name ?? string.Empty) + "</name>");
+ builder.Append("<name>" + DescriptionXmlBuilder.Escape(item.Name ?? string.Empty) + "</name>");
builder.Append("<argumentList>");
@@ -43,9 +44,9 @@ namespace MediaBrowser.Dlna.Service
{
builder.Append("<argument>");
- builder.Append("<name>" + SecurityElement.Escape(argument.Name ?? string.Empty) + "</name>");
- builder.Append("<direction>" + SecurityElement.Escape(argument.Direction ?? string.Empty) + "</direction>");
- builder.Append("<relatedStateVariable>" + SecurityElement.Escape(argument.RelatedStateVariable ?? string.Empty) + "</relatedStateVariable>");
+ builder.Append("<name>" + DescriptionXmlBuilder.Escape(argument.Name ?? string.Empty) + "</name>");
+ builder.Append("<direction>" + DescriptionXmlBuilder.Escape(argument.Direction ?? string.Empty) + "</direction>");
+ builder.Append("<relatedStateVariable>" + DescriptionXmlBuilder.Escape(argument.RelatedStateVariable ?? string.Empty) + "</relatedStateVariable>");
builder.Append("</argument>");
}
@@ -68,15 +69,15 @@ namespace MediaBrowser.Dlna.Service
builder.Append("<stateVariable sendEvents=\"" + sendEvents + "\">");
- builder.Append("<name>" + SecurityElement.Escape(item.Name ?? string.Empty) + "</name>");
- builder.Append("<dataType>" + SecurityElement.Escape(item.DataType ?? string.Empty) + "</dataType>");
+ 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)
{
builder.Append("<allowedValueList>");
foreach (var allowedValue in item.AllowedValues)
{
- builder.Append("<allowedValue>" + SecurityElement.Escape(allowedValue) + "</allowedValue>");
+ builder.Append("<allowedValue>" + DescriptionXmlBuilder.Escape(allowedValue) + "</allowedValue>");
}
builder.Append("</allowedValueList>");
}
diff --git a/MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs b/Emby.Dlna/Ssdp/DeviceDiscovery.cs
index c9bba526a5..1cd19d010c 100644
--- a/MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs
+++ b/Emby.Dlna/Ssdp/DeviceDiscovery.cs
@@ -7,14 +7,17 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
-using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Events;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Threading;
using Rssdp;
+using Rssdp.Infrastructure;
-namespace MediaBrowser.Dlna.Ssdp
+namespace Emby.Dlna.Ssdp
{
public class DeviceDiscovery : IDeviceDiscovery, IDisposable
{
@@ -27,20 +30,25 @@ namespace MediaBrowser.Dlna.Ssdp
public event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscovered;
public event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceLeft;
- private SsdpDeviceLocator _DeviceLocator;
+ private SsdpDeviceLocator _deviceLocator;
- public DeviceDiscovery(ILogger logger, IServerConfigurationManager config)
+ private readonly ITimerFactory _timerFactory;
+ private readonly ISocketFactory _socketFactory;
+
+ public DeviceDiscovery(ILogger logger, IServerConfigurationManager config, ISocketFactory socketFactory, ITimerFactory timerFactory)
{
_tokenSource = new CancellationTokenSource();
_logger = logger;
_config = config;
+ _socketFactory = socketFactory;
+ _timerFactory = timerFactory;
}
// Call this method from somewhere in your code to start the search.
- public void BeginSearch()
+ public void Start(ISsdpCommunicationsServer communicationsServer)
{
- _DeviceLocator = new SsdpDeviceLocator();
+ _deviceLocator = new SsdpDeviceLocator(communicationsServer, _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
@@ -48,8 +56,8 @@ namespace MediaBrowser.Dlna.Ssdp
//_DeviceLocator.NotificationFilter = "upnp:rootdevice";
// Connect our event handler so we process devices as they are found
- _DeviceLocator.DeviceAvailable += deviceLocator_DeviceAvailable;
- _DeviceLocator.DeviceUnavailable += _DeviceLocator_DeviceUnavailable;
+ _deviceLocator.DeviceAvailable += deviceLocator_DeviceAvailable;
+ _deviceLocator.DeviceUnavailable += _DeviceLocator_DeviceUnavailable;
// Perform a search so we don't have to wait for devices to broadcast notifications
// again to get any results right away (notifications are broadcast periodically).
@@ -65,9 +73,9 @@ namespace MediaBrowser.Dlna.Ssdp
try
{
// Enable listening for notifications (optional)
- _DeviceLocator.StartListeningForNotifications();
+ _deviceLocator.StartListeningForNotifications();
- await _DeviceLocator.SearchAsync().ConfigureAwait(false);
+ await _deviceLocator.SearchAsync().ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -123,11 +131,6 @@ namespace MediaBrowser.Dlna.Ssdp
EventHelper.FireEventIfNotNull(DeviceLeft, this, args, _logger);
}
- public void Start()
- {
- BeginSearch();
- }
-
public void Dispose()
{
if (!_disposed)
diff --git a/MediaBrowser.Dlna/Ssdp/Extensions.cs b/Emby.Dlna/Ssdp/Extensions.cs
index 17ebcc7ead..611bf7e022 100644
--- a/MediaBrowser.Dlna/Ssdp/Extensions.cs
+++ b/Emby.Dlna/Ssdp/Extensions.cs
@@ -1,11 +1,10 @@
using System;
using System.Linq;
using System.Net;
-using System.Net.Sockets;
using System.Threading.Tasks;
using System.Xml.Linq;
-namespace MediaBrowser.Dlna.Ssdp
+namespace Emby.Dlna.Ssdp
{
public static class Extensions
{
diff --git a/Emby.Dlna/project.json b/Emby.Dlna/project.json
new file mode 100644
index 0000000000..fbbe9eaf32
--- /dev/null
+++ b/Emby.Dlna/project.json
@@ -0,0 +1,17 @@
+{
+ "frameworks":{
+ "netstandard1.6":{
+ "dependencies":{
+ "NETStandard.Library":"1.6.0",
+ }
+ },
+ ".NETPortable,Version=v4.5,Profile=Profile7":{
+ "buildOptions": {
+ "define": [ ]
+ },
+ "frameworkAssemblies":{
+
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Mono.Nat/Mono.Nat.csproj b/Emby.Drawing.ImageMagick/Emby.Drawing.ImageMagick.csproj
index 9c27814339..98e99c92b1 100644
--- a/Mono.Nat/Mono.Nat.csproj
+++ b/Emby.Drawing.ImageMagick/Emby.Drawing.ImageMagick.csproj
@@ -4,14 +4,13 @@
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <ProjectGuid>{D7453B88-2266-4805-B39B-2B5A2A33E1BA}</ProjectGuid>
+ <ProjectGuid>{6CFEE013-6E7C-432B-AC37-CABF0880C69A}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>Mono.Nat</RootNamespace>
- <AssemblyName>Mono.Nat</AssemblyName>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <RootNamespace>Emby.Drawing.ImageMagick</RootNamespace>
+ <AssemblyName>Emby.Drawing.ImageMagick</AssemblyName>
+ <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
- <TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -31,6 +30,10 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
+ <Reference Include="ImageMagickSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+ <HintPath>..\packages\ImageMagickSharp.1.0.0.18\lib\net45\ImageMagickSharp.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
@@ -41,49 +44,25 @@
<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="IMapper.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\Mappers\PmpMapper.cs" />
- <Compile Include="Pmp\Pmp.cs" />
- <Compile Include="Pmp\PmpConstants.cs" />
- <Compile Include="Pmp\PmpNatDevice.cs" />
- <Compile Include="Pmp\Searchers\PmpSearcher.cs" />
+ <Compile Include="ImageHelpers.cs" />
+ <Compile Include="ImageMagickEncoder.cs" />
+ <Compile Include="PercentPlayedDrawer.cs" />
+ <Compile Include="PlayedIndicatorDrawer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="Upnp\AsyncResults\GetAllMappingsAsyncResult.cs" />
- <Compile Include="Upnp\AsyncResults\PortMapAsyncResult.cs" />
- <Compile Include="Upnp\Mappers\UpnpMapper.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\Requests\DeletePortMappingMessage.cs" />
- <Compile Include="Upnp\Messages\Requests\GetExternalIPAddressMessage.cs" />
- <Compile Include="Upnp\Messages\Requests\GetGenericPortMappingEntry.cs" />
- <Compile Include="Upnp\Messages\Requests\GetSpecificPortMappingEntryMessage.cs" />
- <Compile Include="Upnp\Messages\Responses\CreatePortMappingResponseMessage.cs" />
- <Compile Include="Upnp\Messages\Responses\DeletePortMappingResponseMessage.cs" />
- <Compile Include="Upnp\Messages\Responses\GetExternalIPAddressResponseMessage.cs" />
- <Compile Include="Upnp\Messages\Responses\GetGenericPortMappingEntryResponseMessage.cs" />
- <Compile Include="Upnp\Messages\UpnpMessage.cs" />
- <Compile Include="Upnp\Searchers\UpnpSearcher.cs" />
- <Compile Include="Upnp\Upnp.cs" />
- <Compile Include="Upnp\UpnpNatDevice.cs" />
+ <Compile Include="StripCollageBuilder.cs" />
+ <Compile Include="UnplayedCountIndicator.cs" />
</ItemGroup>
<ItemGroup>
+ <EmbeddedResource Include="fonts\robotoregular.ttf" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="packages.config" />
+ </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>
diff --git a/Emby.Drawing.ImageMagick/ImageHelpers.cs b/Emby.Drawing.ImageMagick/ImageHelpers.cs
new file mode 100644
index 0000000000..c623c21aa2
--- /dev/null
+++ b/Emby.Drawing.ImageMagick/ImageHelpers.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Emby.Drawing.ImageMagick
+{
+ internal static class ImageHelpers
+ {
+ internal static List<string> ProjectPaths(List<string> paths, int count)
+ {
+ if (count <= 0)
+ {
+ throw new ArgumentOutOfRangeException("count");
+ }
+ if (paths.Count == 0)
+ {
+ throw new ArgumentOutOfRangeException("paths");
+ }
+
+ var list = new List<string>();
+
+ AddToList(list, paths, count);
+
+ return list.Take(count).ToList();
+ }
+
+ private static void AddToList(List<string> list, List<string> paths, int count)
+ {
+ while (list.Count < count)
+ {
+ foreach (var path in paths)
+ {
+ list.Add(path);
+
+ if (list.Count >= count)
+ {
+ return;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs b/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs
index 9820829ae6..39088c94b3 100644
--- a/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs
+++ b/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs
@@ -8,8 +8,7 @@ using MediaBrowser.Model.Logging;
using System;
using System.IO;
using System.Linq;
-using CommonIO;
-using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Model.IO;
namespace Emby.Drawing.ImageMagick
{
@@ -17,17 +16,15 @@ namespace Emby.Drawing.ImageMagick
{
private readonly ILogger _logger;
private readonly IApplicationPaths _appPaths;
- private readonly IHttpClient _httpClient;
+ private readonly Func<IHttpClient> _httpClientFactory;
private readonly IFileSystem _fileSystem;
- private readonly IServerConfigurationManager _config;
- public ImageMagickEncoder(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager config)
+ public ImageMagickEncoder(ILogger logger, IApplicationPaths appPaths, Func<IHttpClient> httpClientFactory, IFileSystem fileSystem)
{
_logger = logger;
_appPaths = appPaths;
- _httpClient = httpClient;
+ _httpClientFactory = httpClientFactory;
_fileSystem = fileSystem;
- _config = config;
LogVersion();
}
@@ -151,7 +148,7 @@ namespace Emby.Drawing.ImageMagick
{
using (var originalImage = new MagickWand(inputPath))
{
- ScaleImage(originalImage, width, height);
+ ScaleImage(originalImage, width, height, options.Blur ?? 0);
if (autoOrient)
{
@@ -173,7 +170,7 @@ namespace Emby.Drawing.ImageMagick
{
using (var originalImage = new MagickWand(inputPath))
{
- ScaleImage(originalImage, width, height);
+ ScaleImage(originalImage, width, height, options.Blur ?? 0);
if (autoOrient)
{
@@ -224,13 +221,13 @@ namespace Emby.Drawing.ImageMagick
}
}
- private void ScaleImage(MagickWand wand, int width, int height)
+ private void ScaleImage(MagickWand wand, int width, int height, int blur)
{
- var highQuality = false;
+ var useResize = blur > 1;
- if (highQuality)
+ if (useResize)
{
- wand.CurrentImage.ResizeImage(width, height);
+ wand.CurrentImage.ResizeImage(width, height, FilterTypes.GaussianFilter, blur);
}
else
{
@@ -258,7 +255,7 @@ namespace Emby.Drawing.ImageMagick
{
var currentImageSize = new ImageSize(imageWidth, imageHeight);
- var task = new PlayedIndicatorDrawer(_appPaths, _httpClient, _fileSystem).DrawPlayedIndicator(wand, currentImageSize);
+ var task = new PlayedIndicatorDrawer(_appPaths, _httpClientFactory(), _fileSystem).DrawPlayedIndicator(wand, currentImageSize);
Task.WaitAll(task);
}
else if (options.UnplayedCount.HasValue)
diff --git a/Emby.Drawing/ImageMagick/PercentPlayedDrawer.cs b/Emby.Drawing.ImageMagick/PercentPlayedDrawer.cs
index 90f9d56095..90f9d56095 100644
--- a/Emby.Drawing/ImageMagick/PercentPlayedDrawer.cs
+++ b/Emby.Drawing.ImageMagick/PercentPlayedDrawer.cs
diff --git a/Emby.Drawing/ImageMagick/PlayedIndicatorDrawer.cs b/Emby.Drawing.ImageMagick/PlayedIndicatorDrawer.cs
index d838410b9f..14fb0ddf15 100644
--- a/Emby.Drawing/ImageMagick/PlayedIndicatorDrawer.cs
+++ b/Emby.Drawing.ImageMagick/PlayedIndicatorDrawer.cs
@@ -5,7 +5,9 @@ using MediaBrowser.Model.Drawing;
using System;
using System.IO;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace Emby.Drawing.ImageMagick
{
diff --git a/MediaBrowser.Model.net35/Properties/AssemblyInfo.cs b/Emby.Drawing.ImageMagick/Properties/AssemblyInfo.cs
index 838cccf662..1089607d67 100644
--- a/MediaBrowser.Model.net35/Properties/AssemblyInfo.cs
+++ b/Emby.Drawing.ImageMagick/Properties/AssemblyInfo.cs
@@ -1,15 +1,16 @@
using System.Reflection;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
-[assembly: AssemblyTitle("MediaBrowser.Model.net35")]
+[assembly: AssemblyTitle("Emby.Drawing.ImageMagick")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("MediaBrowser.Model.net35")]
-[assembly: AssemblyCopyright("Copyright © 2013")]
+[assembly: AssemblyProduct("Emby.Drawing.ImageMagick")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -19,7 +20,7 @@ using System.Runtime.InteropServices;
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("801b3f80-cddc-4a3a-986b-3e7f0293da4b")]
+[assembly: Guid("6cfee013-6e7c-432b-ac37-cabf0880c69a")]
// Version information for an assembly consists of the following four values:
//
@@ -27,4 +28,9 @@ using System.Runtime.InteropServices;
// 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.Drawing/ImageMagick/StripCollageBuilder.cs b/Emby.Drawing.ImageMagick/StripCollageBuilder.cs
index 7bc144c11d..715ab46800 100644
--- a/Emby.Drawing/ImageMagick/StripCollageBuilder.cs
+++ b/Emby.Drawing.ImageMagick/StripCollageBuilder.cs
@@ -2,7 +2,9 @@
using MediaBrowser.Common.Configuration;
using System;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace Emby.Drawing.ImageMagick
{
diff --git a/Emby.Drawing/ImageMagick/UnplayedCountIndicator.cs b/Emby.Drawing.ImageMagick/UnplayedCountIndicator.cs
index 1d35b52c49..c531400992 100644
--- a/Emby.Drawing/ImageMagick/UnplayedCountIndicator.cs
+++ b/Emby.Drawing.ImageMagick/UnplayedCountIndicator.cs
@@ -2,7 +2,9 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Drawing;
using System.Globalization;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace Emby.Drawing.ImageMagick
{
diff --git a/Emby.Drawing.ImageMagick/packages.config b/Emby.Drawing.ImageMagick/packages.config
new file mode 100644
index 0000000000..619310d28e
--- /dev/null
+++ b/Emby.Drawing.ImageMagick/packages.config
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="ImageMagickSharp" version="1.0.0.18" targetFramework="net452" />
+</packages> \ No newline at end of file
diff --git a/Emby.Drawing/GDI/DynamicImageHelpers.cs b/Emby.Drawing.Net/DynamicImageHelpers.cs
index 59340af8a9..1910f7840d 100644
--- a/Emby.Drawing/GDI/DynamicImageHelpers.cs
+++ b/Emby.Drawing.Net/DynamicImageHelpers.cs
@@ -3,9 +3,11 @@ using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
-namespace Emby.Drawing.GDI
+namespace Emby.Drawing.Net
{
public static class DynamicImageHelpers
{
diff --git a/Emby.Drawing.Net/Emby.Drawing.Net.csproj b/Emby.Drawing.Net/Emby.Drawing.Net.csproj
new file mode 100644
index 0000000000..16e72a085b
--- /dev/null
+++ b/Emby.Drawing.Net/Emby.Drawing.Net.csproj
@@ -0,0 +1,78 @@
+<?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>
+ <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>
+ <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
diff --git a/Emby.Drawing/GDI/GDIImageEncoder.cs b/Emby.Drawing.Net/GDIImageEncoder.cs
index afd16899dc..831a579792 100644
--- a/Emby.Drawing/GDI/GDIImageEncoder.cs
+++ b/Emby.Drawing.Net/GDIImageEncoder.cs
@@ -7,10 +7,12 @@ using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
using ImageFormat = MediaBrowser.Model.Drawing.ImageFormat;
-namespace Emby.Drawing.GDI
+namespace Emby.Drawing.Net
{
public class GDIImageEncoder : IImageEncoder
{
@@ -81,7 +83,7 @@ namespace Emby.Drawing.GDI
{
_fileSystem.CreateDirectory(Path.GetDirectoryName(outputPath));
- using (var outputStream = _fileSystem.GetFileStream(outputPath, FileMode.Create, FileAccess.Write, FileShare.Read, false))
+ using (var outputStream = _fileSystem.GetFileStream(outputPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, false))
{
croppedImage.Save(System.Drawing.Imaging.ImageFormat.Png, outputStream, 100);
}
@@ -136,7 +138,7 @@ namespace Emby.Drawing.GDI
_fileSystem.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
// Save to the cache location
- using (var cacheFileStream = _fileSystem.GetFileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, false))
+ using (var cacheFileStream = _fileSystem.GetFileStream(cacheFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, false))
{
// Save to the memory stream
thumbnail.Save(outputFormat, cacheFileStream, quality);
diff --git a/Emby.Drawing/GDI/ImageExtensions.cs b/Emby.Drawing.Net/ImageExtensions.cs
index 6af5a8688f..dec2613d0f 100644
--- a/Emby.Drawing/GDI/ImageExtensions.cs
+++ b/Emby.Drawing.Net/ImageExtensions.cs
@@ -4,7 +4,7 @@ using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
-namespace Emby.Drawing.GDI
+namespace Emby.Drawing.Net
{
public static class ImageExtensions
{
diff --git a/Emby.Drawing/ImageHelpers.cs b/Emby.Drawing.Net/ImageHelpers.cs
index 90bde8b3bd..1afc47cd03 100644
--- a/Emby.Drawing/ImageHelpers.cs
+++ b/Emby.Drawing.Net/ImageHelpers.cs
@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Linq;
-namespace Emby.Drawing
+namespace Emby.Drawing.Net
{
internal static class ImageHelpers
{
diff --git a/Emby.Drawing/GDI/PercentPlayedDrawer.cs b/Emby.Drawing.Net/PercentPlayedDrawer.cs
index c7afda60e4..fac15ba47a 100644
--- a/Emby.Drawing/GDI/PercentPlayedDrawer.cs
+++ b/Emby.Drawing.Net/PercentPlayedDrawer.cs
@@ -1,7 +1,7 @@
using System;
using System.Drawing;
-namespace Emby.Drawing.GDI
+namespace Emby.Drawing.Net
{
public class PercentPlayedDrawer
{
diff --git a/Emby.Drawing/GDI/PlayedIndicatorDrawer.cs b/Emby.Drawing.Net/PlayedIndicatorDrawer.cs
index 4428e4cbac..53683e6f45 100644
--- a/Emby.Drawing/GDI/PlayedIndicatorDrawer.cs
+++ b/Emby.Drawing.Net/PlayedIndicatorDrawer.cs
@@ -1,6 +1,6 @@
using System.Drawing;
-namespace Emby.Drawing.GDI
+namespace Emby.Drawing.Net
{
public class PlayedIndicatorDrawer
{
diff --git a/MediaBrowser.Dlna/Properties/AssemblyInfo.cs b/Emby.Drawing.Net/Properties/AssemblyInfo.cs
index a8403e6a5e..321c3a297c 100644
--- a/MediaBrowser.Dlna/Properties/AssemblyInfo.cs
+++ b/Emby.Drawing.Net/Properties/AssemblyInfo.cs
@@ -1,15 +1,16 @@
using System.Reflection;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
-[assembly: AssemblyTitle("MediaBrowser.Dlna")]
+[assembly: AssemblyTitle("Emby.Drawing.Net")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("MediaBrowser.Dlna")]
-[assembly: AssemblyCopyright("Copyright © 2014")]
+[assembly: AssemblyProduct("Emby.Drawing.Net")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -19,7 +20,7 @@ using System.Runtime.InteropServices;
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("c319ebfa-fd9d-42e4-ae74-a40039a6a688")]
+[assembly: Guid("c97a239e-a96c-4d64-a844-ccf8cc30aecb")]
// Version information for an assembly consists of the following four values:
//
@@ -27,4 +28,9 @@ using System.Runtime.InteropServices;
// 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.Drawing/GDI/UnplayedCountIndicator.cs b/Emby.Drawing.Net/UnplayedCountIndicator.cs
index 6420abb27f..a38abeb324 100644
--- a/Emby.Drawing/GDI/UnplayedCountIndicator.cs
+++ b/Emby.Drawing.Net/UnplayedCountIndicator.cs
@@ -1,6 +1,6 @@
using System.Drawing;
-namespace Emby.Drawing.GDI
+namespace Emby.Drawing.Net
{
public class UnplayedCountIndicator
{
diff --git a/Emby.Drawing/GDI/empty.png b/Emby.Drawing.Net/empty.png
index 42e2b375e5..42e2b375e5 100644
--- a/Emby.Drawing/GDI/empty.png
+++ b/Emby.Drawing.Net/empty.png
Binary files differ
diff --git a/Emby.Drawing/Common/ImageHeader.cs b/Emby.Drawing/Common/ImageHeader.cs
index b5c672a56d..c385779a1e 100644
--- a/Emby.Drawing/Common/ImageHeader.cs
+++ b/Emby.Drawing/Common/ImageHeader.cs
@@ -4,7 +4,9 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace Emby.Drawing.Common
{
@@ -46,7 +48,7 @@ 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)
{
- using (var fs = File.OpenRead(path))
+ 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 aa7dc5a153..90418f6317 100644
--- a/Emby.Drawing/Emby.Drawing.csproj
+++ b/Emby.Drawing/Emby.Drawing.csproj
@@ -9,6 +9,8 @@
<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>
@@ -31,30 +33,8 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
- <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
- </Reference>
- <Reference Include="ImageMagickSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\ImageMagickSharp.1.0.0.18\lib\net45\ImageMagickSharp.dll</HintPath>
- </Reference>
- <Reference Include="Patterns.Logging">
- <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
- </Reference>
- <Reference Include="policy.2.0.taglib-sharp">
- <HintPath>..\packages\taglib.2.1.0.0\lib\policy.2.0.taglib-sharp.dll</HintPath>
- </Reference>
- <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.Xml" />
- <Reference Include="taglib-sharp">
- <HintPath>..\packages\taglib.2.1.0.0\lib\taglib-sharp.dll</HintPath>
+ <Reference Include="TagLib.Portable">
+ <HintPath>..\ThirdParty\taglib\TagLib.Portable.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
@@ -62,25 +42,9 @@
<Link>Properties\SharedVersion.cs</Link>
</Compile>
<Compile Include="Common\ImageHeader.cs" />
- <Compile Include="GDI\DynamicImageHelpers.cs" />
- <Compile Include="GDI\GDIImageEncoder.cs" />
- <Compile Include="GDI\ImageExtensions.cs" />
- <Compile Include="GDI\PercentPlayedDrawer.cs" />
- <Compile Include="GDI\PlayedIndicatorDrawer.cs" />
- <Compile Include="GDI\UnplayedCountIndicator.cs" />
- <Compile Include="IImageEncoder.cs" />
- <Compile Include="ImageHelpers.cs" />
- <Compile Include="ImageMagick\ImageMagickEncoder.cs" />
- <Compile Include="ImageMagick\StripCollageBuilder.cs" />
<Compile Include="ImageProcessor.cs" />
- <Compile Include="ImageMagick\PercentPlayedDrawer.cs" />
- <Compile Include="ImageMagick\PlayedIndicatorDrawer.cs" />
<Compile Include="NullImageEncoder.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="ImageMagick\UnplayedCountIndicator.cs" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="ImageMagick\fonts\robotoregular.ttf" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
@@ -96,13 +60,8 @@
<Name>MediaBrowser.Model</Name>
</ProjectReference>
</ItemGroup>
- <ItemGroup>
- <None Include="packages.config" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="GDI\empty.png" />
- </ItemGroup>
- <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <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">
diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs
index e9f8f81f31..a15f75c9ae 100644
--- a/Emby.Drawing/ImageProcessor.cs
+++ b/Emby.Drawing/ImageProcessor.cs
@@ -15,10 +15,14 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.IO;
using Emby.Drawing.Common;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Threading;
+using TagLib;
namespace Emby.Drawing
{
@@ -61,7 +65,7 @@ namespace Emby.Drawing
IFileSystem fileSystem,
IJsonSerializer jsonSerializer,
IImageEncoder imageEncoder,
- int maxConcurrentImageProcesses, Func<ILibraryManager> libraryManager)
+ int maxConcurrentImageProcesses, Func<ILibraryManager> libraryManager, ITimerFactory timerFactory)
{
_logger = logger;
_fileSystem = fileSystem;
@@ -71,7 +75,7 @@ namespace Emby.Drawing
_appPaths = appPaths;
ImageEnhancers = new List<IImageEnhancer>();
- _saveImageSizeTimer = new Timer(SaveImageSizeCallback, null, Timeout.Infinite, Timeout.Infinite);
+ _saveImageSizeTimer = timerFactory.Create(SaveImageSizeCallback, null, Timeout.Infinite, Timeout.Infinite);
Dictionary<Guid, ImageSize> sizeDictionary;
@@ -85,7 +89,7 @@ namespace Emby.Drawing
// No biggie
sizeDictionary = new Dictionary<Guid, ImageSize>();
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
// No biggie
sizeDictionary = new Dictionary<Guid, ImageSize>();
@@ -152,7 +156,7 @@ namespace Emby.Drawing
{
var file = await ProcessImage(options).ConfigureAwait(false);
- using (var fileStream = _fileSystem.GetFileStream(file.Item1, FileMode.Open, FileAccess.Read, FileShare.Read, true))
+ using (var fileStream = _fileSystem.GetFileStream(file.Item1, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true))
{
await fileStream.CopyToAsync(toStream).ConfigureAwait(false);
}
@@ -232,7 +236,7 @@ namespace Emby.Drawing
var quality = options.Quality;
var outputFormat = GetOutputFormat(options.SupportedOutputFormats[0]);
- var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.BackgroundColor, options.ForegroundLayer);
+ var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.Blur, options.BackgroundColor, options.ForegroundLayer);
var imageProcessingLockTaken = false;
@@ -282,7 +286,7 @@ namespace Emby.Drawing
{
try
{
- File.Copy(src, destination, true);
+ _fileSystem.CopyFile(src, destination, true);
}
catch
{
@@ -428,17 +432,12 @@ namespace Emby.Drawing
return GetResult(croppedImagePath);
}
- var imageProcessingLockTaken = false;
-
try
{
_fileSystem.CreateDirectory(Path.GetDirectoryName(croppedImagePath));
var tmpPath = Path.ChangeExtension(Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString("N")), Path.GetExtension(croppedImagePath));
_fileSystem.CreateDirectory(Path.GetDirectoryName(tmpPath));
- await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false);
- imageProcessingLockTaken = true;
-
_imageEncoder.CropWhiteSpace(originalImagePath, tmpPath);
CopyFile(tmpPath, croppedImagePath);
return GetResult(tmpPath);
@@ -455,13 +454,6 @@ namespace Emby.Drawing
return new Tuple<string, DateTime>(originalImagePath, dateModified);
}
- finally
- {
- if (imageProcessingLockTaken)
- {
- _imageProcessingSemaphore.Release();
- }
- }
}
private Tuple<string, DateTime> GetResult(string path)
@@ -477,7 +469,7 @@ namespace Emby.Drawing
/// <summary>
/// Gets the cache file path based on a set of parameters
/// </summary>
- private string GetCacheFilePath(string originalPath, ImageSize outputSize, int quality, DateTime dateModified, ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, string backgroundColor, string foregroundLayer)
+ private string GetCacheFilePath(string originalPath, ImageSize outputSize, int quality, DateTime dateModified, ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, int? blur, string backgroundColor, string foregroundLayer)
{
var filename = originalPath;
@@ -506,6 +498,11 @@ namespace Emby.Drawing
filename += "p=" + unwatchedCount.Value;
}
+ if (blur.HasValue)
+ {
+ filename += "blur=" + blur.Value;
+ }
+
if (!string.IsNullOrEmpty(backgroundColor))
{
filename += "b=" + backgroundColor;
@@ -576,7 +573,7 @@ namespace Emby.Drawing
{
try
{
- using (var file = TagLib.File.Create(path))
+ using (var file = TagLib.File.Create(new StreamFileAbstraction(Path.GetFileName(path), _fileSystem.OpenRead(path), null)))
{
var image = file as TagLib.Image.File;
@@ -596,7 +593,7 @@ namespace Emby.Drawing
return ImageHeader.GetDimensions(path, _logger, _fileSystem);
}
- private readonly Timer _saveImageSizeTimer;
+ private readonly ITimer _saveImageSizeTimer;
private const int SaveImageSizeTimeout = 5000;
private readonly object _saveImageSizeLock = new object();
private void StartSaveImageSizeTimer()
@@ -778,40 +775,38 @@ namespace Emby.Drawing
// All enhanced images are saved as png to allow transparency
var enhancedImagePath = GetCachePath(EnhancedImageCachePath, cacheGuid + ".png");
- var semaphore = GetLock(enhancedImagePath);
-
- await semaphore.WaitAsync().ConfigureAwait(false);
-
// Check again in case of contention
if (_fileSystem.FileExists(enhancedImagePath))
{
- semaphore.Release();
return enhancedImagePath;
}
- var imageProcessingLockTaken = false;
+ _fileSystem.CreateDirectory(Path.GetDirectoryName(enhancedImagePath));
- try
- {
- _fileSystem.CreateDirectory(Path.GetDirectoryName(enhancedImagePath));
+ var tmpPath = Path.Combine(_appPaths.TempDirectory, Path.ChangeExtension(Guid.NewGuid().ToString(), Path.GetExtension(enhancedImagePath)));
+ _fileSystem.CreateDirectory(Path.GetDirectoryName(tmpPath));
- await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false);
+ await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false);
- imageProcessingLockTaken = true;
+ try
+ {
+ await ExecuteImageEnhancers(supportedEnhancers, originalImagePath, tmpPath, item, imageType, imageIndex).ConfigureAwait(false);
- await ExecuteImageEnhancers(supportedEnhancers, originalImagePath, enhancedImagePath, item, imageType, imageIndex).ConfigureAwait(false);
+ try
+ {
+ _fileSystem.CopyFile(tmpPath, enhancedImagePath, true);
+ }
+ catch
+ {
+
+ }
}
finally
{
- if (imageProcessingLockTaken)
- {
- _imageProcessingSemaphore.Release();
- }
-
- semaphore.Release();
+ _imageProcessingSemaphore.Release();
}
- return enhancedImagePath;
+ return tmpPath;
}
/// <summary>
@@ -837,21 +832,6 @@ namespace Emby.Drawing
}
/// <summary>
- /// The _semaphoreLocks
- /// </summary>
- private readonly ConcurrentDictionary<string, SemaphoreSlim> _semaphoreLocks = new ConcurrentDictionary<string, SemaphoreSlim>();
-
- /// <summary>
- /// Gets the lock.
- /// </summary>
- /// <param name="filename">The filename.</param>
- /// <returns>System.Object.</returns>
- private SemaphoreSlim GetLock(string filename)
- {
- return _semaphoreLocks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1));
- }
-
- /// <summary>
/// Gets the cache path.
/// </summary>
/// <param name="path">The path.</param>
@@ -917,20 +897,11 @@ namespace Emby.Drawing
public async Task CreateImageCollage(ImageCollageOptions options)
{
- await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false);
-
- try
- {
- _logger.Info("Creating image collage and saving to {0}", options.OutputPath);
+ _logger.Info("Creating image collage and saving to {0}", options.OutputPath);
- _imageEncoder.CreateImageCollage(options);
+ _imageEncoder.CreateImageCollage(options);
- _logger.Info("Completed creation of image collage and saved to {0}", options.OutputPath);
- }
- finally
- {
- _imageProcessingSemaphore.Release();
- }
+ _logger.Info("Completed creation of image collage and saved to {0}", options.OutputPath);
}
public IEnumerable<IImageEnhancer> GetSupportedEnhancers(IHasImages item, ImageType imageType)
diff --git a/Emby.Drawing/packages.config b/Emby.Drawing/packages.config
deleted file mode 100644
index af298b3e10..0000000000
--- a/Emby.Drawing/packages.config
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
- <package id="CommonIO" version="1.0.0.9" targetFramework="net45" />
- <package id="ImageMagickSharp" version="1.0.0.18" targetFramework="net45" />
- <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
-</packages> \ No newline at end of file
diff --git a/Emby.Drawing/project.json b/Emby.Drawing/project.json
new file mode 100644
index 0000000000..fbbe9eaf32
--- /dev/null
+++ b/Emby.Drawing/project.json
@@ -0,0 +1,17 @@
+{
+ "frameworks":{
+ "netstandard1.6":{
+ "dependencies":{
+ "NETStandard.Library":"1.6.0",
+ }
+ },
+ ".NETPortable,Version=v4.5,Profile=Profile7":{
+ "buildOptions": {
+ "define": [ ]
+ },
+ "frameworkAssemblies":{
+
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj
new file mode 100644
index 0000000000..21631aeb54
--- /dev/null
+++ b/Emby.Photos/Emby.Photos.csproj
@@ -0,0 +1,68 @@
+<?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>
+ <ItemGroup>
+ <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" />
+ </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
diff --git a/Emby.Photos/Emby.Photos.nuget.targets b/Emby.Photos/Emby.Photos.nuget.targets
new file mode 100644
index 0000000000..e69ce0e64f
--- /dev/null
+++ b/Emby.Photos/Emby.Photos.nuget.targets
@@ -0,0 +1,6 @@
+<?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/Photos/PhotoProvider.cs b/Emby.Photos/PhotoProvider.cs
index c48c3d09be..006f29c2fa 100644
--- a/MediaBrowser.Providers/Photos/PhotoProvider.cs
+++ b/Emby.Photos/PhotoProvider.cs
@@ -1,26 +1,30 @@
-using MediaBrowser.Controller.Entities;
+using System;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
-using System;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
using TagLib;
using TagLib.IFD;
using TagLib.IFD.Entries;
using TagLib.IFD.Tags;
-namespace MediaBrowser.Providers.Photos
+namespace Emby.Photos
{
public class PhotoProvider : ICustomMetadataProvider<Photo>, IHasItemChangeMonitor, IForcedProvider
{
private readonly ILogger _logger;
+ private readonly IFileSystem _fileSystem;
- public PhotoProvider(ILogger logger)
+ public PhotoProvider(ILogger logger, IFileSystem fileSystem)
{
_logger = logger;
+ _fileSystem = fileSystem;
}
public Task<ItemUpdateType> FetchAsync(Photo item, MetadataRefreshOptions options, CancellationToken cancellationToken)
@@ -31,7 +35,7 @@ namespace MediaBrowser.Providers.Photos
try
{
- using (var file = TagLib.File.Create(item.Path))
+ using (var file = TagLib.File.Create(new StreamFileAbstraction(Path.GetFileName(item.Path), _fileSystem.OpenRead(item.Path), null)))
{
var image = file as TagLib.Image.File;
@@ -114,7 +118,7 @@ namespace MediaBrowser.Providers.Photos
}
else
{
- Model.Drawing.ImageOrientation orientation;
+ MediaBrowser.Model.Drawing.ImageOrientation orientation;
if (Enum.TryParse(image.ImageTag.Orientation.ToString(), true, out orientation))
{
item.Orientation = orientation;
diff --git a/MediaBrowser.Common.Implementations/Properties/AssemblyInfo.cs b/Emby.Photos/Properties/AssemblyInfo.cs
index 63a83e0034..49ac833457 100644
--- a/MediaBrowser.Common.Implementations/Properties/AssemblyInfo.cs
+++ b/Emby.Photos/Properties/AssemblyInfo.cs
@@ -1,15 +1,16 @@
using System.Reflection;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
-[assembly: AssemblyTitle("MediaBrowser.Common.Implementations")]
+[assembly: AssemblyTitle("Emby.Photos")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("MediaBrowser.Common.Implementations")]
-[assembly: AssemblyCopyright("Copyright © 2013")]
+[assembly: AssemblyProduct("Emby.Photos")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -19,7 +20,7 @@ using System.Runtime.InteropServices;
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("fc7d85c6-0fe7-4db6-8158-54f7b18f17cd")]
+[assembly: Guid("89ab4548-770d-41fd-a891-8daff44f452c")]
// Version information for an assembly consists of the following four values:
//
@@ -28,3 +29,6 @@ using System.Runtime.InteropServices;
// Build Number
// Revision
//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")] \ No newline at end of file
diff --git a/Emby.Photos/project.json b/Emby.Photos/project.json
new file mode 100644
index 0000000000..fbbe9eaf32
--- /dev/null
+++ b/Emby.Photos/project.json
@@ -0,0 +1,17 @@
+{
+ "frameworks":{
+ "netstandard1.6":{
+ "dependencies":{
+ "NETStandard.Library":"1.6.0",
+ }
+ },
+ ".NETPortable,Version=v4.5,Profile=Profile7":{
+ "buildOptions": {
+ "define": [ ]
+ },
+ "frameworkAssemblies":{
+
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Emby.Server.Core/ApplicationHost.cs b/Emby.Server.Core/ApplicationHost.cs
new file mode 100644
index 0000000000..a0a7416e7b
--- /dev/null
+++ b/Emby.Server.Core/ApplicationHost.cs
@@ -0,0 +1,1661 @@
+using MediaBrowser.Api;
+using MediaBrowser.Common;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Events;
+using MediaBrowser.Common.Extensions;
+using Emby.Common.Implementations.ScheduledTasks;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Common.Progress;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Channels;
+using MediaBrowser.Controller.Chapters;
+using MediaBrowser.Controller.Collections;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Connect;
+using MediaBrowser.Controller.Devices;
+using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Controller.Dto;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.FileOrganization;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Controller.Notifications;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Controller.Playlists;
+using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Controller.Resolvers;
+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;
+using MediaBrowser.MediaEncoding.Encoder;
+using MediaBrowser.MediaEncoding.Subtitles;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.MediaInfo;
+using MediaBrowser.Model.System;
+using MediaBrowser.Model.Updates;
+using MediaBrowser.Providers.Chapters;
+using MediaBrowser.Providers.Manager;
+using MediaBrowser.Providers.Subtitles;
+using MediaBrowser.WebDashboard.Api;
+using MediaBrowser.XbmcMetadata.Providers;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Reflection;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+using System.Threading.Tasks;
+using Emby.Common.Implementations;
+using Emby.Common.Implementations.Archiving;
+using Emby.Common.Implementations.Networking;
+using Emby.Common.Implementations.Reflection;
+using Emby.Common.Implementations.Serialization;
+using Emby.Common.Implementations.TextEncoding;
+using Emby.Common.Implementations.Xml;
+using Emby.Photos;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Api.Playback;
+using MediaBrowser.Common.Plugins;
+using MediaBrowser.Common.Security;
+using MediaBrowser.Common.Updates;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Entities.TV;
+using Emby.Dlna;
+using Emby.Dlna.ConnectionManager;
+using Emby.Dlna.ContentDirectory;
+using Emby.Dlna.Main;
+using Emby.Dlna.MediaReceiverRegistrar;
+using Emby.Dlna.Ssdp;
+using Emby.Server.Core;
+using Emby.Server.Implementations.Activity;
+using Emby.Server.Core.Configuration;
+using Emby.Server.Implementations.Devices;
+using Emby.Server.Implementations.FFMpeg;
+using Emby.Server.Core.IO;
+using Emby.Server.Core.Localization;
+using Emby.Server.Implementations.Migrations;
+using Emby.Server.Implementations.Security;
+using Emby.Server.Implementations.Social;
+using Emby.Server.Implementations.Sync;
+using Emby.Server.Implementations.Channels;
+using Emby.Server.Implementations.Collections;
+using Emby.Server.Implementations.Connect;
+using Emby.Server.Implementations.Dto;
+using Emby.Server.Implementations.EntryPoints;
+using Emby.Server.Implementations.FileOrganization;
+using Emby.Server.Implementations.HttpServer;
+using Emby.Server.Implementations.HttpServer.Security;
+using Emby.Server.Implementations.Library;
+using Emby.Server.Implementations.LiveTv;
+using Emby.Server.Implementations.Localization;
+using Emby.Server.Implementations.MediaEncoder;
+using Emby.Server.Implementations.Notifications;
+using Emby.Server.Implementations.Data;
+using Emby.Server.Implementations.Playlists;
+using Emby.Server.Implementations;
+using Emby.Server.Implementations.ServerManager;
+using Emby.Server.Implementations.Session;
+using Emby.Server.Implementations.Social;
+using Emby.Server.Implementations.Sync;
+using Emby.Server.Implementations.TV;
+using Emby.Server.Implementations.Updates;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.News;
+using MediaBrowser.Model.Reflection;
+using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Services;
+using MediaBrowser.Model.Social;
+using MediaBrowser.Model.Text;
+using MediaBrowser.Model.Xml;
+using OpenSubtitlesHandler;
+using ServiceStack;
+using SocketHttpListener.Primitives;
+using StringExtensions = MediaBrowser.Controller.Extensions.StringExtensions;
+using Emby.Drawing;
+using Emby.Server.Implementations.Migrations;
+using MediaBrowser.Model.Diagnostics;
+using Emby.Common.Implementations.Diagnostics;
+
+namespace Emby.Server.Core
+{
+ /// <summary>
+ /// Class CompositionRoot
+ /// </summary>
+ public abstract class ApplicationHost : BaseApplicationHost<ServerApplicationPaths>, IServerApplicationHost, IDependencyContainer
+ {
+ /// <summary>
+ /// Gets the server configuration manager.
+ /// </summary>
+ /// <value>The server configuration manager.</value>
+ public IServerConfigurationManager ServerConfigurationManager
+ {
+ get { return (IServerConfigurationManager)ConfigurationManager; }
+ }
+
+ /// <summary>
+ /// Gets the configuration manager.
+ /// </summary>
+ /// <returns>IConfigurationManager.</returns>
+ protected override IConfigurationManager GetConfigurationManager()
+ {
+ 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; }
+ /// <summary>
+ /// Gets or sets the user manager.
+ /// </summary>
+ /// <value>The user manager.</value>
+ public IUserManager UserManager { get; set; }
+ /// <summary>
+ /// Gets or sets the library manager.
+ /// </summary>
+ /// <value>The library manager.</value>
+ internal ILibraryManager LibraryManager { get; set; }
+ /// <summary>
+ /// Gets or sets the directory watchers.
+ /// </summary>
+ /// <value>The directory watchers.</value>
+ private ILibraryMonitor LibraryMonitor { get; set; }
+ /// <summary>
+ /// Gets or sets the provider manager.
+ /// </summary>
+ /// <value>The provider manager.</value>
+ private IProviderManager ProviderManager { get; set; }
+ /// <summary>
+ /// Gets or sets the HTTP server.
+ /// </summary>
+ /// <value>The HTTP server.</value>
+ private IHttpServer HttpServer { get; set; }
+ private IDtoService DtoService { get; set; }
+ private IImageProcessor ImageProcessor { get; set; }
+
+ /// <summary>
+ /// Gets or sets the media encoder.
+ /// </summary>
+ /// <value>The media encoder.</value>
+ private IMediaEncoder MediaEncoder { get; set; }
+ private ISubtitleEncoder SubtitleEncoder { get; set; }
+
+ private IConnectManager ConnectManager { get; set; }
+ private ISessionManager SessionManager { get; set; }
+
+ private ILiveTvManager LiveTvManager { get; set; }
+
+ public ILocalizationManager LocalizationManager { get; set; }
+
+ private IEncodingManager EncodingManager { get; set; }
+ private IChannelManager ChannelManager { get; set; }
+ private ISyncManager SyncManager { get; set; }
+
+ /// <summary>
+ /// Gets or sets the user data repository.
+ /// </summary>
+ /// <value>The user data repository.</value>
+ private IUserDataManager UserDataManager { get; set; }
+ private IUserRepository UserRepository { get; set; }
+ internal IDisplayPreferencesRepository DisplayPreferencesRepository { get; set; }
+ internal IItemRepository ItemRepository { get; set; }
+ private INotificationsRepository NotificationsRepository { get; set; }
+ private IFileOrganizationRepository FileOrganizationRepository { get; set; }
+
+ private INotificationManager NotificationManager { get; set; }
+ private ISubtitleManager SubtitleManager { get; set; }
+ private IChapterManager ChapterManager { get; set; }
+ private IDeviceManager DeviceManager { get; set; }
+
+ internal IUserViewManager UserViewManager { get; set; }
+
+ private IAuthenticationRepository AuthenticationRepository { get; set; }
+ private ISyncRepository SyncRepository { get; set; }
+ private ITVSeriesManager TVSeriesManager { get; set; }
+ private ICollectionManager CollectionManager { get; set; }
+ private IMediaSourceManager MediaSourceManager { get; set; }
+ private IPlaylistManager PlaylistManager { get; set; }
+
+ /// <summary>
+ /// Gets or sets the installation manager.
+ /// </summary>
+ /// <value>The installation manager.</value>
+ protected IInstallationManager InstallationManager { get; private set; }
+ /// <summary>
+ /// Gets the security manager.
+ /// </summary>
+ /// <value>The security manager.</value>
+ protected ISecurityManager SecurityManager { get; private set; }
+
+ /// <summary>
+ /// Gets or sets the zip client.
+ /// </summary>
+ /// <value>The zip client.</value>
+ protected IZipClient ZipClient { get; private set; }
+
+ protected IAuthService AuthService { get; private set; }
+
+ protected readonly StartupOptions StartupOptions;
+ private readonly string _releaseAssetFilename;
+
+ internal IPowerManagement PowerManagement { get; private set; }
+ internal IImageEncoder ImageEncoder { get; private set; }
+
+ private readonly Action<string, string> _certificateGenerator;
+ private readonly Func<string> _defaultUserNameFactory;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ApplicationHost" /> class.
+ /// </summary>
+ public ApplicationHost(ServerApplicationPaths applicationPaths,
+ ILogManager logManager,
+ StartupOptions options,
+ IFileSystem fileSystem,
+ IPowerManagement powerManagement,
+ string releaseAssetFilename,
+ IEnvironmentInfo environmentInfo,
+ IImageEncoder imageEncoder,
+ ISystemEvents systemEvents,
+ IMemoryStreamFactory memoryStreamFactory,
+ INetworkManager networkManager,
+ Action<string, string> certificateGenerator,
+ Func<string> defaultUsernameFactory)
+ : base(applicationPaths,
+ logManager,
+ fileSystem,
+ environmentInfo,
+ systemEvents,
+ memoryStreamFactory,
+ networkManager)
+ {
+ StartupOptions = options;
+ _certificateGenerator = certificateGenerator;
+ _releaseAssetFilename = releaseAssetFilename;
+ _defaultUserNameFactory = defaultUsernameFactory;
+ PowerManagement = powerManagement;
+
+ ImageEncoder = imageEncoder;
+
+ SetBaseExceptionMessage();
+ }
+
+ private Version _version;
+ /// <summary>
+ /// Gets the current application version
+ /// </summary>
+ /// <value>The application version.</value>
+ public override Version ApplicationVersion
+ {
+ get
+ {
+ return _version ?? (_version = GetAssembly(GetType()).GetName().Version);
+ }
+ }
+
+ public abstract bool SupportsRunningAsService { get; }
+
+ /// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public override string Name
+ {
+ get
+ {
+ return "Emby Server";
+ }
+ }
+
+ private Assembly GetAssembly(Type type)
+ {
+ return type.GetTypeInfo().Assembly;
+ }
+
+ public abstract bool SupportsAutoRunAtStartup { get; }
+
+ private void SetBaseExceptionMessage()
+ {
+ var builder = GetBaseExceptionMessage(ApplicationPaths);
+
+ // Skip if plugins haven't been loaded yet
+ //if (Plugins != null)
+ //{
+ // var pluginString = string.Join("|", Plugins.Select(i => i.Name + "-" + i.Version.ToString()).ToArray());
+ // builder.Insert(0, string.Format("Plugins: {0}{1}", pluginString, Environment.NewLine));
+ //}
+
+ builder.Insert(0, string.Format("Version: {0}{1}", ApplicationVersion, Environment.NewLine));
+ builder.Insert(0, "*** Error Report ***" + Environment.NewLine);
+
+ LogManager.ExceptionMessagePrefix = builder.ToString();
+ }
+
+ /// <summary>
+ /// Runs the startup tasks.
+ /// </summary>
+ public override async Task RunStartupTasks()
+ {
+ await PerformPreInitMigrations().ConfigureAwait(false);
+
+ await base.RunStartupTasks().ConfigureAwait(false);
+
+ await MediaEncoder.Init().ConfigureAwait(false);
+
+ if (string.IsNullOrWhiteSpace(MediaEncoder.EncoderPath))
+ {
+ if (ServerConfigurationManager.Configuration.IsStartupWizardCompleted)
+ {
+ ServerConfigurationManager.Configuration.IsStartupWizardCompleted = false;
+ ServerConfigurationManager.SaveConfiguration();
+ }
+ }
+
+ Logger.Info("ServerId: {0}", SystemId);
+ Logger.Info("Core startup complete");
+ HttpServer.GlobalResponse = null;
+
+ PerformPostInitMigrations();
+ Logger.Info("Post-init migrations complete");
+
+ foreach (var entryPoint in GetExports<IServerEntryPoint>().ToList())
+ {
+ var name = entryPoint.GetType().FullName;
+ Logger.Info("Starting entry point {0}", name);
+ var now = DateTime.UtcNow;
+ try
+ {
+ entryPoint.Run();
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error in {0}", ex, name);
+ }
+ Logger.Info("Entry point completed: {0}. Duration: {1} seconds", name, (DateTime.UtcNow - now).TotalSeconds.ToString(CultureInfo.InvariantCulture));
+ }
+ Logger.Info("All entry points have started");
+
+ LogManager.RemoveConsoleOutput();
+ }
+
+ protected override 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
+ }
+
+ var result = new JsonSerializer(FileSystemManager, LogManager.GetLogger("JsonSerializer"));
+
+ ServiceStack.Text.JsConfig<LiveTvProgram>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<LiveTvChannel>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<LiveTvVideoRecording>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<LiveTvAudioRecording>.ExcludePropertyNames = new[] { "Artists", "AlbumArtists", "ChannelMediaSources", "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<Series>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<Audio>.ExcludePropertyNames = new[] { "Artists", "AlbumArtists", "ChannelMediaSources", "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<MusicAlbum>.ExcludePropertyNames = new[] { "Artists", "AlbumArtists", "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<MusicArtist>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<MusicGenre>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<MusicVideo>.ExcludePropertyNames = new[] { "Artists", "AlbumArtists", "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<Movie>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<Playlist>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<AudioPodcast>.ExcludePropertyNames = new[] { "Artists", "AlbumArtists", "ChannelMediaSources", "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<AudioBook>.ExcludePropertyNames = new[] { "Artists", "AlbumArtists", "ChannelMediaSources", "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<Trailer>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<BoxSet>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<Episode>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<Season>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<Book>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<CollectionFolder>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<Folder>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<Game>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<GameGenre>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<GameSystem>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<Genre>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<Person>.ExcludePropertyNames = new[] { "PlaceOfBirth", "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<Photo>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<PhotoAlbum>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<Studio>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<UserRootFolder>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<UserView>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<Video>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<Year>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<Channel>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+ ServiceStack.Text.JsConfig<AggregateFolder>.ExcludePropertyNames = new[] { "ProviderIds", "ImageInfos", "ProductionLocations", "ThemeSongIds", "ThemeVideoIds", "TotalBitrate", "ShortOverview", "Taglines", "Keywords", "ExtraType" };
+
+ return result;
+ }
+
+ public override Task Init(IProgress<double> progress)
+ {
+ HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
+ HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
+
+ // Safeguard against invalid configuration
+ if (HttpPort == HttpsPort)
+ {
+ HttpPort = ServerConfiguration.DefaultHttpPort;
+ HttpsPort = ServerConfiguration.DefaultHttpsPort;
+ }
+
+ return base.Init(progress);
+ }
+
+ private async Task PerformPreInitMigrations()
+ {
+ var migrations = new List<IVersionMigration>
+ {
+ new UpdateLevelMigration(ServerConfigurationManager, this, HttpClient, JsonSerializer, _releaseAssetFilename, Logger)
+ };
+
+ foreach (var task in migrations)
+ {
+ try
+ {
+ await task.Run().ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error running migration", ex);
+ }
+ }
+ }
+
+ private void PerformPostInitMigrations()
+ {
+ var migrations = new List<IVersionMigration>
+ {
+ new LibraryScanMigration(ServerConfigurationManager, TaskManager)
+ };
+
+ foreach (var task in migrations)
+ {
+ try
+ {
+ task.Run();
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error running migration", ex);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Registers resources that classes will depend on
+ /// </summary>
+ protected override async Task RegisterResources(IProgress<double> progress)
+ {
+ await base.RegisterResources(progress).ConfigureAwait(false);
+
+ RegisterSingleInstance(PowerManagement);
+
+ 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);
+ RegisterSingleInstance(InstallationManager);
+
+ ZipClient = new ZipClient(FileSystemManager);
+ RegisterSingleInstance(ZipClient);
+
+ RegisterSingleInstance<IHttpResultFactory>(new HttpResultFactory(LogManager, FileSystemManager, JsonSerializer, MemoryStreamFactory));
+
+ RegisterSingleInstance<IServerApplicationHost>(this);
+ RegisterSingleInstance<IServerApplicationPaths>(ApplicationPaths);
+
+ RegisterSingleInstance(ServerConfigurationManager);
+
+ IAssemblyInfo assemblyInfo = new AssemblyInfo();
+ RegisterSingleInstance<IAssemblyInfo>(assemblyInfo);
+
+ LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager, JsonSerializer, LogManager.GetLogger("LocalizationManager"), assemblyInfo, new TextLocalizer());
+ StringExtensions.LocalizationManager = LocalizationManager;
+ RegisterSingleInstance(LocalizationManager);
+
+ ITextEncoding textEncoding = new TextEncoding(FileSystemManager);
+ RegisterSingleInstance(textEncoding);
+ Utilities.EncodingHelper = textEncoding;
+ RegisterSingleInstance<IBlurayExaminer>(() => new BdInfoExaminer(FileSystemManager, textEncoding));
+
+ RegisterSingleInstance<IXmlReaderSettingsFactory>(new XmlReaderSettingsFactory());
+
+ UserDataManager = new UserDataManager(LogManager, ServerConfigurationManager);
+ 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);
+ DisplayPreferencesRepository = displayPreferencesRepo;
+ RegisterSingleInstance(DisplayPreferencesRepository);
+
+ var itemRepo = new SqliteItemRepository(ServerConfigurationManager, JsonSerializer, LogManager.GetLogger("SqliteItemRepository"), MemoryStreamFactory, assemblyInfo, FileSystemManager, EnvironmentInfo, TimerFactory);
+ ItemRepository = itemRepo;
+ RegisterSingleInstance(ItemRepository);
+
+ FileOrganizationRepository = GetFileOrganizationRepository();
+ RegisterSingleInstance(FileOrganizationRepository);
+
+ AuthenticationRepository = await GetAuthenticationRepository().ConfigureAwait(false);
+ RegisterSingleInstance(AuthenticationRepository);
+
+ SyncRepository = GetSyncRepository();
+ RegisterSingleInstance(SyncRepository);
+
+ UserManager = new UserManager(LogManager.GetLogger("UserManager"), ServerConfigurationManager, UserRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, () => ConnectManager, this, JsonSerializer, FileSystemManager, CryptographyProvider, _defaultUserNameFactory());
+ RegisterSingleInstance(UserManager);
+
+ LibraryManager = new LibraryManager(Logger, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager);
+ RegisterSingleInstance(LibraryManager);
+
+ var musicManager = new MusicManager(LibraryManager);
+ RegisterSingleInstance<IMusicManager>(new MusicManager(LibraryManager));
+
+ 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));
+
+ CertificatePath = GetCertificatePath(true);
+ Certificate = GetCertificate(CertificatePath);
+
+ HttpServer = HttpServerFactory.CreateServer(this, LogManager, ServerConfigurationManager, NetworkManager, MemoryStreamFactory, "Emby", "web/index.html", textEncoding, SocketFactory, CryptographyProvider, JsonSerializer, XmlSerializer, EnvironmentInfo, Certificate, SupportsDualModeSockets);
+ HttpServer.GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading");
+ RegisterSingleInstance(HttpServer, false);
+ progress.Report(10);
+
+ ServerManager = new ServerManager(this, JsonSerializer, LogManager.GetLogger("ServerManager"), ServerConfigurationManager, MemoryStreamFactory, textEncoding);
+ RegisterSingleInstance(ServerManager);
+
+ var innerProgress = new ActionableProgress<double>();
+ innerProgress.RegisterAction(p => progress.Report((.75 * p) + 15));
+
+ ImageProcessor = GetImageProcessor();
+ RegisterSingleInstance(ImageProcessor);
+
+ TVSeriesManager = new TVSeriesManager(UserManager, UserDataManager, LibraryManager, ServerConfigurationManager);
+ RegisterSingleInstance(TVSeriesManager);
+
+ SyncManager = new SyncManager(LibraryManager, SyncRepository, ImageProcessor, LogManager.GetLogger("SyncManager"), UserManager, () => DtoService, this, TVSeriesManager, () => MediaEncoder, FileSystemManager, () => SubtitleEncoder, ServerConfigurationManager, UserDataManager, () => MediaSourceManager, JsonSerializer, TaskManager, MemoryStreamFactory);
+ 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 = new ConnectManager(LogManager.GetLogger("ConnectManager"), ApplicationPaths, JsonSerializer, encryptionManager, HttpClient, this, ServerConfigurationManager, UserManager, ProviderManager, SecurityManager, FileSystemManager);
+ RegisterSingleInstance(ConnectManager);
+
+ DeviceManager = new DeviceManager(new DeviceRepository(ApplicationPaths, JsonSerializer, LogManager.GetLogger("DeviceManager"), FileSystemManager), UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager, LogManager.GetLogger("DeviceManager"), NetworkManager);
+ RegisterSingleInstance(DeviceManager);
+
+ var newsService = new Emby.Server.Implementations.News.NewsService(ApplicationPaths, JsonSerializer);
+ RegisterSingleInstance<INewsService>(newsService);
+
+ var fileOrganizationService = new FileOrganizationService(TaskManager, FileOrganizationRepository, LogManager.GetLogger("FileOrganizationService"), LibraryMonitor, LibraryManager, ServerConfigurationManager, FileSystemManager, ProviderManager);
+ RegisterSingleInstance<IFileOrganizationService>(fileOrganizationService);
+
+ progress.Report(15);
+
+ ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LogManager.GetLogger("ChannelManager"), ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, LocalizationManager, HttpClient, ProviderManager);
+ 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);
+ 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);
+ 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());
+ 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);
+ 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);
+
+ EncodingManager = new EncodingManager(FileSystemManager, Logger, MediaEncoder, ChapterManager, LibraryManager);
+ RegisterSingleInstance(EncodingManager);
+
+ var sharingRepo = new SharingRepository(LogManager.GetLogger("SharingRepository"), ApplicationPaths);
+ 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);
+ RegisterSingleInstance<IAuthorizationContext>(authContext);
+ RegisterSingleInstance<ISessionContext>(new SessionContext(UserManager, authContext, SessionManager));
+
+ AuthService = new AuthService(UserManager, authContext, ServerConfigurationManager, ConnectManager, SessionManager, DeviceManager);
+ RegisterSingleInstance<IAuthService>(AuthService);
+
+ SubtitleEncoder = new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager, MemoryStreamFactory, ProcessFactory, textEncoding);
+ RegisterSingleInstance(SubtitleEncoder);
+
+ displayPreferencesRepo.Initialize();
+
+ var userDataRepo = new SqliteUserDataRepository(LogManager.GetLogger("SqliteUserDataRepository"), ApplicationPaths, FileSystemManager);
+
+ ((UserDataManager)UserDataManager).Repository = userDataRepo;
+ itemRepo.Initialize(userDataRepo);
+ ((LibraryManager)LibraryManager).ItemRepository = ItemRepository;
+ ConfigureNotificationsRepository();
+ progress.Report(100);
+
+ SetStaticProperties();
+
+ await ((UserManager)UserManager).Initialize().ConfigureAwait(false);
+ }
+
+ protected abstract bool SupportsDualModeSockets { get; }
+
+ private ICertificate GetCertificate(string certificateLocation)
+ {
+ if (string.IsNullOrWhiteSpace(certificateLocation))
+ {
+ return null;
+ }
+
+ try
+ {
+ if (!FileSystemManager.FileExists(certificateLocation))
+ {
+ return null;
+ }
+
+ X509Certificate2 localCert = new X509Certificate2(certificateLocation);
+ //localCert.PrivateKey = PrivateKey.CreateFromFile(pvk_file).RSA;
+ if (!localCert.HasPrivateKey)
+ {
+ //throw new FileNotFoundException("Secure requested, no private key included", certificateLocation);
+ return null;
+ }
+
+ return new Certificate(localCert);
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error loading cert from {0}", ex, certificateLocation);
+ return null;
+ }
+ }
+
+ private IImageProcessor GetImageProcessor()
+ {
+ var maxConcurrentImageProcesses = Math.Max(Environment.ProcessorCount, 4);
+
+ if (StartupOptions.ContainsOption("-imagethreads"))
+ {
+ int.TryParse(StartupOptions.GetOption("-imagethreads"), NumberStyles.Any, CultureInfo.InvariantCulture, out maxConcurrentImageProcesses);
+ }
+
+ return new ImageProcessor(LogManager.GetLogger("ImageProcessor"), ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer, ImageEncoder, maxConcurrentImageProcesses, () => LibraryManager, TimerFactory);
+ }
+
+ protected abstract FFMpegInstallInfo GetFfmpegInstallInfo();
+
+ /// <summary>
+ /// Registers the media encoder.
+ /// </summary>
+ /// <returns>Task.</returns>
+ private async Task RegisterMediaEncoder(IProgress<double> progress)
+ {
+ string encoderPath = null;
+ string probePath = null;
+
+ var info = await new FFMpegLoader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager, GetFfmpegInstallInfo())
+ .GetFFMpegInfo(StartupOptions, progress).ConfigureAwait(false);
+
+ encoderPath = info.EncoderPath;
+ probePath = info.ProbePath;
+ var hasExternalEncoder = string.Equals(info.Version, "external", StringComparison.OrdinalIgnoreCase);
+
+ var mediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"),
+ JsonSerializer,
+ encoderPath,
+ probePath,
+ hasExternalEncoder,
+ ServerConfigurationManager,
+ FileSystemManager,
+ LiveTvManager,
+ IsoManager,
+ LibraryManager,
+ ChannelManager,
+ SessionManager,
+ () => SubtitleEncoder,
+ () => MediaSourceManager,
+ HttpClient,
+ ZipClient,
+ MemoryStreamFactory,
+ ProcessFactory,
+ (Environment.ProcessorCount > 2 ? 14000 : 40000),
+ EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows,
+ EnvironmentInfo);
+
+ MediaEncoder = mediaEncoder;
+ RegisterSingleInstance(MediaEncoder);
+ }
+
+ /// <summary>
+ /// Gets the user repository.
+ /// </summary>
+ /// <returns>Task{IUserRepository}.</returns>
+ private IUserRepository GetUserRepository()
+ {
+ var repo = new SqliteUserRepository(LogManager.GetLogger("SqliteUserRepository"), ApplicationPaths, JsonSerializer, MemoryStreamFactory);
+
+ repo.Initialize();
+
+ return repo;
+ }
+
+ /// <summary>
+ /// Gets the file organization repository.
+ /// </summary>
+ /// <returns>Task{IUserRepository}.</returns>
+ private IFileOrganizationRepository GetFileOrganizationRepository()
+ {
+ var repo = new SqliteFileOrganizationRepository(LogManager.GetLogger("SqliteFileOrganizationRepository"), ServerConfigurationManager.ApplicationPaths);
+
+ repo.Initialize();
+
+ return repo;
+ }
+
+ private async Task<IAuthenticationRepository> GetAuthenticationRepository()
+ {
+ var repo = new AuthenticationRepository(LogManager.GetLogger("AuthenticationRepository"), ServerConfigurationManager.ApplicationPaths);
+
+ repo.Initialize();
+
+ return repo;
+ }
+
+ private IActivityRepository GetActivityLogRepository()
+ {
+ var repo = new ActivityRepository(LogManager.GetLogger("ActivityRepository"), ServerConfigurationManager.ApplicationPaths);
+
+ repo.Initialize();
+
+ return repo;
+ }
+
+ private ISyncRepository GetSyncRepository()
+ {
+ var repo = new SyncRepository(LogManager.GetLogger("SyncRepository"), JsonSerializer, ServerConfigurationManager.ApplicationPaths);
+
+ repo.Initialize();
+
+ return repo;
+ }
+
+ /// <summary>
+ /// Configures the repositories.
+ /// </summary>
+ private void ConfigureNotificationsRepository()
+ {
+ var repo = new SqliteNotificationsRepository(LogManager.GetLogger("SqliteNotificationsRepository"), ServerConfigurationManager.ApplicationPaths);
+
+ repo.Initialize();
+
+ NotificationsRepository = repo;
+
+ RegisterSingleInstance(NotificationsRepository);
+ }
+
+ /// <summary>
+ /// Dirty hacks
+ /// </summary>
+ private void SetStaticProperties()
+ {
+ // For now there's no real way to inject these properly
+ BaseItem.Logger = LogManager.GetLogger("BaseItem");
+ BaseItem.ConfigurationManager = ServerConfigurationManager;
+ BaseItem.LibraryManager = LibraryManager;
+ 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;
+ Folder.UserViewManager = UserViewManager;
+ UserView.TVSeriesManager = TVSeriesManager;
+ UserView.PlaylistManager = PlaylistManager;
+ BaseItem.CollectionManager = CollectionManager;
+ BaseItem.MediaSourceManager = MediaSourceManager;
+ CollectionFolder.XmlSerializer = XmlSerializer;
+ BaseStreamingService.AppHost = this;
+ BaseStreamingService.HttpClient = HttpClient;
+ Utilities.CryptographyProvider = CryptographyProvider;
+ AuthenticatedAttribute.AuthService = AuthService;
+ }
+
+ /// <summary>
+ /// Finds the parts.
+ /// </summary>
+ protected override void FindParts()
+ {
+ if (!ServerConfigurationManager.Configuration.IsPortAuthorized)
+ {
+ RegisterServerWithAdministratorAccess();
+ ServerConfigurationManager.Configuration.IsPortAuthorized = true;
+ ConfigurationManager.SaveConfiguration();
+ }
+
+ RegisterModules();
+
+ base.FindParts();
+
+ HttpServer.Init(GetExports<IService>(false));
+
+ ServerManager.AddWebSocketListeners(GetExports<IWebSocketListener>(false));
+
+ StartServer();
+
+ LibraryManager.AddParts(GetExports<IResolverIgnoreRule>(),
+ GetExports<IVirtualFolderCreator>(),
+ GetExports<IItemResolver>(),
+ GetExports<IIntroProvider>(),
+ GetExports<IBaseItemComparer>(),
+ GetExports<ILibraryPostScanTask>());
+
+ ProviderManager.AddParts(GetExports<IImageProvider>(),
+ GetExports<IMetadataService>(),
+ GetExports<IMetadataProvider>(),
+ GetExports<IMetadataSaver>(),
+ GetExports<IExternalId>());
+
+ ImageProcessor.AddParts(GetExports<IImageEnhancer>());
+
+ LiveTvManager.AddParts(GetExports<ILiveTvService>(), GetExports<ITunerHost>(), GetExports<IListingsProvider>());
+
+ 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>());
+ }
+
+ private string CertificatePath { get; set; }
+ private ICertificate Certificate { get; set; }
+
+ private IEnumerable<string> GetUrlPrefixes()
+ {
+ var hosts = new List<string>();
+
+ hosts.Add("+");
+
+ return hosts.SelectMany(i =>
+ {
+ var prefixes = new List<string>
+ {
+ "http://"+i+":" + HttpPort + "/"
+ };
+
+ if (!string.IsNullOrWhiteSpace(CertificatePath))
+ {
+ prefixes.Add("https://" + i + ":" + HttpsPort + "/");
+ }
+
+ return prefixes;
+ });
+ }
+
+ /// <summary>
+ /// Starts the server.
+ /// </summary>
+ private void StartServer()
+ {
+ try
+ {
+ ServerManager.Start(GetUrlPrefixes());
+ return;
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error starting http server", ex);
+
+ if (HttpPort == ServerConfiguration.DefaultHttpPort)
+ {
+ throw;
+ }
+ }
+
+ HttpPort = ServerConfiguration.DefaultHttpPort;
+
+ try
+ {
+ ServerManager.Start(GetUrlPrefixes());
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error starting http server", ex);
+
+ throw;
+ }
+ }
+
+ private string GetCertificatePath(bool generateCertificate)
+ {
+ if (!string.IsNullOrWhiteSpace(ServerConfigurationManager.Configuration.CertificatePath))
+ {
+ // Custom cert
+ return ServerConfigurationManager.Configuration.CertificatePath;
+ }
+
+ // Generate self-signed cert
+ var certHost = GetHostnameFromExternalDns(ServerConfigurationManager.Configuration.WanDdns);
+ var certPath = Path.Combine(ServerConfigurationManager.ApplicationPaths.ProgramDataPath, "ssl", "cert_" + (certHost + "1").GetMD5().ToString("N") + ".pfx");
+
+ if (generateCertificate)
+ {
+ if (!FileSystemManager.FileExists(certPath))
+ {
+ FileSystemManager.CreateDirectory(Path.GetDirectoryName(certPath));
+
+ try
+ {
+ _certificateGenerator(certPath, certHost);
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error creating ssl cert", ex);
+ return null;
+ }
+ }
+ }
+
+ return certPath;
+ }
+
+ /// <summary>
+ /// Called when [configuration updated].
+ /// </summary>
+ /// <param name="sender">The sender.</param>
+ /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
+ protected override void OnConfigurationUpdated(object sender, EventArgs e)
+ {
+ base.OnConfigurationUpdated(sender, e);
+
+ var requiresRestart = false;
+
+ // Don't do anything if these haven't been set yet
+ if (HttpPort != 0 && HttpsPort != 0)
+ {
+ // Need to restart if ports have changed
+ if (ServerConfigurationManager.Configuration.HttpServerPortNumber != HttpPort ||
+ ServerConfigurationManager.Configuration.HttpsPortNumber != HttpsPort)
+ {
+ if (ServerConfigurationManager.Configuration.IsPortAuthorized)
+ {
+ ServerConfigurationManager.Configuration.IsPortAuthorized = false;
+ ServerConfigurationManager.SaveConfiguration();
+
+ requiresRestart = true;
+ }
+ }
+ }
+
+ if (!HttpServer.UrlPrefixes.SequenceEqual(GetUrlPrefixes(), StringComparer.OrdinalIgnoreCase))
+ {
+ requiresRestart = true;
+ }
+
+ if (!string.Equals(CertificatePath, GetCertificatePath(false), StringComparison.OrdinalIgnoreCase))
+ {
+ requiresRestart = true;
+ }
+
+ if (requiresRestart)
+ {
+ NotifyPendingRestart();
+ }
+ }
+
+ /// <summary>
+ /// Restarts this instance.
+ /// </summary>
+ public override async Task Restart()
+ {
+ if (!CanSelfRestart)
+ {
+ throw new PlatformNotSupportedException("The server is unable to self-restart. Please restart manually.");
+ }
+
+ try
+ {
+ await SessionManager.SendServerRestartNotification(CancellationToken.None).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error sending server restart notification", ex);
+ }
+
+ Logger.Info("Calling RestartInternal");
+
+ RestartInternal();
+ }
+
+ protected abstract void RestartInternal();
+
+ /// <summary>
+ /// Gets the composable part assemblies.
+ /// </summary>
+ /// <returns>IEnumerable{Assembly}.</returns>
+ protected override IEnumerable<Assembly> GetComposablePartAssemblies()
+ {
+ var list = GetPluginAssemblies()
+ .ToList();
+
+ // 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
+
+ // Include composable parts in the Api assembly
+ list.Add(GetAssembly(typeof(ApiEntryPoint)));
+
+ // Include composable parts in the Dashboard assembly
+ list.Add(GetAssembly(typeof(DashboardService)));
+
+ // Include composable parts in the Model assembly
+ list.Add(GetAssembly(typeof(SystemInfo)));
+
+ // Include composable parts in the Common assembly
+ list.Add(GetAssembly(typeof(IApplicationHost)));
+
+ // Include composable parts in the Controller assembly
+ list.Add(GetAssembly(typeof(IServerApplicationHost)));
+
+ // Include composable parts in the Providers assembly
+ list.Add(GetAssembly(typeof(ProviderUtils)));
+
+ // Include composable parts in the Photos assembly
+ list.Add(GetAssembly(typeof(PhotoProvider)));
+
+ // Common implementations
+ list.Add(GetAssembly(typeof(TaskManager)));
+
+ // Emby.Server implementations
+ list.Add(GetAssembly(typeof(InstallationManager)));
+
+ // Emby.Server.Core
+ list.Add(GetAssembly(typeof(ServerApplicationPaths)));
+
+ // MediaEncoding
+ list.Add(GetAssembly(typeof(MediaEncoder)));
+
+ // Dlna
+ list.Add(GetAssembly(typeof(DlnaEntryPoint)));
+
+ // Local metadata
+ list.Add(GetAssembly(typeof(BoxSetXmlSaver)));
+
+ // Xbmc
+ list.Add(GetAssembly(typeof(ArtistNfoProvider)));
+
+ list.AddRange(GetAssembliesWithPartsInternal());
+
+ // Include composable parts in the running assembly
+ list.Add(GetAssembly(typeof(ApplicationHost)));
+
+ return list;
+ }
+
+ protected abstract List<Assembly> GetAssembliesWithPartsInternal();
+
+ /// <summary>
+ /// Gets the plugin assemblies.
+ /// </summary>
+ /// <returns>IEnumerable{Assembly}.</returns>
+ private IEnumerable<Assembly> GetPluginAssemblies()
+ {
+ try
+ {
+ return Directory.EnumerateFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.TopDirectoryOnly)
+ .Where(EnablePlugin)
+ .Select(LoadAssembly)
+ .Where(a => a != null)
+ .ToList();
+ }
+ catch (DirectoryNotFoundException)
+ {
+ return new List<Assembly>();
+ }
+ }
+
+ private bool EnablePlugin(string path)
+ {
+ var filename = Path.GetFileName(path);
+
+ var exclude = new[]
+ {
+ "mbplus.dll",
+ "mbintros.dll"
+ };
+
+ return !exclude.Contains(filename ?? string.Empty, StringComparer.OrdinalIgnoreCase);
+ }
+
+ /// <summary>
+ /// Gets the system status.
+ /// </summary>
+ /// <returns>SystemInfo.</returns>
+ public async Task<SystemInfo> GetSystemInfo()
+ {
+ var localAddress = await GetLocalApiUrl().ConfigureAwait(false);
+
+ return new SystemInfo
+ {
+ HasPendingRestart = HasPendingRestart,
+ Version = ApplicationVersion.ToString(),
+ WebSocketPortNumber = HttpPort,
+ FailedPluginAssemblies = FailedAssemblies.ToList(),
+ InProgressInstallations = InstallationManager.CurrentInstallations.Select(i => i.Item1).ToList(),
+ CompletedInstallations = InstallationManager.CompletedInstallations.ToList(),
+ Id = SystemId,
+ ProgramDataPath = ApplicationPaths.ProgramDataPath,
+ LogPath = ApplicationPaths.LogDirectoryPath,
+ ItemsByNamePath = ApplicationPaths.ItemsByNamePath,
+ InternalMetadataPath = ApplicationPaths.InternalMetadataPath,
+ CachePath = ApplicationPaths.CachePath,
+ MacAddress = GetMacAddress(),
+ HttpServerPortNumber = HttpPort,
+ SupportsHttps = SupportsHttps,
+ HttpsPortNumber = HttpsPort,
+ OperatingSystem = EnvironmentInfo.OperatingSystem.ToString(),
+ OperatingSystemDisplayName = OperatingSystemDisplayName,
+ CanSelfRestart = CanSelfRestart,
+ CanSelfUpdate = CanSelfUpdate,
+ WanAddress = ConnectManager.WanApiAddress,
+ HasUpdateAvailable = HasUpdateAvailable,
+ SupportsAutoRunAtStartup = SupportsAutoRunAtStartup,
+ TranscodingTempPath = ApplicationPaths.TranscodingTempPath,
+ IsRunningAsService = IsRunningAsService,
+ SupportsRunningAsService = SupportsRunningAsService,
+ ServerName = FriendlyName,
+ LocalAddress = localAddress,
+ SupportsLibraryMonitor = true,
+ EncoderLocationType = MediaEncoder.EncoderLocationType,
+ SystemArchitecture = EnvironmentInfo.SystemArchitecture,
+ SystemUpdateLevel = ConfigurationManager.CommonConfiguration.SystemUpdateLevel,
+ PackageName = StartupOptions.GetOption("-package")
+ };
+ }
+
+ public bool EnableHttps
+ {
+ get
+ {
+ return SupportsHttps && ServerConfigurationManager.Configuration.EnableHttps;
+ }
+ }
+
+ public bool SupportsHttps
+ {
+ get { return Certificate != null; }
+ }
+
+ public async Task<string> GetLocalApiUrl()
+ {
+ try
+ {
+ // Return the first matched address, if found, or the first known local address
+ var address = (await GetLocalIpAddresses().ConfigureAwait(false)).FirstOrDefault(i => !i.Equals(IpAddressInfo.Loopback) && !i.Equals(IpAddressInfo.IPv6Loopback));
+
+ if (address != null)
+ {
+ return GetLocalApiUrl(address);
+ }
+
+ return null;
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error getting local Ip address information", ex);
+ }
+
+ return null;
+ }
+
+ public string GetLocalApiUrl(IpAddressInfo ipAddress)
+ {
+ if (ipAddress.AddressFamily == IpAddressFamily.InterNetworkV6)
+ {
+ return GetLocalApiUrl("[" + ipAddress.Address + "]");
+ }
+
+ return GetLocalApiUrl(ipAddress.Address);
+ }
+
+ public string GetLocalApiUrl(string host)
+ {
+ return string.Format("http://{0}:{1}",
+ host,
+ HttpPort.ToString(CultureInfo.InvariantCulture));
+ }
+
+ public async Task<List<IpAddressInfo>> GetLocalIpAddresses()
+ {
+ var addresses = ServerConfigurationManager
+ .Configuration
+ .LocalNetworkAddresses
+ .Select(NormalizeConfiguredLocalAddress)
+ .Where(i => i != null)
+ .ToList();
+
+ if (addresses.Count == 0)
+ {
+ addresses.AddRange(NetworkManager.GetLocalIpAddresses());
+
+ var list = new List<IpAddressInfo>();
+
+ foreach (var address in addresses)
+ {
+ var valid = await IsIpAddressValidAsync(address).ConfigureAwait(false);
+ if (valid)
+ {
+ list.Add(address);
+ }
+ }
+
+ addresses = list;
+ }
+
+ return addresses;
+ }
+
+ private IpAddressInfo NormalizeConfiguredLocalAddress(string address)
+ {
+ var index = address.Trim('/').IndexOf('/');
+
+ if (index != -1)
+ {
+ address = address.Substring(index + 1);
+ }
+
+ IpAddressInfo result;
+ if (NetworkManager.TryParseIpAddress(address.Trim('/'), out result))
+ {
+ return result;
+ }
+ return null;
+ }
+
+ private readonly ConcurrentDictionary<string, bool> _validAddressResults = new ConcurrentDictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
+ private DateTime _lastAddressCacheClear;
+ private async Task<bool> IsIpAddressValidAsync(IpAddressInfo address)
+ {
+ if (address.Equals(IpAddressInfo.Loopback) ||
+ address.Equals(IpAddressInfo.IPv6Loopback))
+ {
+ return true;
+ }
+
+ var apiUrl = GetLocalApiUrl(address);
+ apiUrl += "/system/ping";
+
+ if ((DateTime.UtcNow - _lastAddressCacheClear).TotalMinutes >= 15)
+ {
+ _lastAddressCacheClear = DateTime.UtcNow;
+ _validAddressResults.Clear();
+ }
+
+ bool cachedResult;
+ if (_validAddressResults.TryGetValue(apiUrl, out cachedResult))
+ {
+ return cachedResult;
+ }
+
+ try
+ {
+ using (var response = await HttpClient.SendAsync(new HttpRequestOptions
+ {
+ Url = apiUrl,
+ LogErrorResponseBody = false,
+ LogErrors = false,
+ LogRequest = false,
+ TimeoutMs = 30000,
+ BufferContent = false
+
+ }, "POST").ConfigureAwait(false))
+ {
+ using (var reader = new StreamReader(response.Content))
+ {
+ var result = reader.ReadToEnd();
+ var valid = string.Equals(Name, result, StringComparison.OrdinalIgnoreCase);
+
+ _validAddressResults.AddOrUpdate(apiUrl, valid, (k, v) => valid);
+ //Logger.Debug("Ping test result to {0}. Success: {1}", apiUrl, valid);
+ return valid;
+ }
+ }
+ }
+ catch
+ {
+ //Logger.Debug("Ping test result to {0}. Success: {1}", apiUrl, false);
+
+ _validAddressResults.AddOrUpdate(apiUrl, false, (k, v) => false);
+ return false;
+ }
+ }
+
+ public string FriendlyName
+ {
+ get
+ {
+ return string.IsNullOrWhiteSpace(ServerConfigurationManager.Configuration.ServerName)
+ ? Environment.MachineName
+ : ServerConfigurationManager.Configuration.ServerName;
+ }
+ }
+
+ public int HttpPort { get; private set; }
+
+ 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 override async Task Shutdown()
+ {
+ try
+ {
+ await SessionManager.SendServerShutdownNotification(CancellationToken.None).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error sending server shutdown notification", ex);
+ }
+
+ ShutdownInternal();
+ }
+
+ protected abstract void ShutdownInternal();
+
+ /// <summary>
+ /// Registers the server with administrator access.
+ /// </summary>
+ private void RegisterServerWithAdministratorAccess()
+ {
+ Logger.Info("Requesting administrative access to authorize http server");
+
+ try
+ {
+ AuthorizeServer();
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error authorizing server", ex);
+ }
+ }
+
+ protected abstract void AuthorizeServer();
+
+ public event EventHandler HasUpdateAvailableChanged;
+
+ private bool _hasUpdateAvailable;
+ public bool HasUpdateAvailable
+ {
+ get { return _hasUpdateAvailable; }
+ set
+ {
+ var fireEvent = value && !_hasUpdateAvailable;
+
+ _hasUpdateAvailable = value;
+
+ if (fireEvent)
+ {
+ EventHelper.FireEventIfNotNull(HasUpdateAvailableChanged, this, EventArgs.Empty, Logger);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Checks for update.
+ /// </summary>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <param name="progress">The progress.</param>
+ /// <returns>Task{CheckForUpdateResult}.</returns>
+ public override async Task<CheckForUpdateResult> CheckForApplicationUpdate(CancellationToken cancellationToken, IProgress<double> progress)
+ {
+ var cacheLength = TimeSpan.FromHours(3);
+ var updateLevel = ConfigurationManager.CommonConfiguration.SystemUpdateLevel;
+
+ if (updateLevel == PackageVersionClass.Beta)
+ {
+ cacheLength = TimeSpan.FromHours(1);
+ }
+ else if (updateLevel == PackageVersionClass.Dev)
+ {
+ cacheLength = TimeSpan.FromMinutes(5);
+ }
+
+ var result = await new GithubUpdater(HttpClient, JsonSerializer).CheckForUpdateResult("MediaBrowser", "Emby", ApplicationVersion, updateLevel, _releaseAssetFilename,
+ "MBServer", "Mbserver.zip", cacheLength, cancellationToken).ConfigureAwait(false);
+
+ HasUpdateAvailable = result.IsUpdateAvailable;
+
+ return result;
+ }
+
+ /// <summary>
+ /// Updates the application.
+ /// </summary>
+ /// <param name="package">The package that contains the update</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <param name="progress">The progress.</param>
+ public override async Task UpdateApplication(PackageVersionInfo package, CancellationToken cancellationToken, IProgress<double> progress)
+ {
+ await InstallationManager.InstallPackage(package, false, progress, cancellationToken).ConfigureAwait(false);
+
+ HasUpdateAvailable = false;
+
+ OnApplicationUpdated(package);
+ }
+
+ /// <summary>
+ /// Configures the automatic run at startup.
+ /// </summary>
+ /// <param name="autorun">if set to <c>true</c> [autorun].</param>
+ protected override void ConfigureAutoRunAtStartup(bool autorun)
+ {
+ if (SupportsAutoRunAtStartup)
+ {
+ ConfigureAutoRunInternal(autorun);
+ }
+ }
+
+ protected abstract void ConfigureAutoRunInternal(bool autorun);
+
+ /// <summary>
+ /// This returns localhost in the case of no external dns, and the hostname if the
+ /// dns is prefixed with a valid Uri prefix.
+ /// </summary>
+ /// <param name="externalDns">The external dns prefix to get the hostname of.</param>
+ /// <returns>The hostname in <paramref name="externalDns"/></returns>
+ private static string GetHostnameFromExternalDns(string externalDns)
+ {
+ if (string.IsNullOrWhiteSpace(externalDns))
+ {
+ return "localhost";
+ }
+
+ try
+ {
+ return new Uri(externalDns).Host;
+ }
+ catch
+ {
+ return externalDns;
+ }
+ }
+
+ public void LaunchUrl(string url)
+ {
+ if (EnvironmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows)
+ {
+ throw new NotImplementedException();
+ }
+
+ var process = ProcessFactory.Create(new ProcessOptions
+ {
+ FileName = url,
+ EnableRaisingEvents = true,
+ UseShellExecute = true,
+ ErrorDialog = false
+ });
+
+ process.Exited += ProcessExited;
+
+ try
+ {
+ process.Start();
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("Error launching url: {0}", url);
+ Logger.ErrorException("Error launching url: {0}", ex, url);
+
+ throw;
+ }
+ }
+
+ private static void ProcessExited(object sender, EventArgs e)
+ {
+ ((IProcess)sender).Dispose();
+ }
+
+ public void EnableLoopback(string appName)
+ {
+ EnableLoopbackInternal(appName);
+ }
+
+ protected abstract void EnableLoopbackInternal(string appName);
+
+ 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);
+ }
+ }
+ }
+
+ void IDependencyContainer.RegisterSingleInstance<T>(T obj, bool manageLifetime)
+ {
+ RegisterSingleInstance(obj, manageLifetime);
+ }
+
+ void IDependencyContainer.RegisterSingleInstance<T>(Func<T> func)
+ {
+ RegisterSingleInstance(func);
+ }
+
+ void IDependencyContainer.Register(Type typeInterface, Type typeImplementation)
+ {
+ Container.Register(typeInterface, typeImplementation);
+ }
+
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs b/Emby.Server.Core/Configuration/ServerConfigurationManager.cs
index e8669bbc2c..f98c096ca3 100644
--- a/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs
+++ b/Emby.Server.Core/Configuration/ServerConfigurationManager.cs
@@ -1,7 +1,10 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Emby.Common.Implementations.Configuration;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events;
-using MediaBrowser.Common.Implementations.Configuration;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@@ -10,14 +13,11 @@ using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Events;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
-using System;
-using System.IO;
-using System.Linq;
-using CommonIO;
-namespace MediaBrowser.Server.Implementations.Configuration
+namespace Emby.Server.Core.Configuration
{
/// <summary>
/// Class ServerConfigurationManager
diff --git a/Emby.Server.Core/Emby.Server.Core.xproj b/Emby.Server.Core/Emby.Server.Core.xproj
new file mode 100644
index 0000000000..00f7664bd6
--- /dev/null
+++ b/Emby.Server.Core/Emby.Server.Core.xproj
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
+ <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+ </PropertyGroup>
+ <Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>65aa7d67-8059-40cd-91f1-16d02687226c</ProjectGuid>
+ <RootNamespace>Emby.Server.Core</RootNamespace>
+ <BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
+ <OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
+ <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
+ </PropertyGroup>
+ <PropertyGroup>
+ <SchemaVersion>2.0</SchemaVersion>
+ </PropertyGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\ServiceStack\ServiceStack.csproj" />
+ <ProjectReference Include="..\Emby.Drawing\Emby.Drawing.csproj" />
+ <ProjectReference Include="..\Emby.Photos\Emby.Photos.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Api\MediaBrowser.Api.csproj" />
+ <ProjectReference Include="..\MediaBrowser.XbmcMetadata\MediaBrowser.XbmcMetadata.csproj" />
+ <ProjectReference Include="..\MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj" />
+ <ProjectReference Include="..\MediaBrowser.WebDashboard\MediaBrowser.WebDashboard.csproj" />
+ <ProjectReference Include="..\MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj" />
+ <ProjectReference Include="..\Emby.Dlna\Emby.Dlna.csproj" />
+ <ProjectReference Include="..\Emby.Server.Implementations\Emby.Server.Implementations.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
+ </ItemGroup>
+ <Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
+</Project> \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs
index 3274231ee7..b75de2ff4f 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
+++ b/Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs
@@ -1,34 +1,39 @@
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Model.Logging;
-using Mono.Nat;
-using System;
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net;
-using MediaBrowser.Common.Threading;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Events;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Threading;
+using Mono.Nat;
-namespace MediaBrowser.Server.Implementations.EntryPoints
+namespace Emby.Server.Core.EntryPoints
{
public class ExternalPortForwarding : IServerEntryPoint
{
private readonly IServerApplicationHost _appHost;
private readonly ILogger _logger;
+ private readonly IHttpClient _httpClient;
private readonly IServerConfigurationManager _config;
private readonly IDeviceDiscovery _deviceDiscovery;
- private PeriodicTimer _timer;
+ private ITimer _timer;
private bool _isStarted;
+ private readonly ITimerFactory _timerFactory;
- public ExternalPortForwarding(ILogManager logmanager, IServerApplicationHost appHost, IServerConfigurationManager config, IDeviceDiscovery deviceDiscovery)
+ public ExternalPortForwarding(ILogManager logmanager, IServerApplicationHost appHost, IServerConfigurationManager config, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient, ITimerFactory timerFactory)
{
_logger = logmanager.GetLogger("PortMapper");
_appHost = appHost;
_config = config;
_deviceDiscovery = deviceDiscovery;
+ _httpClient = httpClient;
+ _timerFactory = timerFactory;
}
private string _lastConfigIdentifier;
@@ -63,6 +68,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
public void Run()
{
NatUtility.Logger = _logger;
+ NatUtility.HttpClient = _httpClient;
if (_config.Configuration.EnableUPnP)
{
@@ -87,12 +93,9 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
NatUtility.DeviceLost += NatUtility_DeviceLost;
- // it is hard to say what one should do when an unhandled exception is raised
- // because there isn't anything one can do about it. Probably save a log or ignored it.
- NatUtility.UnhandledException += NatUtility_UnhandledException;
NatUtility.StartDiscovery();
- _timer = new PeriodicTimer(ClearCreatedRules, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
+ _timer = _timerFactory.Create(ClearCreatedRules, null, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10));
_deviceDiscovery.DeviceDiscovered += _deviceDiscovery_DeviceDiscovered;
@@ -136,7 +139,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
_usnsHandled.Add(identifier);
}
- _logger.Debug("Calling Nat.Handle on " + identifier);
+ _logger.Debug("Found NAT device: " + identifier);
IPAddress address;
if (IPAddress.TryParse(info.Location.Host, out address))
@@ -150,16 +153,23 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
{
var localAddressString = await _appHost.GetLocalApiUrl().ConfigureAwait(false);
- if (!IPAddress.TryParse(localAddressString, out localAddress))
+ Uri uri;
+ if (Uri.TryCreate(localAddressString, UriKind.Absolute, out uri))
{
- return;
+ localAddressString = uri.Host;
+
+ if (!IPAddress.TryParse(localAddressString, out localAddress))
+ {
+ return;
+ }
}
}
- catch
+ catch (Exception ex)
{
return;
}
+ _logger.Debug("Calling Nat.Handle on " + identifier);
NatUtility.Handle(localAddress, info, endpoint, NatProtocol.Upnp);
}
}
@@ -173,21 +183,6 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
}
}
- void NatUtility_UnhandledException(object sender, UnhandledExceptionEventArgs e)
- {
- var ex = e.ExceptionObject as Exception;
-
- if (ex == null)
- {
- //_logger.Error("Unidentified error reported by Mono.Nat");
- }
- else
- {
- // Seeing some blank exceptions coming through here
- //_logger.ErrorException("Error reported by Mono.Nat: ", ex);
- }
- }
-
void NatUtility_DeviceFound(object sender, DeviceEventArgs e)
{
try
@@ -229,13 +224,22 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
}
}
- private void CreatePortMap(INatDevice device, int privatePort, int publicPort)
+ private async void CreatePortMap(INatDevice device, int privatePort, int publicPort)
{
_logger.Debug("Creating port map on port {0}", privatePort);
- device.CreatePortMap(new Mapping(Protocol.Tcp, privatePort, publicPort)
+
+ try
{
- Description = _appHost.Name
- });
+ await device.CreatePortMap(new Mapping(Protocol.Tcp, privatePort, publicPort)
+ {
+ Description = _appHost.Name
+
+ }).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.Error("Error creating port map: " + ex.Message);
+ }
}
// As I said before, this method will be never invoked. You can remove it.
@@ -268,7 +272,6 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
NatUtility.StopDiscovery();
NatUtility.DeviceFound -= NatUtility_DeviceFound;
NatUtility.DeviceLost -= NatUtility_DeviceLost;
- NatUtility.UnhandledException -= NatUtility_UnhandledException;
}
// Statements in try-block will no fail because StopDiscovery is a one-line
// method that was no chances to fail.
diff --git a/Emby.Server.Core/HttpServerFactory.cs b/Emby.Server.Core/HttpServerFactory.cs
new file mode 100644
index 0000000000..8ec5fb0269
--- /dev/null
+++ b/Emby.Server.Core/HttpServerFactory.cs
@@ -0,0 +1,109 @@
+using System;
+using System.IO;
+using System.Net.Security;
+using System.Net.Sockets;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading.Tasks;
+using Emby.Common.Implementations.Net;
+using Emby.Server.Implementations.HttpServer;
+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.Core
+{
+ /// <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,
+ ICertificate certificate,
+ bool enableDualModeSockets)
+ {
+ var logger = logManager.GetLogger("HttpServer");
+
+ return new HttpListenerHost(applicationHost,
+ logger,
+ config,
+ serverName,
+ defaultRedirectpath,
+ networkmanager,
+ streamProvider,
+ textEncoding,
+ socketFactory,
+ cryptoProvider,
+ json,
+ xml,
+ environment,
+ certificate,
+ new StreamFactory(),
+ GetParseFn,
+ enableDualModeSockets);
+ }
+
+ private static Func<string, object> GetParseFn(Type propertyType)
+ {
+ return s => JsvReader.GetParseFn(propertyType)(s);
+ }
+ }
+
+ public class StreamFactory : IStreamFactory
+ {
+ public Stream CreateNetworkStream(ISocket socket, bool ownsSocket)
+ {
+ var netSocket = (NetSocket)socket;
+
+ return new NetworkStream(netSocket.Socket, ownsSocket);
+ }
+
+ public Task AuthenticateSslStreamAsServer(Stream stream, ICertificate certificate)
+ {
+ var sslStream = (SslStream)stream;
+ var cert = (Certificate)certificate;
+
+ return sslStream.AuthenticateAsServerAsync(cert.X509Certificate);
+ }
+
+ public Stream CreateSslStream(Stream innerStream, bool leaveInnerStreamOpen)
+ {
+ return new SslStream(innerStream, leaveInnerStreamOpen);
+ }
+ }
+
+ public class Certificate : ICertificate
+ {
+ public Certificate(X509Certificate x509Certificate)
+ {
+ X509Certificate = x509Certificate;
+ }
+
+ public X509Certificate X509Certificate { get; private set; }
+ }
+}
diff --git a/Emby.Server.Core/IO/LibraryMonitor.cs b/Emby.Server.Core/IO/LibraryMonitor.cs
new file mode 100644
index 0000000000..6ed096f441
--- /dev/null
+++ b/Emby.Server.Core/IO/LibraryMonitor.cs
@@ -0,0 +1,631 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.System;
+using MediaBrowser.Model.Tasks;
+using MediaBrowser.Model.Threading;
+using Emby.Server.Implementations.IO;
+
+namespace Emby.Server.Core.IO
+{
+ public class LibraryMonitor : ILibraryMonitor
+ {
+ /// <summary>
+ /// The file system watchers
+ /// </summary>
+ private readonly ConcurrentDictionary<string, FileSystemWatcher> _fileSystemWatchers = new ConcurrentDictionary<string, FileSystemWatcher>(StringComparer.OrdinalIgnoreCase);
+ /// <summary>
+ /// The affected paths
+ /// </summary>
+ private readonly List<FileRefresher> _activeRefreshers = new List<FileRefresher>();
+
+ /// <summary>
+ /// A dynamic list of paths that should be ignored. Added to during our own file sytem modifications.
+ /// </summary>
+ private readonly ConcurrentDictionary<string, string> _tempIgnoredPaths = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+ /// <summary>
+ /// Any file name ending in any of these will be ignored by the watchers
+ /// </summary>
+ private readonly IReadOnlyList<string> _alwaysIgnoreFiles = new List<string>
+ {
+ "small.jpg",
+ "albumart.jpg",
+
+ // WMC temp recording directories that will constantly be written to
+ "TempRec",
+ "TempSBE"
+ };
+
+ private readonly IReadOnlyList<string> _alwaysIgnoreSubstrings = new List<string>
+ {
+ // Synology
+ "eaDir",
+ "#recycle",
+ ".wd_tv",
+ ".actors"
+ };
+
+ private readonly IReadOnlyList<string> _alwaysIgnoreExtensions = new List<string>
+ {
+ // thumbs.db
+ ".db",
+
+ // bts sync files
+ ".bts",
+ ".sync"
+ };
+
+ /// <summary>
+ /// Add the path to our temporary ignore list. Use when writing to a path within our listening scope.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ private void TemporarilyIgnore(string path)
+ {
+ _tempIgnoredPaths[path] = path;
+ }
+
+ public void ReportFileSystemChangeBeginning(string path)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ throw new ArgumentNullException("path");
+ }
+
+ TemporarilyIgnore(path);
+ }
+
+ public bool IsPathLocked(string path)
+ {
+ var lockedPaths = _tempIgnoredPaths.Keys.ToList();
+ return lockedPaths.Any(i => string.Equals(i, path, StringComparison.OrdinalIgnoreCase) || _fileSystem.ContainsSubPath(i, path));
+ }
+
+ public async void ReportFileSystemChangeComplete(string path, bool refreshPath)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ throw new ArgumentNullException("path");
+ }
+
+ // This is an arbitraty amount of time, but delay it because file system writes often trigger events long after the file was actually written to.
+ // Seeing long delays in some situations, especially over the network, sometimes up to 45 seconds
+ // But if we make this delay too high, we risk missing legitimate changes, such as user adding a new file, or hand-editing metadata
+ await Task.Delay(45000).ConfigureAwait(false);
+
+ string val;
+ _tempIgnoredPaths.TryRemove(path, out val);
+
+ if (refreshPath)
+ {
+ try
+ {
+ ReportFileSystemChanged(path);
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error in ReportFileSystemChanged for {0}", ex, path);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the logger.
+ /// </summary>
+ /// <value>The logger.</value>
+ private ILogger Logger { get; set; }
+
+ /// <summary>
+ /// Gets or sets the task manager.
+ /// </summary>
+ /// <value>The task manager.</value>
+ private ITaskManager TaskManager { get; set; }
+
+ private ILibraryManager LibraryManager { get; set; }
+ private IServerConfigurationManager ConfigurationManager { get; set; }
+
+ private readonly IFileSystem _fileSystem;
+ private readonly ITimerFactory _timerFactory;
+ private readonly IEnvironmentInfo _environmentInfo;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LibraryMonitor" /> class.
+ /// </summary>
+ public LibraryMonitor(ILogManager logManager, ITaskManager taskManager, ILibraryManager libraryManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ITimerFactory timerFactory, ISystemEvents systemEvents, IEnvironmentInfo environmentInfo)
+ {
+ if (taskManager == null)
+ {
+ throw new ArgumentNullException("taskManager");
+ }
+
+ LibraryManager = libraryManager;
+ TaskManager = taskManager;
+ Logger = logManager.GetLogger(GetType().Name);
+ ConfigurationManager = configurationManager;
+ _fileSystem = fileSystem;
+ _timerFactory = timerFactory;
+ _environmentInfo = environmentInfo;
+
+ systemEvents.Resume += _systemEvents_Resume;
+ }
+
+ private void _systemEvents_Resume(object sender, EventArgs e)
+ {
+ Restart();
+ }
+
+ private void Restart()
+ {
+ Stop();
+ Start();
+ }
+
+ private bool IsLibraryMonitorEnabaled(BaseItem item)
+ {
+ if (item is BasePluginFolder)
+ {
+ return false;
+ }
+
+ var options = LibraryManager.GetLibraryOptions(item);
+
+ if (options != null)
+ {
+ return options.EnableRealtimeMonitor;
+ }
+
+ return false;
+ }
+
+ public void Start()
+ {
+ LibraryManager.ItemAdded += LibraryManager_ItemAdded;
+ LibraryManager.ItemRemoved += LibraryManager_ItemRemoved;
+
+ var pathsToWatch = new List<string> { };
+
+ var paths = LibraryManager
+ .RootFolder
+ .Children
+ .Where(IsLibraryMonitorEnabaled)
+ .OfType<Folder>()
+ .SelectMany(f => f.PhysicalLocations)
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .OrderBy(i => i)
+ .ToList();
+
+ foreach (var path in paths)
+ {
+ if (!ContainsParentFolder(pathsToWatch, path))
+ {
+ pathsToWatch.Add(path);
+ }
+ }
+
+ foreach (var path in pathsToWatch)
+ {
+ StartWatchingPath(path);
+ }
+ }
+
+ private void StartWatching(BaseItem item)
+ {
+ if (IsLibraryMonitorEnabaled(item))
+ {
+ StartWatchingPath(item.Path);
+ }
+ }
+
+ /// <summary>
+ /// Handles the ItemRemoved event of the LibraryManager control.
+ /// </summary>
+ /// <param name="sender">The source of the event.</param>
+ /// <param name="e">The <see cref="ItemChangeEventArgs"/> instance containing the event data.</param>
+ void LibraryManager_ItemRemoved(object sender, ItemChangeEventArgs e)
+ {
+ if (e.Item.GetParent() is AggregateFolder)
+ {
+ StopWatchingPath(e.Item.Path);
+ }
+ }
+
+ /// <summary>
+ /// Handles the ItemAdded event of the LibraryManager control.
+ /// </summary>
+ /// <param name="sender">The source of the event.</param>
+ /// <param name="e">The <see cref="ItemChangeEventArgs"/> instance containing the event data.</param>
+ void LibraryManager_ItemAdded(object sender, ItemChangeEventArgs e)
+ {
+ if (e.Item.GetParent() is AggregateFolder)
+ {
+ StartWatching(e.Item);
+ }
+ }
+
+ /// <summary>
+ /// Examine a list of strings assumed to be file paths to see if it contains a parent of
+ /// the provided path.
+ /// </summary>
+ /// <param name="lst">The LST.</param>
+ /// <param name="path">The path.</param>
+ /// <returns><c>true</c> if [contains parent folder] [the specified LST]; otherwise, <c>false</c>.</returns>
+ /// <exception cref="System.ArgumentNullException">path</exception>
+ private static bool ContainsParentFolder(IEnumerable<string> lst, string path)
+ {
+ if (string.IsNullOrWhiteSpace(path))
+ {
+ throw new ArgumentNullException("path");
+ }
+
+ path = path.TrimEnd(Path.DirectorySeparatorChar);
+
+ return lst.Any(str =>
+ {
+ //this should be a little quicker than examining each actual parent folder...
+ var compare = str.TrimEnd(Path.DirectorySeparatorChar);
+
+ return path.Equals(compare, StringComparison.OrdinalIgnoreCase) || (path.StartsWith(compare, StringComparison.OrdinalIgnoreCase) && path[compare.Length] == Path.DirectorySeparatorChar);
+ });
+ }
+
+ /// <summary>
+ /// Starts the watching path.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ private void StartWatchingPath(string path)
+ {
+ // Creating a FileSystemWatcher over the LAN can take hundreds of milliseconds, so wrap it in a Task to do them all in parallel
+ Task.Run(() =>
+ {
+ try
+ {
+ var newWatcher = new FileSystemWatcher(path, "*")
+ {
+ IncludeSubdirectories = true
+ };
+
+ if (_environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows)
+ {
+ newWatcher.InternalBufferSize = 32767;
+ }
+
+ newWatcher.NotifyFilter = NotifyFilters.CreationTime |
+ NotifyFilters.DirectoryName |
+ NotifyFilters.FileName |
+ NotifyFilters.LastWrite |
+ NotifyFilters.Size |
+ NotifyFilters.Attributes;
+
+ newWatcher.Created += watcher_Changed;
+ newWatcher.Deleted += watcher_Changed;
+ newWatcher.Renamed += watcher_Changed;
+ newWatcher.Changed += watcher_Changed;
+
+ newWatcher.Error += watcher_Error;
+
+ if (_fileSystemWatchers.TryAdd(path, newWatcher))
+ {
+ newWatcher.EnableRaisingEvents = true;
+ Logger.Info("Watching directory " + path);
+ }
+ else
+ {
+ Logger.Info("Unable to add directory watcher for {0}. It already exists in the dictionary.", path);
+ newWatcher.Dispose();
+ }
+
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error watching path: {0}", ex, path);
+ }
+ });
+ }
+
+ /// <summary>
+ /// Stops the watching path.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ private void StopWatchingPath(string path)
+ {
+ FileSystemWatcher watcher;
+
+ if (_fileSystemWatchers.TryGetValue(path, out watcher))
+ {
+ DisposeWatcher(watcher);
+ }
+ }
+
+ /// <summary>
+ /// Disposes the watcher.
+ /// </summary>
+ /// <param name="watcher">The watcher.</param>
+ private void DisposeWatcher(FileSystemWatcher watcher)
+ {
+ try
+ {
+ using (watcher)
+ {
+ Logger.Info("Stopping directory watching for path {0}", watcher.Path);
+
+ watcher.EnableRaisingEvents = false;
+ }
+ }
+ catch
+ {
+
+ }
+ finally
+ {
+ RemoveWatcherFromList(watcher);
+ }
+ }
+
+ /// <summary>
+ /// Removes the watcher from list.
+ /// </summary>
+ /// <param name="watcher">The watcher.</param>
+ private void RemoveWatcherFromList(FileSystemWatcher watcher)
+ {
+ FileSystemWatcher removed;
+
+ _fileSystemWatchers.TryRemove(watcher.Path, out removed);
+ }
+
+ /// <summary>
+ /// Handles the Error event of the watcher control.
+ /// </summary>
+ /// <param name="sender">The source of the event.</param>
+ /// <param name="e">The <see cref="ErrorEventArgs" /> instance containing the event data.</param>
+ void watcher_Error(object sender, ErrorEventArgs e)
+ {
+ var ex = e.GetException();
+ var dw = (FileSystemWatcher)sender;
+
+ Logger.ErrorException("Error in Directory watcher for: " + dw.Path, ex);
+
+ DisposeWatcher(dw);
+ }
+
+ /// <summary>
+ /// Handles the Changed event of the watcher control.
+ /// </summary>
+ /// <param name="sender">The source of the event.</param>
+ /// <param name="e">The <see cref="FileSystemEventArgs" /> instance containing the event data.</param>
+ void watcher_Changed(object sender, FileSystemEventArgs e)
+ {
+ try
+ {
+ Logger.Debug("Changed detected of type " + e.ChangeType + " to " + e.FullPath);
+
+ var path = e.FullPath;
+
+ // For deletes, use the parent path
+ if (e.ChangeType == WatcherChangeTypes.Deleted)
+ {
+ var parentPath = Path.GetDirectoryName(path);
+
+ if (!string.IsNullOrWhiteSpace(parentPath))
+ {
+ path = parentPath;
+ }
+ }
+
+ ReportFileSystemChanged(path);
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Exception in ReportFileSystemChanged. Path: {0}", ex, e.FullPath);
+ }
+ }
+
+ public void ReportFileSystemChanged(string path)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ throw new ArgumentNullException("path");
+ }
+
+ var filename = Path.GetFileName(path);
+
+ var monitorPath = !string.IsNullOrEmpty(filename) &&
+ !_alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase) &&
+ !_alwaysIgnoreExtensions.Contains(Path.GetExtension(path) ?? string.Empty, StringComparer.OrdinalIgnoreCase) &&
+ _alwaysIgnoreSubstrings.All(i => path.IndexOf(i, StringComparison.OrdinalIgnoreCase) == -1);
+
+ // Ignore certain files
+ var tempIgnorePaths = _tempIgnoredPaths.Keys.ToList();
+
+ // If the parent of an ignored path has a change event, ignore that too
+ if (tempIgnorePaths.Any(i =>
+ {
+ if (string.Equals(i, path, StringComparison.OrdinalIgnoreCase))
+ {
+ Logger.Debug("Ignoring change to {0}", path);
+ return true;
+ }
+
+ if (_fileSystem.ContainsSubPath(i, path))
+ {
+ Logger.Debug("Ignoring change to {0}", path);
+ return true;
+ }
+
+ // Go up a level
+ var parent = Path.GetDirectoryName(i);
+ if (!string.IsNullOrEmpty(parent))
+ {
+ if (string.Equals(parent, path, StringComparison.OrdinalIgnoreCase))
+ {
+ Logger.Debug("Ignoring change to {0}", path);
+ return true;
+ }
+ }
+
+ return false;
+
+ }))
+ {
+ monitorPath = false;
+ }
+
+ if (monitorPath)
+ {
+ // Avoid implicitly captured closure
+ CreateRefresher(path);
+ }
+ }
+
+ private void CreateRefresher(string path)
+ {
+ var parentPath = Path.GetDirectoryName(path);
+
+ lock (_activeRefreshers)
+ {
+ var refreshers = _activeRefreshers.ToList();
+ foreach (var refresher in refreshers)
+ {
+ // Path is already being refreshed
+ if (string.Equals(path, refresher.Path, StringComparison.Ordinal))
+ {
+ refresher.RestartTimer();
+ return;
+ }
+
+ // Parent folder is already being refreshed
+ if (_fileSystem.ContainsSubPath(refresher.Path, path))
+ {
+ refresher.AddPath(path);
+ return;
+ }
+
+ // New path is a parent
+ if (_fileSystem.ContainsSubPath(path, refresher.Path))
+ {
+ refresher.ResetPath(path, null);
+ return;
+ }
+
+ // They are siblings. Rebase the refresher to the parent folder.
+ if (string.Equals(parentPath, Path.GetDirectoryName(refresher.Path), StringComparison.Ordinal))
+ {
+ refresher.ResetPath(parentPath, path);
+ return;
+ }
+ }
+
+ var newRefresher = new FileRefresher(path, _fileSystem, ConfigurationManager, LibraryManager, TaskManager, Logger, _timerFactory, _environmentInfo);
+ newRefresher.Completed += NewRefresher_Completed;
+ _activeRefreshers.Add(newRefresher);
+ }
+ }
+
+ private void NewRefresher_Completed(object sender, EventArgs e)
+ {
+ var refresher = (FileRefresher)sender;
+ DisposeRefresher(refresher);
+ }
+
+ /// <summary>
+ /// Stops this instance.
+ /// </summary>
+ public void Stop()
+ {
+ LibraryManager.ItemAdded -= LibraryManager_ItemAdded;
+ LibraryManager.ItemRemoved -= LibraryManager_ItemRemoved;
+
+ 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();
+ }
+
+ _fileSystemWatchers.Clear();
+ DisposeRefreshers();
+ }
+
+ private void DisposeRefresher(FileRefresher refresher)
+ {
+ lock (_activeRefreshers)
+ {
+ refresher.Dispose();
+ _activeRefreshers.Remove(refresher);
+ }
+ }
+
+ private void DisposeRefreshers()
+ {
+ lock (_activeRefreshers)
+ {
+ foreach (var refresher in _activeRefreshers.ToList())
+ {
+ refresher.Dispose();
+ }
+ _activeRefreshers.Clear();
+ }
+ }
+
+ /// <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)
+ {
+ Stop();
+ }
+ }
+ }
+
+ public class LibraryMonitorStartup : IServerEntryPoint
+ {
+ private readonly ILibraryMonitor _monitor;
+
+ public LibraryMonitorStartup(ILibraryMonitor monitor)
+ {
+ _monitor = monitor;
+ }
+
+ public void Run()
+ {
+ _monitor.Start();
+ }
+
+ public void Dispose()
+ {
+ }
+ }
+}
diff --git a/Emby.Server.Core/Localization/TextLocalizer.cs b/Emby.Server.Core/Localization/TextLocalizer.cs
new file mode 100644
index 0000000000..6690c62638
--- /dev/null
+++ b/Emby.Server.Core/Localization/TextLocalizer.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using Emby.Server.Implementations.Localization;
+
+namespace Emby.Server.Core.Localization
+{
+ public class TextLocalizer : ITextLocalizer
+ {
+ public string RemoveDiacritics(string text)
+ {
+ if (text == null)
+ {
+ throw new ArgumentNullException("text");
+ }
+
+ var chars = Normalize(text, NormalizationForm.FormD)
+ .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) != UnicodeCategory.NonSpacingMark);
+
+ return Normalize(String.Concat(chars), NormalizationForm.FormC);
+ }
+
+ private static string Normalize(string text, NormalizationForm form, bool stripStringOnFailure = true)
+ {
+ if (stripStringOnFailure)
+ {
+ try
+ {
+ return text.Normalize(form);
+ }
+ catch (ArgumentException)
+ {
+ // will throw if input contains invalid unicode chars
+ // https://mnaoumov.wordpress.com/2014/06/14/stripping-invalid-characters-from-utf-16-strings/
+ text = StripInvalidUnicodeCharacters(text);
+ return Normalize(text, form, false);
+ }
+ }
+
+ return text.Normalize(form);
+ }
+
+ private static string StripInvalidUnicodeCharacters(string str)
+ {
+ var invalidCharactersRegex = new Regex("([\ud800-\udbff](?![\udc00-\udfff]))|((?<![\ud800-\udbff])[\udc00-\udfff])");
+ return invalidCharactersRegex.Replace(str, "");
+ }
+
+ public string NormalizeFormKD(string text)
+ {
+ return text.Normalize(NormalizationForm.FormKD);
+ }
+ }
+}
diff --git a/Emby.Server.Core/Properties/AssemblyInfo.cs b/Emby.Server.Core/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..69df3d58db
--- /dev/null
+++ b/Emby.Server.Core/Properties/AssemblyInfo.cs
@@ -0,0 +1,19 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Emby.Server.Core")]
+[assembly: AssemblyTrademark("")]
+
+// 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("65aa7d67-8059-40cd-91f1-16d02687226c")]
diff --git a/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs b/Emby.Server.Core/ServerApplicationPaths.cs
index 237d49fdae..dc80b773c5 100644
--- a/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs
+++ b/Emby.Server.Core/ServerApplicationPaths.cs
@@ -1,8 +1,8 @@
-using MediaBrowser.Common.Implementations;
+using System.IO;
+using Emby.Common.Implementations;
using MediaBrowser.Controller;
-using System.IO;
-namespace MediaBrowser.Server.Implementations
+namespace Emby.Server.Core
{
/// <summary>
/// Extends BaseApplicationPaths to add paths that are only applicable on the server
@@ -12,8 +12,8 @@ namespace MediaBrowser.Server.Implementations
/// <summary>
/// Initializes a new instance of the <see cref="BaseApplicationPaths" /> class.
/// </summary>
- public ServerApplicationPaths(string programDataPath, string applicationPath, string applicationResourcesPath)
- : base(programDataPath, applicationPath)
+ public ServerApplicationPaths(string programDataPath, string appFolderPath, string applicationResourcesPath)
+ : base(programDataPath, appFolderPath)
{
ApplicationResourcesPath = applicationResourcesPath;
}
diff --git a/MediaBrowser.Server.Startup.Common/UnhandledExceptionWriter.cs b/Emby.Server.Core/UnhandledExceptionWriter.cs
index 804533b9d1..5147be9e7c 100644
--- a/MediaBrowser.Server.Startup.Common/UnhandledExceptionWriter.cs
+++ b/Emby.Server.Core/UnhandledExceptionWriter.cs
@@ -1,10 +1,9 @@
using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Implementations.Logging;
using MediaBrowser.Model.Logging;
using System;
using System.IO;
-namespace MediaBrowser.Server.Startup.Common
+namespace Emby.Server.Core
{
public class UnhandledExceptionWriter
{
diff --git a/Emby.Server.Core/project.json b/Emby.Server.Core/project.json
new file mode 100644
index 0000000000..e987da6aa9
--- /dev/null
+++ b/Emby.Server.Core/project.json
@@ -0,0 +1,131 @@
+{
+ "version": "1.0.0-*",
+
+ "dependencies": {
+
+ },
+
+ "frameworks": {
+ "net46": {
+ "frameworkAssemblies": {
+ "System.Runtime": "4.0.0"
+ },
+ "dependencies": {
+ "MediaBrowser.Model": {
+ "target": "project"
+ },
+ "MediaBrowser.Common": {
+ "target": "project"
+ },
+ "MediaBrowser.Controller": {
+ "target": "project"
+ },
+ "Emby.Common.Implementations": {
+ "target": "project"
+ },
+ "Mono.Nat": {
+ "target": "project"
+ },
+ "Emby.Server.Implementations": {
+ "target": "project"
+ },
+ "MediaBrowser.Server.Implementations": {
+ "target": "project"
+ },
+ "Emby.Dlna": {
+ "target": "project"
+ },
+ "Emby.Photos": {
+ "target": "project"
+ },
+ "MediaBrowser.Api": {
+ "target": "project"
+ },
+ "MediaBrowser.MediaEncoding": {
+ "target": "project"
+ },
+ "MediaBrowser.XbmcMetadata": {
+ "target": "project"
+ },
+ "MediaBrowser.LocalMetadata": {
+ "target": "project"
+ },
+ "MediaBrowser.WebDashboard": {
+ "target": "project"
+ },
+ "Emby.Drawing": {
+ "target": "project"
+ },
+ "ServiceStack": {
+ "target": "project"
+ },
+ "SocketHttpListener.Portable": {
+ "target": "project"
+ }
+ }
+ },
+ "netstandard1.6": {
+ "imports": "dnxcore50",
+ "dependencies": {
+ "NETStandard.Library": "1.6.1",
+ "System.AppDomain": "2.0.11",
+ "System.Globalization.Extensions": "4.3.0",
+ "System.IO.FileSystem.Watcher": "4.3.0",
+ "System.Net.Security": "4.3.0",
+ "System.Security.Cryptography.X509Certificates": "4.3.0",
+ "System.Runtime.Extensions": "4.3.0",
+ "MediaBrowser.Model": {
+ "target": "project"
+ },
+ "MediaBrowser.Common": {
+ "target": "project"
+ },
+ "MediaBrowser.Controller": {
+ "target": "project"
+ },
+ "Emby.Common.Implementations": {
+ "target": "project"
+ },
+ "Mono.Nat": {
+ "target": "project"
+ },
+ "Emby.Server.Implementations": {
+ "target": "project"
+ },
+ "MediaBrowser.Server.Implementations": {
+ "target": "project"
+ },
+ "Emby.Dlna": {
+ "target": "project"
+ },
+ "Emby.Photos": {
+ "target": "project"
+ },
+ "MediaBrowser.Api": {
+ "target": "project"
+ },
+ "MediaBrowser.MediaEncoding": {
+ "target": "project"
+ },
+ "MediaBrowser.XbmcMetadata": {
+ "target": "project"
+ },
+ "MediaBrowser.LocalMetadata": {
+ "target": "project"
+ },
+ "MediaBrowser.WebDashboard": {
+ "target": "project"
+ },
+ "Emby.Drawing": {
+ "target": "project"
+ },
+ "SocketHttpListener.Portable": {
+ "target": "project"
+ },
+ "ServiceStack": {
+ "target": "project"
+ }
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
index a36583a412..11fd3a872e 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/ActivityLogEntryPoint.cs
+++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
@@ -1,14 +1,10 @@
using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Implementations.Logging;
using MediaBrowser.Common.Plugins;
-using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller;
-using MediaBrowser.Controller.Activity;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
using MediaBrowser.Controller.Subtitles;
@@ -21,8 +17,9 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using MediaBrowser.Model.Globalization;
-namespace MediaBrowser.Server.Implementations.EntryPoints
+namespace Emby.Server.Implementations.Activity
{
public class ActivityLogEntryPoint : IServerEntryPoint
{
@@ -126,8 +123,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
return;
}
- var themeMedia = item as IThemeMedia;
- if (themeMedia != null && themeMedia.IsThemeMedia)
+ if (item.IsThemeMedia)
{
// Don't report theme song or local trailer playback
return;
@@ -159,8 +155,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
return;
}
- var themeMedia = item as IThemeMedia;
- if (themeMedia != null && themeMedia.IsThemeMedia)
+ if (item.IsThemeMedia)
{
// Don't report theme song or local trailer playback
return;
@@ -401,8 +396,8 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
{
var task = e.Argument;
- var activityTask = task.ScheduledTask as IScheduledTaskActivityLog;
- if (activityTask != null && !activityTask.IsActivityLogged)
+ var activityTask = task.ScheduledTask as IConfigurableScheduledTask;
+ if (activityTask != null && !activityTask.IsLogged)
{
return;
}
@@ -419,8 +414,8 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
var result = e.Result;
var task = e.Task;
- var activityTask = task.ScheduledTask as IScheduledTaskActivityLog;
- if (activityTask != null && !activityTask.IsActivityLogged)
+ var activityTask = task.ScheduledTask as IConfigurableScheduledTask;
+ if (activityTask != null && !activityTask.IsLogged)
{
return;
}
diff --git a/MediaBrowser.Server.Implementations/Activity/ActivityManager.cs b/Emby.Server.Implementations/Activity/ActivityManager.cs
index 0904c92f1c..b6095f0820 100644
--- a/MediaBrowser.Server.Implementations/Activity/ActivityManager.cs
+++ b/Emby.Server.Implementations/Activity/ActivityManager.cs
@@ -1,5 +1,4 @@
using MediaBrowser.Common.Events;
-using MediaBrowser.Controller.Activity;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Events;
@@ -9,7 +8,7 @@ using System;
using System.Linq;
using System.Threading.Tasks;
-namespace MediaBrowser.Server.Implementations.Activity
+namespace Emby.Server.Implementations.Activity
{
public class ActivityManager : IActivityManager
{
diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs
new file mode 100644
index 0000000000..bf88358465
--- /dev/null
+++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs
@@ -0,0 +1,222 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Emby.Server.Implementations.Data;
+using MediaBrowser.Controller;
+using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Querying;
+using SQLitePCL.pretty;
+
+namespace Emby.Server.Implementations.Activity
+{
+ public class ActivityRepository : BaseSqliteRepository, IActivityRepository
+ {
+ private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+
+ public ActivityRepository(ILogger logger, IServerApplicationPaths appPaths)
+ : base(logger)
+ {
+ DbFilePath = Path.Combine(appPaths.DataPath, "activitylog.db");
+ }
+
+ public void Initialize()
+ {
+ using (var connection = CreateConnection())
+ {
+ RunDefaultInitialization(connection);
+
+ string[] queries = {
+ "create table if not exists ActivityLogEntries (Id GUID PRIMARY KEY, Name TEXT, Overview TEXT, ShortOverview TEXT, Type TEXT, ItemId TEXT, UserId TEXT, DateCreated DATETIME, LogSeverity TEXT)",
+ "create index if not exists idx_ActivityLogEntries on ActivityLogEntries(Id)"
+ };
+
+ connection.RunQueries(queries);
+ }
+ }
+
+ private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLogEntries";
+
+ public Task Create(ActivityLogEntry entry)
+ {
+ return Update(entry);
+ }
+
+ public async Task Update(ActivityLogEntry 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 ActivityLogEntries (Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) values (@Id, @Name, @Overview, @ShortOverview, @Type, @ItemId, @UserId, @DateCreated, @LogSeverity)"))
+ {
+ statement.TryBind("@Id", entry.Id.ToGuidParamValue());
+ 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);
+ statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue());
+ statement.TryBind("@LogSeverity", entry.Severity.ToString());
+
+ statement.MoveNext();
+ }
+ }, TransactionMode);
+ }
+ }
+ }
+
+ public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit)
+ {
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
+ {
+ var commandText = BaseActivitySelectText;
+ var whereClauses = new List<string>();
+
+ if (minDate.HasValue)
+ {
+ whereClauses.Add("DateCreated>=@DateCreated");
+ }
+
+ var whereTextWithoutPaging = whereClauses.Count == 0 ?
+ string.Empty :
+ " where " + string.Join(" AND ", whereClauses.ToArray());
+
+ if (startIndex.HasValue && startIndex.Value > 0)
+ {
+ var pagingWhereText = whereClauses.Count == 0 ?
+ string.Empty :
+ " where " + string.Join(" AND ", whereClauses.ToArray());
+
+ whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM ActivityLogEntries {0} ORDER BY DateCreated DESC LIMIT {1})",
+ pagingWhereText,
+ startIndex.Value.ToString(_usCulture)));
+ }
+
+ var whereText = whereClauses.Count == 0 ?
+ string.Empty :
+ " where " + string.Join(" AND ", whereClauses.ToArray());
+
+ commandText += whereText;
+
+ commandText += " ORDER BY DateCreated DESC";
+
+ if (limit.HasValue)
+ {
+ commandText += " LIMIT " + limit.Value.ToString(_usCulture);
+ }
+
+ var statementTexts = new List<string>();
+ statementTexts.Add(commandText);
+ statementTexts.Add("select count (Id) from ActivityLogEntries" + whereTextWithoutPaging);
+
+ return connection.RunInTransaction(db =>
+ {
+ var list = new List<ActivityLogEntry>();
+ var result = new QueryResult<ActivityLogEntry>();
+
+ var statements = PrepareAllSafe(db, statementTexts).ToList();
+
+ using (var statement = statements[0])
+ {
+ if (minDate.HasValue)
+ {
+ statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue());
+ }
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ list.Add(GetEntry(row));
+ }
+ }
+
+ using (var statement = statements[1])
+ {
+ if (minDate.HasValue)
+ {
+ statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue());
+ }
+
+ result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
+ }
+
+ result.Items = list.ToArray();
+ return result;
+
+ }, ReadTransactionMode);
+ }
+ }
+ }
+
+ private ActivityLogEntry GetEntry(IReadOnlyList<IResultSetValue> reader)
+ {
+ var index = 0;
+
+ var info = new ActivityLogEntry
+ {
+ Id = reader[index].ReadGuid().ToString("N")
+ };
+
+ index++;
+ if (reader[index].SQLiteType != SQLiteType.Null)
+ {
+ info.Name = reader[index].ToString();
+ }
+
+ index++;
+ if (reader[index].SQLiteType != SQLiteType.Null)
+ {
+ info.Overview = reader[index].ToString();
+ }
+
+ index++;
+ if (reader[index].SQLiteType != SQLiteType.Null)
+ {
+ info.ShortOverview = reader[index].ToString();
+ }
+
+ index++;
+ if (reader[index].SQLiteType != SQLiteType.Null)
+ {
+ info.Type = reader[index].ToString();
+ }
+
+ index++;
+ if (reader[index].SQLiteType != SQLiteType.Null)
+ {
+ info.ItemId = reader[index].ToString();
+ }
+
+ index++;
+ if (reader[index].SQLiteType != SQLiteType.Null)
+ {
+ info.UserId = reader[index].ToString();
+ }
+
+ index++;
+ info.Date = reader[index].ReadDateTime();
+
+ index++;
+ if (reader[index].SQLiteType != SQLiteType.Null)
+ {
+ info.Severity = (LogSeverity)Enum.Parse(typeof(LogSeverity), reader[index].ToString(), true);
+ }
+
+ return info;
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Branding/BrandingConfigurationFactory.cs b/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs
index d6cd3424b1..a29f55f168 100644
--- a/MediaBrowser.Server.Implementations/Branding/BrandingConfigurationFactory.cs
+++ b/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs
@@ -2,7 +2,7 @@
using MediaBrowser.Model.Branding;
using System.Collections.Generic;
-namespace MediaBrowser.Server.Implementations.Branding
+namespace Emby.Server.Implementations.Branding
{
public class BrandingConfigurationFactory : IConfigurationFactory
{
diff --git a/MediaBrowser.Server.Startup.Common/Browser/BrowserLauncher.cs b/Emby.Server.Implementations/Browser/BrowserLauncher.cs
index 1a0e2d9731..05cde91e22 100644
--- a/MediaBrowser.Server.Startup.Common/Browser/BrowserLauncher.cs
+++ b/Emby.Server.Implementations/Browser/BrowserLauncher.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Controller;
using System;
-namespace MediaBrowser.Server.Startup.Common.Browser
+namespace Emby.Server.Implementations.Browser
{
/// <summary>
/// Class BrowserLauncher
@@ -65,10 +65,8 @@ namespace MediaBrowser.Server.Startup.Common.Browser
{
}
- catch (Exception ex)
+ catch (Exception)
{
- Console.WriteLine("Error launching url: " + url);
- Console.WriteLine(ex.Message);
}
}
}
diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelConfigurations.cs b/Emby.Server.Implementations/Channels/ChannelConfigurations.cs
index 9dfb0404e0..ef0973e7f4 100644
--- a/MediaBrowser.Server.Implementations/Channels/ChannelConfigurations.cs
+++ b/Emby.Server.Implementations/Channels/ChannelConfigurations.cs
@@ -2,7 +2,7 @@
using MediaBrowser.Model.Configuration;
using System.Collections.Generic;
-namespace MediaBrowser.Server.Implementations.Channels
+namespace Emby.Server.Implementations.Channels
{
public static class ChannelConfigurationExtension
{
diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs b/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
index fae78b9bc0..98011ddd48 100644
--- a/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
+++ b/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
@@ -7,7 +7,7 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
-namespace MediaBrowser.Server.Implementations.Channels
+namespace Emby.Server.Implementations.Channels
{
public class ChannelDynamicMediaSourceProvider : IMediaSourceProvider
{
diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelImageProvider.cs b/Emby.Server.Implementations/Channels/ChannelImageProvider.cs
index c98f71ce2a..f892b1e620 100644
--- a/MediaBrowser.Server.Implementations/Channels/ChannelImageProvider.cs
+++ b/Emby.Server.Implementations/Channels/ChannelImageProvider.cs
@@ -7,7 +7,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-namespace MediaBrowser.Server.Implementations.Channels
+namespace Emby.Server.Implementations.Channels
{
public class ChannelImageProvider : IDynamicImageProvider, IHasItemChangeMonitor
{
diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index 1369efae1b..0df916ded3 100644
--- a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -5,7 +5,6 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Dto;
@@ -24,12 +23,15 @@ using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.Globalization;
-namespace MediaBrowser.Server.Implementations.Channels
+namespace Emby.Server.Implementations.Channels
{
public class ChannelManager : IChannelManager
{
@@ -250,6 +252,42 @@ namespace MediaBrowser.Server.Implementations.Channels
return item;
}
+ private List<ChannelMediaInfo> GetSavedMediaSources(BaseItem item)
+ {
+ var path = Path.Combine(item.GetInternalMetadataPath(), "channelmediasources.json");
+
+ try
+ {
+ return _jsonSerializer.DeserializeFromFile<List<ChannelMediaInfo>>(path) ?? new List<ChannelMediaInfo>();
+ }
+ catch
+ {
+ return new List<ChannelMediaInfo>();
+ }
+ }
+
+ private void SaveMediaSources(BaseItem item, List<ChannelMediaInfo> mediaSources)
+ {
+ var path = Path.Combine(item.GetInternalMetadataPath(), "channelmediasources.json");
+
+ if (mediaSources == null || mediaSources.Count == 0)
+ {
+ try
+ {
+ _fileSystem.DeleteFile(path);
+ }
+ catch
+ {
+
+ }
+ return;
+ }
+
+ _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
+
+ _jsonSerializer.SerializeToFile(mediaSources, path);
+ }
+
public async Task<IEnumerable<MediaSourceInfo>> GetStaticMediaSources(BaseItem item, bool includeCachedVersions, CancellationToken cancellationToken)
{
IEnumerable<ChannelMediaInfo> results = new List<ChannelMediaInfo>();
@@ -261,7 +299,7 @@ namespace MediaBrowser.Server.Implementations.Channels
var audio = item as Audio;
if (audio != null)
{
- results = audio.ChannelMediaSources ?? new List<ChannelMediaInfo>();
+ results = audio.ChannelMediaSources ?? GetSavedMediaSources(audio);
}
var sources = SortMediaInfoResults(results)
@@ -288,7 +326,7 @@ namespace MediaBrowser.Server.Implementations.Channels
if (requiresCallback != null)
{
- results = await GetChannelItemMediaSourcesInternal(requiresCallback, item.ExternalId, cancellationToken)
+ results = await GetChannelItemMediaSourcesInternal(requiresCallback, GetItemExternalId(item), cancellationToken)
.ConfigureAwait(false);
}
else
@@ -372,7 +410,7 @@ namespace MediaBrowser.Server.Implementations.Channels
}
}
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
}
@@ -743,7 +781,7 @@ namespace MediaBrowser.Server.Implementations.Channels
{
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
}
@@ -763,7 +801,7 @@ namespace MediaBrowser.Server.Implementations.Channels
{
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
}
@@ -905,7 +943,7 @@ namespace MediaBrowser.Server.Implementations.Channels
{
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
}
@@ -925,7 +963,7 @@ namespace MediaBrowser.Server.Implementations.Channels
{
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
}
@@ -1037,6 +1075,18 @@ namespace MediaBrowser.Server.Implementations.Channels
return result;
}
+ private string GetItemExternalId(BaseItem item)
+ {
+ var externalId = item.ExternalId;
+
+ if (string.IsNullOrWhiteSpace(externalId))
+ {
+ externalId = item.GetProviderId("ProviderExternalId");
+ }
+
+ return externalId;
+ }
+
private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1);
private async Task<ChannelItemResult> GetChannelItems(IChannel channel,
User user,
@@ -1058,7 +1108,11 @@ namespace MediaBrowser.Server.Implementations.Channels
{
if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
{
- return _jsonSerializer.DeserializeFromFile<ChannelItemResult>(cachePath);
+ var cachedResult = _jsonSerializer.DeserializeFromFile<ChannelItemResult>(cachePath);
+ if (cachedResult != null)
+ {
+ return cachedResult;
+ }
}
}
}
@@ -1066,7 +1120,7 @@ namespace MediaBrowser.Server.Implementations.Channels
{
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
}
@@ -1081,7 +1135,11 @@ namespace MediaBrowser.Server.Implementations.Channels
{
if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
{
- return _jsonSerializer.DeserializeFromFile<ChannelItemResult>(cachePath);
+ var cachedResult = _jsonSerializer.DeserializeFromFile<ChannelItemResult>(cachePath);
+ if (cachedResult != null)
+ {
+ return cachedResult;
+ }
}
}
}
@@ -1089,7 +1147,7 @@ namespace MediaBrowser.Server.Implementations.Channels
{
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
}
@@ -1107,11 +1165,16 @@ namespace MediaBrowser.Server.Implementations.Channels
{
var categoryItem = _libraryManager.GetItemById(new Guid(folderId));
- query.FolderId = categoryItem.ExternalId;
+ query.FolderId = GetItemExternalId(categoryItem);
}
var result = await channel.GetChannelItems(query, cancellationToken).ConfigureAwait(false);
+ if (result == null)
+ {
+ throw new InvalidOperationException("Channel returned a null result from GetChannelItems");
+ }
+
if (!startIndex.HasValue && !limit.HasValue)
{
CacheResponse(result, cachePath);
@@ -1383,7 +1446,6 @@ namespace MediaBrowser.Server.Implementations.Channels
if (channelAudioItem != null)
{
channelAudioItem.ExtraType = info.ExtraType;
- channelAudioItem.ChannelMediaSources = info.MediaSources;
var mediaSource = info.MediaSources.FirstOrDefault();
item.Path = mediaSource == null ? null : mediaSource.Path;
@@ -1424,6 +1486,8 @@ namespace MediaBrowser.Server.Implementations.Channels
await item.UpdateToRepository(ItemUpdateType.None, cancellationToken).ConfigureAwait(false);
}
+ SaveMediaSources(item, info.MediaSources);
+
return item;
}
diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs
index b25c9c8180..aef06bd1d6 100644
--- a/MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs
+++ b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs
@@ -4,14 +4,14 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Logging;
-using MoreLinq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Extensions;
-namespace MediaBrowser.Server.Implementations.Channels
+namespace Emby.Server.Implementations.Channels
{
public class ChannelPostScanTask
{
diff --git a/MediaBrowser.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
index 5ac3da3db6..d5ec86445d 100644
--- a/MediaBrowser.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
+++ b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
@@ -1,14 +1,14 @@
-using MediaBrowser.Common.ScheduledTasks;
-using MediaBrowser.Controller.Channels;
+using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
+using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Server.Implementations.Channels
+namespace Emby.Server.Implementations.Channels
{
- class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask
+ class RefreshChannelsScheduledTask : IScheduledTask
{
private readonly IChannelManager _channelManager;
private readonly IUserManager _userManager;
@@ -48,14 +48,23 @@ namespace MediaBrowser.Server.Implementations.Channels
.ConfigureAwait(false);
}
- public IEnumerable<ITaskTrigger> GetDefaultTriggers()
+ /// <summary>
+ /// Creates the triggers that define when the task will run
+ /// </summary>
+ public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- return new ITaskTrigger[]
- {
- new IntervalTrigger{ Interval = TimeSpan.FromHours(24)}
+ return new[] {
+
+ // Every so often
+ new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
};
}
+ public string Key
+ {
+ get { return "RefreshInternetChannels"; }
+ }
+
public bool IsHidden
{
get { return false; }
diff --git a/MediaBrowser.Server.Implementations/Collections/CollectionImageProvider.cs b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
index 25393d30f6..b82d4e44e0 100644
--- a/MediaBrowser.Server.Implementations/Collections/CollectionImageProvider.cs
+++ b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs
@@ -6,14 +6,14 @@ using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Server.Implementations.Photos;
-using MoreLinq;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-using CommonIO;
+using Emby.Server.Implementations.Images;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Extensions;
-namespace MediaBrowser.Server.Implementations.Collections
+namespace Emby.Server.Implementations.Collections
{
public class CollectionImageProvider : BaseDynamicImageProvider<BoxSet>
{
diff --git a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs
index cb2bd645dd..d0bd76c354 100644
--- a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs
+++ b/Emby.Server.Implementations/Collections/CollectionManager.cs
@@ -11,9 +11,9 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.IO;
-namespace MediaBrowser.Server.Implementations.Collections
+namespace Emby.Server.Implementations.Collections
{
public class CollectionManager : ICollectionManager
{
diff --git a/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs b/Emby.Server.Implementations/Collections/CollectionsDynamicFolder.cs
index 50bb6c5592..4ff33e6451 100644
--- a/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs
+++ b/Emby.Server.Implementations/Collections/CollectionsDynamicFolder.cs
@@ -1,10 +1,12 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
using System.IO;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Collections;
+using MediaBrowser.Controller.IO;
-namespace MediaBrowser.Server.Implementations.Collections
+namespace Emby.Server.Implementations.Collections
{
public class CollectionsDynamicFolder : IVirtualFolderCreator
{
diff --git a/MediaBrowser.Server.Implementations/Connect/ConnectData.cs b/Emby.Server.Implementations/Connect/ConnectData.cs
index 5ec0bea22c..41b89ce52b 100644
--- a/MediaBrowser.Server.Implementations/Connect/ConnectData.cs
+++ b/Emby.Server.Implementations/Connect/ConnectData.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
-namespace MediaBrowser.Server.Implementations.Connect
+namespace Emby.Server.Implementations.Connect
{
public class ConnectData
{
diff --git a/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs b/Emby.Server.Implementations/Connect/ConnectEntryPoint.cs
index f9eff3c92f..170ef07f31 100644
--- a/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs
+++ b/Emby.Server.Implementations/Connect/ConnectEntryPoint.cs
@@ -7,18 +7,16 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
using System;
using System.IO;
-using System.Net;
-using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
-using CommonIO;
-using MediaBrowser.Common.Threading;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Threading;
-namespace MediaBrowser.Server.Implementations.Connect
+namespace Emby.Server.Implementations.Connect
{
public class ConnectEntryPoint : IServerEntryPoint
{
- private PeriodicTimer _timer;
+ private ITimer _timer;
private readonly IHttpClient _httpClient;
private readonly IApplicationPaths _appPaths;
private readonly ILogger _logger;
@@ -27,8 +25,9 @@ namespace MediaBrowser.Server.Implementations.Connect
private readonly INetworkManager _networkManager;
private readonly IApplicationHost _appHost;
private readonly IFileSystem _fileSystem;
+ private readonly ITimerFactory _timerFactory;
- public ConnectEntryPoint(IHttpClient httpClient, IApplicationPaths appPaths, ILogger logger, INetworkManager networkManager, IConnectManager connectManager, IApplicationHost appHost, IFileSystem fileSystem)
+ public ConnectEntryPoint(IHttpClient httpClient, IApplicationPaths appPaths, ILogger logger, INetworkManager networkManager, IConnectManager connectManager, IApplicationHost appHost, IFileSystem fileSystem, ITimerFactory timerFactory)
{
_httpClient = httpClient;
_appPaths = appPaths;
@@ -37,13 +36,14 @@ namespace MediaBrowser.Server.Implementations.Connect
_connectManager = connectManager;
_appHost = appHost;
_fileSystem = fileSystem;
+ _timerFactory = timerFactory;
}
public void Run()
{
LoadCachedAddress();
- _timer = new PeriodicTimer(TimerCallback, null, TimeSpan.FromSeconds(5), TimeSpan.FromHours(1));
+ _timer = _timerFactory.Create(TimerCallback, null, TimeSpan.FromSeconds(5), TimeSpan.FromHours(1));
((ConnectManager)_connectManager).Start();
}
@@ -55,7 +55,7 @@ namespace MediaBrowser.Server.Implementations.Connect
private async void TimerCallback(object state)
{
- IPAddress validIpAddress = null;
+ IpAddressInfo validIpAddress = null;
foreach (var ipLookupUrl in _ipLookups)
{
@@ -64,7 +64,7 @@ namespace MediaBrowser.Server.Implementations.Connect
validIpAddress = await GetIpAddress(ipLookupUrl).ConfigureAwait(false);
// Try to find the ipv4 address, if present
- if (validIpAddress.AddressFamily == AddressFamily.InterNetwork)
+ if (validIpAddress.AddressFamily != IpAddressFamily.InterNetworkV6)
{
break;
}
@@ -77,9 +77,9 @@ namespace MediaBrowser.Server.Implementations.Connect
_logger.ErrorException("Error getting connection info", ex);
}
}
-
+
// If this produced an ipv6 address, try again
- if (validIpAddress != null && validIpAddress.AddressFamily == AddressFamily.InterNetworkV6)
+ if (validIpAddress != null && validIpAddress.AddressFamily == IpAddressFamily.InterNetworkV6)
{
foreach (var ipLookupUrl in _ipLookups)
{
@@ -88,7 +88,7 @@ namespace MediaBrowser.Server.Implementations.Connect
var newAddress = await GetIpAddress(ipLookupUrl, true).ConfigureAwait(false);
// Try to find the ipv4 address, if present
- if (newAddress.AddressFamily == AddressFamily.InterNetwork)
+ if (newAddress.AddressFamily != IpAddressFamily.InterNetworkV6)
{
validIpAddress = newAddress;
break;
@@ -111,7 +111,7 @@ namespace MediaBrowser.Server.Implementations.Connect
}
}
- private async Task<IPAddress> GetIpAddress(string lookupUrl, bool preferIpv4 = false)
+ private async Task<IpAddressInfo> GetIpAddress(string lookupUrl, bool preferIpv4 = false)
{
// Sometimes whatismyipaddress might fail, but it won't do us any good having users raise alarms over it.
var logErrors = false;
@@ -136,7 +136,7 @@ namespace MediaBrowser.Server.Implementations.Connect
{
var addressString = await reader.ReadToEndAsync().ConfigureAwait(false);
- return IPAddress.Parse(addressString);
+ return _networkManager.ParseIpAddress(addressString);
}
}
}
@@ -146,7 +146,7 @@ namespace MediaBrowser.Server.Implementations.Connect
get { return Path.Combine(_appPaths.DataPath, "wan.txt"); }
}
- private void CacheAddress(IPAddress address)
+ private void CacheAddress(IpAddressInfo address)
{
var path = CacheFilePath;
@@ -170,9 +170,9 @@ namespace MediaBrowser.Server.Implementations.Connect
try
{
var endpoint = _fileSystem.ReadAllText(path, Encoding.UTF8);
- IPAddress ipAddress;
+ IpAddressInfo ipAddress;
- if (IPAddress.TryParse(endpoint, out ipAddress))
+ if (_networkManager.TryParseIpAddress(endpoint, out ipAddress))
{
((ConnectManager)_connectManager).OnWanAddressResolved(ipAddress);
}
diff --git a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs b/Emby.Server.Implementations/Connect/ConnectManager.cs
index d7c1b0da07..b7faaa9013 100644
--- a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs
+++ b/Emby.Server.Implementations/Connect/ConnectManager.cs
@@ -19,14 +19,13 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
-using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Common.Extensions;
-namespace MediaBrowser.Server.Implementations.Connect
+namespace Emby.Server.Implementations.Connect
{
public class ConnectManager : IConnectManager
{
@@ -55,7 +54,7 @@ namespace MediaBrowser.Server.Implementations.Connect
get { return _data.AccessKey; }
}
- private IPAddress DiscoveredWanIpAddress { get; set; }
+ private IpAddressInfo DiscoveredWanIpAddress { get; set; }
public string WanIpAddress
{
@@ -75,7 +74,7 @@ namespace MediaBrowser.Server.Implementations.Connect
if (string.IsNullOrWhiteSpace(address) && DiscoveredWanIpAddress != null)
{
- if (DiscoveredWanIpAddress.AddressFamily == AddressFamily.InterNetworkV6)
+ if (DiscoveredWanIpAddress.AddressFamily == IpAddressFamily.InterNetworkV6)
{
address = "[" + DiscoveredWanIpAddress + "]";
}
@@ -146,7 +145,7 @@ namespace MediaBrowser.Server.Implementations.Connect
_config.ConfigurationUpdated += _config_ConfigurationUpdated;
}
- internal void OnWanAddressResolved(IPAddress address)
+ internal void OnWanAddressResolved(IpAddressInfo address)
{
DiscoveredWanIpAddress = address;
@@ -818,7 +817,6 @@ namespace MediaBrowser.Server.Implementations.Connect
}
}
- private readonly SemaphoreSlim _connectImageSemaphore = new SemaphoreSlim(5, 5);
private async Task RefreshAuthorizations(List<ServerUserAuthorizationResponse> list, bool refreshImages)
{
var users = _userManager.Users.ToList();
@@ -993,7 +991,7 @@ namespace MediaBrowser.Server.Implementations.Connect
if (changed)
{
- await _providerManager.SaveImage(user, imageUrl, _connectImageSemaphore, ImageType.Primary, null, CancellationToken.None).ConfigureAwait(false);
+ await _providerManager.SaveImage(user, imageUrl, null, ImageType.Primary, null, CancellationToken.None).ConfigureAwait(false);
await user.RefreshMetadata(new MetadataRefreshOptions(_fileSystem)
{
diff --git a/MediaBrowser.Server.Implementations/Connect/Responses.cs b/Emby.Server.Implementations/Connect/Responses.cs
index f865278294..87cb6cdf93 100644
--- a/MediaBrowser.Server.Implementations/Connect/Responses.cs
+++ b/Emby.Server.Implementations/Connect/Responses.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Connect;
-namespace MediaBrowser.Server.Implementations.Connect
+namespace Emby.Server.Implementations.Connect
{
public class ServerRegistrationResponse
{
diff --git a/MediaBrowser.Server.Implementations/Connect/Validator.cs b/Emby.Server.Implementations/Connect/Validator.cs
index 8cdfc4a6b3..5c94fa71c7 100644
--- a/MediaBrowser.Server.Implementations/Connect/Validator.cs
+++ b/Emby.Server.Implementations/Connect/Validator.cs
@@ -1,6 +1,6 @@
using System.Text.RegularExpressions;
-namespace MediaBrowser.Server.Implementations.Connect
+namespace Emby.Server.Implementations.Connect
{
public static class Validator
{
diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
new file mode 100644
index 0000000000..64a0d889eb
--- /dev/null
+++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
@@ -0,0 +1,401 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Logging;
+using SQLitePCL.pretty;
+using System.Linq;
+using SQLitePCL;
+
+namespace Emby.Server.Implementations.Data
+{
+ public abstract class BaseSqliteRepository : IDisposable
+ {
+ protected string DbFilePath { get; set; }
+ protected ReaderWriterLockSlim WriteLock;
+
+ protected ILogger Logger { get; private set; }
+
+ protected BaseSqliteRepository(ILogger logger)
+ {
+ Logger = logger;
+
+ WriteLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
+ }
+
+ protected TransactionMode TransactionMode
+ {
+ get { return TransactionMode.Deferred; }
+ }
+
+ protected TransactionMode ReadTransactionMode
+ {
+ get { return TransactionMode.Deferred; }
+ }
+
+ internal static int ThreadSafeMode { get; set; }
+
+ static BaseSqliteRepository()
+ {
+ SQLite3.EnableSharedCache = false;
+
+ int rc = raw.sqlite3_config(raw.SQLITE_CONFIG_MEMSTATUS, 0);
+ //CheckOk(rc);
+
+ rc = raw.sqlite3_config(raw.SQLITE_CONFIG_MULTITHREAD, 1);
+ //rc = raw.sqlite3_config(raw.SQLITE_CONFIG_SINGLETHREAD, 1);
+ //rc = raw.sqlite3_config(raw.SQLITE_CONFIG_SERIALIZED, 1);
+ //CheckOk(rc);
+
+ rc = raw.sqlite3_enable_shared_cache(1);
+
+ ThreadSafeMode = raw.sqlite3_threadsafe();
+ }
+
+ private static bool _versionLogged;
+
+ private string _defaultWal;
+ protected ManagedConnection _connection;
+
+ protected virtual bool EnableSingleConnection
+ {
+ get { return true; }
+ }
+
+ protected ManagedConnection CreateConnection(bool isReadOnly = false)
+ {
+ if (_connection != null)
+ {
+ return _connection;
+ }
+
+ lock (WriteLock)
+ {
+ if (!_versionLogged)
+ {
+ _versionLogged = true;
+ Logger.Info("Sqlite version: " + SQLite3.Version);
+ Logger.Info("Sqlite compiler options: " + string.Join(",", SQLite3.CompilerOptions.ToArray()));
+ }
+
+ ConnectionFlags connectionFlags;
+
+ if (isReadOnly)
+ {
+ //Logger.Info("Opening read connection");
+ //connectionFlags = ConnectionFlags.ReadOnly;
+ connectionFlags = ConnectionFlags.Create;
+ connectionFlags |= ConnectionFlags.ReadWrite;
+ }
+ else
+ {
+ //Logger.Info("Opening write connection");
+ connectionFlags = ConnectionFlags.Create;
+ connectionFlags |= ConnectionFlags.ReadWrite;
+ }
+
+ if (EnableSingleConnection)
+ {
+ connectionFlags |= ConnectionFlags.PrivateCache;
+ }
+ else
+ {
+ connectionFlags |= ConnectionFlags.SharedCached;
+ }
+
+ connectionFlags |= ConnectionFlags.NoMutex;
+
+ var db = SQLite3.Open(DbFilePath, connectionFlags, null);
+
+ if (string.IsNullOrWhiteSpace(_defaultWal))
+ {
+ _defaultWal = db.Query("PRAGMA journal_mode").SelectScalarString().First();
+
+ Logger.Info("Default journal_mode for {0} is {1}", DbFilePath, _defaultWal);
+ }
+
+ var queries = new List<string>
+ {
+ //"PRAGMA cache size=-10000"
+ //"PRAGMA read_uncommitted = true",
+ "PRAGMA synchronous=Normal"
+ };
+
+ if (CacheSize.HasValue)
+ {
+ queries.Add("PRAGMA cache_size=-" + CacheSize.Value.ToString(CultureInfo.InvariantCulture));
+ }
+
+ if (EnableTempStoreMemory)
+ {
+ queries.Add("PRAGMA temp_store = memory");
+ }
+
+ //var cacheSize = CacheSize;
+ //if (cacheSize.HasValue)
+ //{
+
+ //}
+
+ ////foreach (var query in queries)
+ ////{
+ //// db.Execute(query);
+ ////}
+
+ //Logger.Info("synchronous: " + db.Query("PRAGMA synchronous").SelectScalarString().First());
+ //Logger.Info("temp_store: " + db.Query("PRAGMA temp_store").SelectScalarString().First());
+
+ /*if (!string.Equals(_defaultWal, "wal", StringComparison.OrdinalIgnoreCase))
+ {
+ queries.Add("PRAGMA journal_mode=WAL");
+
+ using (WriteLock.Write())
+ {
+ db.ExecuteAll(string.Join(";", queries.ToArray()));
+ }
+ }
+ else*/
+ foreach (var query in queries)
+ {
+ db.Execute(query);
+ }
+
+ _connection = new ManagedConnection(db, false);
+
+ return _connection;
+ }
+ }
+
+ public IStatement PrepareStatement(ManagedConnection connection, string sql)
+ {
+ return connection.PrepareStatement(sql);
+ }
+
+ public IStatement PrepareStatementSafe(ManagedConnection connection, string sql)
+ {
+ return connection.PrepareStatement(sql);
+ }
+
+ public IStatement PrepareStatement(IDatabaseConnection connection, string sql)
+ {
+ return connection.PrepareStatement(sql);
+ }
+
+ public IStatement PrepareStatementSafe(IDatabaseConnection connection, string sql)
+ {
+ return connection.PrepareStatement(sql);
+ }
+
+ public List<IStatement> PrepareAll(IDatabaseConnection connection, IEnumerable<string> sql)
+ {
+ return PrepareAllSafe(connection, sql);
+ }
+
+ public List<IStatement> PrepareAllSafe(IDatabaseConnection connection, IEnumerable<string> sql)
+ {
+ return sql.Select(connection.PrepareStatement).ToList();
+ }
+
+ protected void RunDefaultInitialization(ManagedConnection db)
+ {
+ var queries = new List<string>
+ {
+ "PRAGMA journal_mode=WAL",
+ "PRAGMA page_size=4096",
+ "PRAGMA synchronous=Normal"
+ };
+
+ if (EnableTempStoreMemory)
+ {
+ queries.AddRange(new List<string>
+ {
+ "pragma default_temp_store = memory",
+ "pragma temp_store = memory"
+ });
+ }
+
+ db.ExecuteAll(string.Join(";", queries.ToArray()));
+ Logger.Info("PRAGMA synchronous=" + db.Query("PRAGMA synchronous").SelectScalarString().First());
+ }
+
+ protected virtual bool EnableTempStoreMemory
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ protected virtual int? CacheSize
+ {
+ get
+ {
+ return null;
+ }
+ }
+
+ internal static void CheckOk(int rc)
+ {
+ string msg = "";
+
+ if (raw.SQLITE_OK != rc)
+ {
+ throw CreateException((ErrorCode)rc, msg);
+ }
+ }
+
+ internal static Exception CreateException(ErrorCode rc, string msg)
+ {
+ var exp = new Exception(msg);
+
+ return exp;
+ }
+
+ private bool _disposed;
+ protected void CheckDisposed()
+ {
+ if (_disposed)
+ {
+ throw new ObjectDisposedException(GetType().Name + " has been disposed and cannot be accessed.");
+ }
+ }
+
+ public void Dispose()
+ {
+ _disposed = true;
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ private readonly object _disposeLock = new object();
+
+ /// <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)
+ {
+ try
+ {
+ lock (_disposeLock)
+ {
+ using (WriteLock.Write())
+ {
+ if (_connection != null)
+ {
+ using (_connection)
+ {
+
+ }
+ _connection = null;
+ }
+
+ CloseConnection();
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error disposing database", ex);
+ }
+ }
+ }
+
+ protected virtual void CloseConnection()
+ {
+
+ }
+
+ protected List<string> GetColumnNames(IDatabaseConnection connection, string table)
+ {
+ var list = new List<string>();
+
+ foreach (var row in connection.Query("PRAGMA table_info(" + table + ")"))
+ {
+ if (row[1].SQLiteType != SQLiteType.Null)
+ {
+ var name = row[1].ToString();
+
+ list.Add(name);
+ }
+ }
+
+ return list;
+ }
+
+ protected void AddColumn(IDatabaseConnection connection, string table, string columnName, string type, List<string> existingColumnNames)
+ {
+ if (existingColumnNames.Contains(columnName, StringComparer.OrdinalIgnoreCase))
+ {
+ return;
+ }
+
+ connection.Execute("alter table " + table + " add column " + columnName + " " + type + " NULL");
+ }
+ }
+
+ public static class ReaderWriterLockSlimExtensions
+ {
+ private sealed class ReadLockToken : IDisposable
+ {
+ private ReaderWriterLockSlim _sync;
+ public ReadLockToken(ReaderWriterLockSlim sync)
+ {
+ _sync = sync;
+ sync.EnterReadLock();
+ }
+ public void Dispose()
+ {
+ if (_sync != null)
+ {
+ _sync.ExitReadLock();
+ _sync = null;
+ }
+ }
+ }
+ private sealed class WriteLockToken : IDisposable
+ {
+ private ReaderWriterLockSlim _sync;
+ public WriteLockToken(ReaderWriterLockSlim sync)
+ {
+ _sync = sync;
+ sync.EnterWriteLock();
+ }
+ public void Dispose()
+ {
+ if (_sync != null)
+ {
+ _sync.ExitWriteLock();
+ _sync = null;
+ }
+ }
+ }
+
+ public class DummyToken : IDisposable
+ {
+ public void Dispose()
+ {
+ }
+ }
+
+ public static IDisposable Read(this ReaderWriterLockSlim obj)
+ {
+ //if (BaseSqliteRepository.ThreadSafeMode > 0)
+ //{
+ // return new DummyToken();
+ //}
+ return new WriteLockToken(obj);
+ }
+ public static IDisposable Write(this ReaderWriterLockSlim obj)
+ {
+ //if (BaseSqliteRepository.ThreadSafeMode > 0)
+ //{
+ // return new DummyToken();
+ //}
+ return new WriteLockToken(obj);
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
index c1394ee1c3..5bc3a625f1 100644
--- a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs
+++ b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
@@ -1,6 +1,4 @@
using MediaBrowser.Common.Progress;
-using MediaBrowser.Common.ScheduledTasks;
-using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
@@ -8,44 +6,29 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Localization;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Server.Implementations.ScheduledTasks;
+using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Server.Implementations.Persistence
+namespace Emby.Server.Implementations.Data
{
public class CleanDatabaseScheduledTask : IScheduledTask
{
private readonly ILibraryManager _libraryManager;
private readonly IItemRepository _itemRepo;
private readonly ILogger _logger;
- private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
- private readonly IHttpServer _httpServer;
- private readonly ILocalizationManager _localization;
- private readonly ITaskManager _taskManager;
- public const int MigrationVersion = 23;
- public static bool EnableUnavailableMessage = false;
-
- public CleanDatabaseScheduledTask(ILibraryManager libraryManager, IItemRepository itemRepo, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem, IHttpServer httpServer, ILocalizationManager localization, ITaskManager taskManager)
+ public CleanDatabaseScheduledTask(ILibraryManager libraryManager, IItemRepository itemRepo, ILogger logger, IFileSystem fileSystem)
{
_libraryManager = libraryManager;
_itemRepo = itemRepo;
_logger = logger;
- _config = config;
_fileSystem = fileSystem;
- _httpServer = httpServer;
- _localization = localization;
- _taskManager = taskManager;
}
public string Name
@@ -65,8 +48,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
{
- OnProgress(0);
-
// Ensure these objects are lazy loaded.
// Without this there is a deadlock that will need to be investigated
var rootChildren = _libraryManager.RootFolder.Children.ToList();
@@ -75,19 +56,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
var innerProgress = new ActionableProgress<double>();
innerProgress.RegisterAction(p =>
{
- double newPercentCommplete = .4 * p;
- OnProgress(newPercentCommplete);
-
- progress.Report(newPercentCommplete);
- });
-
- await UpdateToLatestSchema(cancellationToken, innerProgress).ConfigureAwait(false);
-
- innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(p =>
- {
- double newPercentCommplete = 40 + .05 * p;
- OnProgress(newPercentCommplete);
+ double newPercentCommplete = .45 * p;
progress.Report(newPercentCommplete);
});
await CleanDeadItems(cancellationToken, innerProgress).ConfigureAwait(false);
@@ -97,122 +66,12 @@ namespace MediaBrowser.Server.Implementations.Persistence
innerProgress.RegisterAction(p =>
{
double newPercentCommplete = 45 + .55 * p;
- OnProgress(newPercentCommplete);
progress.Report(newPercentCommplete);
});
await CleanDeletedItems(cancellationToken, innerProgress).ConfigureAwait(false);
progress.Report(100);
await _itemRepo.UpdateInheritedValues(cancellationToken).ConfigureAwait(false);
-
- if (_config.Configuration.MigrationVersion < MigrationVersion)
- {
- _config.Configuration.MigrationVersion = MigrationVersion;
- _config.SaveConfiguration();
- }
-
- if (_config.Configuration.SchemaVersion < SqliteItemRepository.LatestSchemaVersion)
- {
- _config.Configuration.SchemaVersion = SqliteItemRepository.LatestSchemaVersion;
- _config.SaveConfiguration();
- }
-
- if (EnableUnavailableMessage)
- {
- EnableUnavailableMessage = false;
- _httpServer.GlobalResponse = null;
- _taskManager.QueueScheduledTask<RefreshMediaLibraryTask>();
- }
-
- _taskManager.SuspendTriggers = false;
- }
-
- private void OnProgress(double newPercentCommplete)
- {
- if (EnableUnavailableMessage)
- {
- var html = "<!doctype html><html><head><title>Emby</title></head><body>";
- var text = _localization.GetLocalizedString("DbUpgradeMessage");
- html += string.Format(text, newPercentCommplete.ToString("N2", CultureInfo.InvariantCulture));
-
- html += "<script>setTimeout(function(){window.location.reload(true);}, 5000);</script>";
- html += "</body></html>";
-
- _httpServer.GlobalResponse = html;
- }
- }
-
- private async Task UpdateToLatestSchema(CancellationToken cancellationToken, IProgress<double> progress)
- {
- var itemIds = _libraryManager.GetItemIds(new InternalItemsQuery
- {
- IsCurrentSchema = false,
- ExcludeItemTypes = new[] { typeof(LiveTvProgram).Name }
- });
-
- var numComplete = 0;
- var numItems = itemIds.Count;
-
- _logger.Debug("Upgrading schema for {0} items", numItems);
-
- var list = new List<BaseItem>();
-
- foreach (var itemId in itemIds)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- if (itemId != Guid.Empty)
- {
- // Somehow some invalid data got into the db. It probably predates the boundary checking
- var item = _libraryManager.GetItemById(itemId);
-
- if (item != null)
- {
- list.Add(item);
- }
- }
-
- if (list.Count >= 1000)
- {
- try
- {
- await _itemRepo.SaveItems(list, cancellationToken).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- throw;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error saving item", ex);
- }
-
- list.Clear();
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= numItems;
- progress.Report(percent * 100);
- }
-
- if (list.Count > 0)
- {
- try
- {
- await _itemRepo.SaveItems(list, cancellationToken).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- throw;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error saving item", ex);
- }
- }
-
- progress.Report(100);
}
private async Task CleanDeadItems(CancellationToken cancellationToken, IProgress<double> progress)
@@ -276,9 +135,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
});
var numComplete = 0;
- var numItems = result.Items.Length;
+ var numItems = result.Count;
- foreach (var item in result.Items)
+ foreach (var item in result)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -337,12 +196,22 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
}
- public IEnumerable<ITaskTrigger> GetDefaultTriggers()
+ /// <summary>
+ /// Creates the triggers that define when the task will run
+ /// </summary>
+ /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
+ public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- return new ITaskTrigger[]
- {
- new IntervalTrigger{ Interval = TimeSpan.FromHours(24)}
+ return new[] {
+
+ // Every so often
+ new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
};
}
+
+ public string Key
+ {
+ get { return "CleanDatabase"; }
+ }
}
} \ No newline at end of file
diff --git a/Emby.Server.Implementations/Data/ManagedConnection.cs b/Emby.Server.Implementations/Data/ManagedConnection.cs
new file mode 100644
index 0000000000..91a2dfdf62
--- /dev/null
+++ b/Emby.Server.Implementations/Data/ManagedConnection.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using SQLitePCL.pretty;
+
+namespace Emby.Server.Implementations.Data
+{
+ public class ManagedConnection : IDisposable
+ {
+ private SQLiteDatabaseConnection db;
+ private readonly bool _closeOnDispose;
+
+ public ManagedConnection(SQLiteDatabaseConnection db, bool closeOnDispose)
+ {
+ this.db = db;
+ _closeOnDispose = closeOnDispose;
+ }
+
+ public IStatement PrepareStatement(string sql)
+ {
+ return db.PrepareStatement(sql);
+ }
+
+ public IEnumerable<IStatement> PrepareAll(string sql)
+ {
+ return db.PrepareAll(sql);
+ }
+
+ public void ExecuteAll(string sql)
+ {
+ db.ExecuteAll(sql);
+ }
+
+ public void Execute(string sql, params object[] values)
+ {
+ db.Execute(sql, values);
+ }
+
+ public void RunQueries(string[] sql)
+ {
+ db.RunQueries(sql);
+ }
+
+ public void RunInTransaction(Action<IDatabaseConnection> action, TransactionMode mode)
+ {
+ db.RunInTransaction(action, mode);
+ }
+
+ public T RunInTransaction<T>(Func<IDatabaseConnection, T> action, TransactionMode mode)
+ {
+ return db.RunInTransaction<T>(action, mode);
+ }
+
+ public IEnumerable<IReadOnlyList<IResultSetValue>> Query(string sql)
+ {
+ return db.Query(sql);
+ }
+
+ public IEnumerable<IReadOnlyList<IResultSetValue>> Query(string sql, params object[] values)
+ {
+ return db.Query(sql, values);
+ }
+
+ public void Close()
+ {
+ using (db)
+ {
+
+ }
+ }
+
+ public void Dispose()
+ {
+ if (_closeOnDispose)
+ {
+ Close();
+ }
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
new file mode 100644
index 0000000000..f3d84315e9
--- /dev/null
+++ b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
@@ -0,0 +1,239 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Serialization;
+using SQLitePCL.pretty;
+
+namespace Emby.Server.Implementations.Data
+{
+ /// <summary>
+ /// Class SQLiteDisplayPreferencesRepository
+ /// </summary>
+ public class SqliteDisplayPreferencesRepository : BaseSqliteRepository, IDisplayPreferencesRepository
+ {
+ private readonly IMemoryStreamFactory _memoryStreamProvider;
+
+ public SqliteDisplayPreferencesRepository(ILogger logger, IJsonSerializer jsonSerializer, IApplicationPaths appPaths, IMemoryStreamFactory memoryStreamProvider)
+ : base(logger)
+ {
+ _jsonSerializer = jsonSerializer;
+ _memoryStreamProvider = memoryStreamProvider;
+ DbFilePath = Path.Combine(appPaths.DataPath, "displaypreferences.db");
+ }
+
+ /// <summary>
+ /// Gets the name of the repository
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name
+ {
+ get
+ {
+ return "SQLite";
+ }
+ }
+
+ /// <summary>
+ /// The _json serializer
+ /// </summary>
+ private readonly IJsonSerializer _jsonSerializer;
+
+ /// <summary>
+ /// Opens the connection to the database
+ /// </summary>
+ /// <returns>Task.</returns>
+ public void Initialize()
+ {
+ using (var connection = CreateConnection())
+ {
+ RunDefaultInitialization(connection);
+
+ string[] queries = {
+
+ "create table if not exists userdisplaypreferences (id GUID, userId GUID, client text, data BLOB)",
+ "create unique index if not exists userdisplaypreferencesindex on userdisplaypreferences (id, userId, client)"
+ };
+
+ connection.RunQueries(queries);
+ }
+ }
+
+ /// <summary>
+ /// Save the display preferences associated with an item in the repo
+ /// </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>
+ /// <exception cref="System.ArgumentNullException">item</exception>
+ public async Task SaveDisplayPreferences(DisplayPreferences displayPreferences, Guid userId, string client, CancellationToken cancellationToken)
+ {
+ if (displayPreferences == null)
+ {
+ throw new ArgumentNullException("displayPreferences");
+ }
+ if (string.IsNullOrWhiteSpace(displayPreferences.Id))
+ {
+ throw new ArgumentNullException("displayPreferences.Id");
+ }
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ using (WriteLock.Write())
+ {
+ using (var connection = CreateConnection())
+ {
+ connection.RunInTransaction(db =>
+ {
+ SaveDisplayPreferences(displayPreferences, userId, client, db);
+ }, TransactionMode);
+ }
+ }
+ }
+
+ private void SaveDisplayPreferences(DisplayPreferences displayPreferences, Guid userId, string client, IDatabaseConnection connection)
+ {
+ var serialized = _jsonSerializer.SerializeToBytes(displayPreferences, _memoryStreamProvider);
+
+ using (var statement = connection.PrepareStatement("replace into userdisplaypreferences (id, userid, client, data) values (@id, @userId, @client, @data)"))
+ {
+ statement.TryBind("@id", displayPreferences.Id.ToGuidParamValue());
+ statement.TryBind("@userId", userId.ToGuidParamValue());
+ statement.TryBind("@client", client);
+ statement.TryBind("@data", serialized);
+
+ statement.MoveNext();
+ }
+ }
+
+ /// <summary>
+ /// Save all display preferences associated with a user in the repo
+ /// </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>
+ /// <exception cref="System.ArgumentNullException">item</exception>
+ public async Task SaveAllDisplayPreferences(IEnumerable<DisplayPreferences> displayPreferences, Guid userId, CancellationToken cancellationToken)
+ {
+ if (displayPreferences == null)
+ {
+ throw new ArgumentNullException("displayPreferences");
+ }
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ using (WriteLock.Write())
+ {
+ using (var connection = CreateConnection())
+ {
+ connection.RunInTransaction(db =>
+ {
+ foreach (var displayPreference in displayPreferences)
+ {
+ SaveDisplayPreferences(displayPreference, userId, displayPreference.Client, db);
+ }
+ }, TransactionMode);
+ }
+ }
+ }
+
+ /// <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>
+ /// <exception cref="System.ArgumentNullException">item</exception>
+ public DisplayPreferences GetDisplayPreferences(string displayPreferencesId, Guid userId, string client)
+ {
+ if (string.IsNullOrWhiteSpace(displayPreferencesId))
+ {
+ throw new ArgumentNullException("displayPreferencesId");
+ }
+
+ var guidId = displayPreferencesId.GetMD5();
+
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
+ {
+ using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where id = @id and userId=@userId and client=@client"))
+ {
+ statement.TryBind("@id", guidId.ToGuidParamValue());
+ statement.TryBind("@userId", userId.ToGuidParamValue());
+ statement.TryBind("@client", client);
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ return Get(row);
+ }
+ }
+
+ return new DisplayPreferences
+ {
+ Id = guidId.ToString("N")
+ };
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets all display preferences for the given user.
+ /// </summary>
+ /// <param name="userId">The user id.</param>
+ /// <returns>Task{DisplayPreferences}.</returns>
+ /// <exception cref="System.ArgumentNullException">item</exception>
+ public IEnumerable<DisplayPreferences> GetAllDisplayPreferences(Guid userId)
+ {
+ var list = new List<DisplayPreferences>();
+
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
+ {
+ using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where userId=@userId"))
+ {
+ statement.TryBind("@userId", userId.ToGuidParamValue());
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ list.Add(Get(row));
+ }
+ }
+ }
+ }
+
+ return list;
+ }
+
+ private DisplayPreferences Get(IReadOnlyList<IResultSetValue> row)
+ {
+ using (var stream = _memoryStreamProvider.CreateNew(row[0].ToBlob()))
+ {
+ stream.Position = 0;
+ return _jsonSerializer.DeserializeFromStream<DisplayPreferences>(stream);
+ }
+ }
+
+ public Task SaveDisplayPreferences(DisplayPreferences displayPreferences, string userId, string client, CancellationToken cancellationToken)
+ {
+ return SaveDisplayPreferences(displayPreferences, new Guid(userId), client, cancellationToken);
+ }
+
+ public DisplayPreferences GetDisplayPreferences(string displayPreferencesId, string userId, string client)
+ {
+ return GetDisplayPreferences(displayPreferencesId, new Guid(userId), client);
+ }
+ }
+} \ No newline at end of file
diff --git a/Emby.Server.Implementations/Data/SqliteExtensions.cs b/Emby.Server.Implementations/Data/SqliteExtensions.cs
new file mode 100644
index 0000000000..d6ad0ba8ab
--- /dev/null
+++ b/Emby.Server.Implementations/Data/SqliteExtensions.cs
@@ -0,0 +1,394 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Serialization;
+using SQLitePCL.pretty;
+
+namespace Emby.Server.Implementations.Data
+{
+ public static class SqliteExtensions
+ {
+ public static void RunQueries(this SQLiteDatabaseConnection connection, string[] queries)
+ {
+ if (queries == null)
+ {
+ throw new ArgumentNullException("queries");
+ }
+
+ connection.RunInTransaction(conn =>
+ {
+ //foreach (var query in queries)
+ //{
+ // conn.Execute(query);
+ //}
+ conn.ExecuteAll(string.Join(";", queries));
+ });
+ }
+
+ public static byte[] ToGuidParamValue(this string str)
+ {
+ return ToGuidParamValue(new Guid(str));
+ }
+
+ public static byte[] ToGuidParamValue(this Guid guid)
+ {
+ return guid.ToByteArray();
+ }
+
+ public static Guid ReadGuid(this IResultSetValue result)
+ {
+ return new Guid(result.ToBlob());
+ }
+
+ public static string ToDateTimeParamValue(this DateTime dateValue)
+ {
+ var kind = DateTimeKind.Utc;
+
+ return (dateValue.Kind == DateTimeKind.Unspecified)
+ ? DateTime.SpecifyKind(dateValue, kind).ToString(
+ GetDateTimeKindFormat(kind),
+ CultureInfo.InvariantCulture)
+ : dateValue.ToString(
+ GetDateTimeKindFormat(dateValue.Kind),
+ CultureInfo.InvariantCulture);
+ }
+
+ private static string GetDateTimeKindFormat(
+ DateTimeKind kind)
+ {
+ return (kind == DateTimeKind.Utc) ? _datetimeFormatUtc : _datetimeFormatLocal;
+ }
+
+ /// <summary>
+ /// An array of ISO-8601 DateTime formats that we support parsing.
+ /// </summary>
+ private static string[] _datetimeFormats = new string[] {
+ "THHmmssK",
+ "THHmmK",
+ "HH:mm:ss.FFFFFFFK",
+ "HH:mm:ssK",
+ "HH:mmK",
+ "yyyy-MM-dd HH:mm:ss.FFFFFFFK", /* NOTE: UTC default (5). */
+ "yyyy-MM-dd HH:mm:ssK",
+ "yyyy-MM-dd HH:mmK",
+ "yyyy-MM-ddTHH:mm:ss.FFFFFFFK",
+ "yyyy-MM-ddTHH:mmK",
+ "yyyy-MM-ddTHH:mm:ssK",
+ "yyyyMMddHHmmssK",
+ "yyyyMMddHHmmK",
+ "yyyyMMddTHHmmssFFFFFFFK",
+ "THHmmss",
+ "THHmm",
+ "HH:mm:ss.FFFFFFF",
+ "HH:mm:ss",
+ "HH:mm",
+ "yyyy-MM-dd HH:mm:ss.FFFFFFF", /* NOTE: Non-UTC default (19). */
+ "yyyy-MM-dd HH:mm:ss",
+ "yyyy-MM-dd HH:mm",
+ "yyyy-MM-ddTHH:mm:ss.FFFFFFF",
+ "yyyy-MM-ddTHH:mm",
+ "yyyy-MM-ddTHH:mm:ss",
+ "yyyyMMddHHmmss",
+ "yyyyMMddHHmm",
+ "yyyyMMddTHHmmssFFFFFFF",
+ "yyyy-MM-dd",
+ "yyyyMMdd",
+ "yy-MM-dd"
+ };
+
+ private static string _datetimeFormatUtc = _datetimeFormats[5];
+ private static string _datetimeFormatLocal = _datetimeFormats[19];
+
+ public static DateTime ReadDateTime(this IResultSetValue result)
+ {
+ var dateText = result.ToString();
+
+ return DateTime.ParseExact(
+ dateText, _datetimeFormats,
+ DateTimeFormatInfo.InvariantInfo,
+ DateTimeStyles.None).ToUniversalTime();
+ }
+
+ /// <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)
+ {
+ if (obj == null)
+ {
+ throw new ArgumentNullException("obj");
+ }
+
+ using (var stream = streamProvider.CreateNew())
+ {
+ json.SerializeToStream(obj, stream);
+ return stream.ToArray();
+ }
+ }
+
+ public static void Attach(ManagedConnection db, string path, string alias)
+ {
+ var commandText = string.Format("attach @path as {0};", alias);
+
+ using (var statement = db.PrepareStatement(commandText))
+ {
+ statement.TryBind("@path", path);
+ statement.MoveNext();
+ }
+ }
+
+ public static bool IsDBNull(this IReadOnlyList<IResultSetValue> result, int index)
+ {
+ return result[index].SQLiteType == SQLiteType.Null;
+ }
+
+ public static string GetString(this IReadOnlyList<IResultSetValue> result, int index)
+ {
+ return result[index].ToString();
+ }
+
+ public static bool GetBoolean(this IReadOnlyList<IResultSetValue> result, int index)
+ {
+ return result[index].ToBool();
+ }
+
+ public static int GetInt32(this IReadOnlyList<IResultSetValue> result, int index)
+ {
+ return result[index].ToInt();
+ }
+
+ public static long GetInt64(this IReadOnlyList<IResultSetValue> result, int index)
+ {
+ return result[index].ToInt64();
+ }
+
+ public static float GetFloat(this IReadOnlyList<IResultSetValue> result, int index)
+ {
+ return result[index].ToFloat();
+ }
+
+ public static Guid GetGuid(this IReadOnlyList<IResultSetValue> result, int index)
+ {
+ return result[index].ReadGuid();
+ }
+
+ private static void CheckName(string name)
+ {
+#if DEBUG
+ //if (!name.IndexOf("@", StringComparison.OrdinalIgnoreCase) != 0)
+ {
+ throw new Exception("Invalid param name: " + name);
+ }
+#endif
+ }
+
+ public static void TryBind(this IStatement statement, string name, double value)
+ {
+ IBindParameter bindParam;
+ if (statement.BindParameters.TryGetValue(name, out bindParam))
+ {
+ bindParam.Bind(value);
+ }
+ else
+ {
+ CheckName(name);
+ }
+ }
+
+ public static void TryBind(this IStatement statement, string name, string value)
+ {
+ IBindParameter bindParam;
+ if (statement.BindParameters.TryGetValue(name, out bindParam))
+ {
+ if (value == null)
+ {
+ bindParam.BindNull();
+ }
+ else
+ {
+ bindParam.Bind(value);
+ }
+ }
+ else
+ {
+ CheckName(name);
+ }
+ }
+
+ public static void TryBind(this IStatement statement, string name, bool value)
+ {
+ IBindParameter bindParam;
+ if (statement.BindParameters.TryGetValue(name, out bindParam))
+ {
+ bindParam.Bind(value);
+ }
+ else
+ {
+ CheckName(name);
+ }
+ }
+
+ public static void TryBind(this IStatement statement, string name, float value)
+ {
+ IBindParameter bindParam;
+ if (statement.BindParameters.TryGetValue(name, out bindParam))
+ {
+ bindParam.Bind(value);
+ }
+ else
+ {
+ CheckName(name);
+ }
+ }
+
+ public static void TryBind(this IStatement statement, string name, int value)
+ {
+ IBindParameter bindParam;
+ if (statement.BindParameters.TryGetValue(name, out bindParam))
+ {
+ bindParam.Bind(value);
+ }
+ else
+ {
+ CheckName(name);
+ }
+ }
+
+ public static void TryBind(this IStatement statement, string name, Guid value)
+ {
+ IBindParameter bindParam;
+ if (statement.BindParameters.TryGetValue(name, out bindParam))
+ {
+ bindParam.Bind(value.ToGuidParamValue());
+ }
+ else
+ {
+ CheckName(name);
+ }
+ }
+
+ public static void TryBind(this IStatement statement, string name, DateTime value)
+ {
+ IBindParameter bindParam;
+ if (statement.BindParameters.TryGetValue(name, out bindParam))
+ {
+ bindParam.Bind(value.ToDateTimeParamValue());
+ }
+ else
+ {
+ CheckName(name);
+ }
+ }
+
+ public static void TryBind(this IStatement statement, string name, long value)
+ {
+ IBindParameter bindParam;
+ if (statement.BindParameters.TryGetValue(name, out bindParam))
+ {
+ bindParam.Bind(value);
+ }
+ else
+ {
+ CheckName(name);
+ }
+ }
+
+ public static void TryBind(this IStatement statement, string name, byte[] value)
+ {
+ IBindParameter bindParam;
+ if (statement.BindParameters.TryGetValue(name, out bindParam))
+ {
+ bindParam.Bind(value);
+ }
+ else
+ {
+ CheckName(name);
+ }
+ }
+
+ public static void TryBindNull(this IStatement statement, string name)
+ {
+ IBindParameter bindParam;
+ if (statement.BindParameters.TryGetValue(name, out bindParam))
+ {
+ bindParam.BindNull();
+ }
+ else
+ {
+ CheckName(name);
+ }
+ }
+
+ public static void TryBind(this IStatement statement, string name, DateTime? value)
+ {
+ if (value.HasValue)
+ {
+ TryBind(statement, name, value.Value);
+ }
+ else
+ {
+ TryBindNull(statement, name);
+ }
+ }
+
+ public static void TryBind(this IStatement statement, string name, Guid? value)
+ {
+ if (value.HasValue)
+ {
+ TryBind(statement, name, value.Value);
+ }
+ else
+ {
+ TryBindNull(statement, name);
+ }
+ }
+
+ public static void TryBind(this IStatement statement, string name, int? value)
+ {
+ if (value.HasValue)
+ {
+ TryBind(statement, name, value.Value);
+ }
+ else
+ {
+ TryBindNull(statement, name);
+ }
+ }
+
+ public static void TryBind(this IStatement statement, string name, float? value)
+ {
+ if (value.HasValue)
+ {
+ TryBind(statement, name, value.Value);
+ }
+ else
+ {
+ TryBindNull(statement, name);
+ }
+ }
+
+ public static void TryBind(this IStatement statement, string name, bool? value)
+ {
+ if (value.HasValue)
+ {
+ TryBind(statement, name, value.Value);
+ }
+ else
+ {
+ TryBindNull(statement, name);
+ }
+ }
+
+ public static IEnumerable<IReadOnlyList<IResultSetValue>> ExecuteQuery(
+ this IStatement This)
+ {
+ while (This.MoveNext())
+ {
+ yield return This.Current;
+ }
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Data/SqliteFileOrganizationRepository.cs b/Emby.Server.Implementations/Data/SqliteFileOrganizationRepository.cs
new file mode 100644
index 0000000000..9fbe8669d1
--- /dev/null
+++ b/Emby.Server.Implementations/Data/SqliteFileOrganizationRepository.cs
@@ -0,0 +1,284 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Model.FileOrganization;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Querying;
+using SQLitePCL.pretty;
+
+namespace Emby.Server.Implementations.Data
+{
+ public class SqliteFileOrganizationRepository : BaseSqliteRepository, IFileOrganizationRepository, IDisposable
+ {
+ private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+
+ public SqliteFileOrganizationRepository(ILogger logger, IServerApplicationPaths appPaths) : base(logger)
+ {
+ DbFilePath = Path.Combine(appPaths.DataPath, "fileorganization.db");
+ }
+
+ /// <summary>
+ /// Opens the connection to the database
+ /// </summary>
+ /// <returns>Task.</returns>
+ public void Initialize()
+ {
+ using (var connection = CreateConnection())
+ {
+ RunDefaultInitialization(connection);
+
+ string[] queries = {
+
+ "create table if not exists FileOrganizerResults (ResultId GUID PRIMARY KEY, OriginalPath TEXT, TargetPath TEXT, FileLength INT, OrganizationDate datetime, Status TEXT, OrganizationType TEXT, StatusMessage TEXT, ExtractedName TEXT, ExtractedYear int null, ExtractedSeasonNumber int null, ExtractedEpisodeNumber int null, ExtractedEndingEpisodeNumber, DuplicatePaths TEXT int null)",
+ "create index if not exists idx_FileOrganizerResults on FileOrganizerResults(ResultId)"
+ };
+
+ connection.RunQueries(queries);
+ }
+ }
+
+ public async Task SaveResult(FileOrganizationResult result, CancellationToken cancellationToken)
+ {
+ if (result == null)
+ {
+ throw new ArgumentNullException("result");
+ }
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ using (WriteLock.Write())
+ {
+ using (var connection = CreateConnection())
+ {
+ connection.RunInTransaction(db =>
+ {
+ var commandText = "replace into FileOrganizerResults (ResultId, OriginalPath, TargetPath, FileLength, OrganizationDate, Status, OrganizationType, StatusMessage, ExtractedName, ExtractedYear, ExtractedSeasonNumber, ExtractedEpisodeNumber, ExtractedEndingEpisodeNumber, DuplicatePaths) values (@ResultId, @OriginalPath, @TargetPath, @FileLength, @OrganizationDate, @Status, @OrganizationType, @StatusMessage, @ExtractedName, @ExtractedYear, @ExtractedSeasonNumber, @ExtractedEpisodeNumber, @ExtractedEndingEpisodeNumber, @DuplicatePaths)";
+
+ using (var statement = db.PrepareStatement(commandText))
+ {
+ statement.TryBind("@ResultId", result.Id.ToGuidParamValue());
+ statement.TryBind("@OriginalPath", result.OriginalPath);
+
+ statement.TryBind("@TargetPath", result.TargetPath);
+ statement.TryBind("@FileLength", result.FileSize);
+ statement.TryBind("@OrganizationDate", result.Date.ToDateTimeParamValue());
+ statement.TryBind("@Status", result.Status.ToString());
+ statement.TryBind("@OrganizationType", result.Type.ToString());
+ statement.TryBind("@StatusMessage", result.StatusMessage);
+ statement.TryBind("@ExtractedName", result.ExtractedName);
+ statement.TryBind("@ExtractedYear", result.ExtractedYear);
+ statement.TryBind("@ExtractedSeasonNumber", result.ExtractedSeasonNumber);
+ statement.TryBind("@ExtractedEpisodeNumber", result.ExtractedEpisodeNumber);
+ statement.TryBind("@ExtractedEndingEpisodeNumber", result.ExtractedEndingEpisodeNumber);
+ statement.TryBind("@DuplicatePaths", string.Join("|", result.DuplicatePaths.ToArray()));
+
+ statement.MoveNext();
+ }
+ }, TransactionMode);
+ }
+ }
+ }
+
+ public async Task Delete(string id)
+ {
+ if (string.IsNullOrEmpty(id))
+ {
+ throw new ArgumentNullException("id");
+ }
+
+ using (WriteLock.Write())
+ {
+ using (var connection = CreateConnection())
+ {
+ connection.RunInTransaction(db =>
+ {
+ using (var statement = db.PrepareStatement("delete from FileOrganizerResults where ResultId = @ResultId"))
+ {
+ statement.TryBind("@ResultId", id.ToGuidParamValue());
+ statement.MoveNext();
+ }
+ }, TransactionMode);
+ }
+ }
+ }
+
+ public async Task DeleteAll()
+ {
+ using (WriteLock.Write())
+ {
+ using (var connection = CreateConnection())
+ {
+ connection.RunInTransaction(db =>
+ {
+ var commandText = "delete from FileOrganizerResults";
+
+ db.Execute(commandText);
+ }, TransactionMode);
+ }
+ }
+ }
+
+ public QueryResult<FileOrganizationResult> GetResults(FileOrganizationResultQuery query)
+ {
+ if (query == null)
+ {
+ throw new ArgumentNullException("query");
+ }
+
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
+ {
+ var commandText = "SELECT ResultId, OriginalPath, TargetPath, FileLength, OrganizationDate, Status, OrganizationType, StatusMessage, ExtractedName, ExtractedYear, ExtractedSeasonNumber, ExtractedEpisodeNumber, ExtractedEndingEpisodeNumber, DuplicatePaths from FileOrganizerResults";
+
+ if (query.StartIndex.HasValue && query.StartIndex.Value > 0)
+ {
+ commandText += string.Format(" WHERE ResultId NOT IN (SELECT ResultId FROM FileOrganizerResults ORDER BY OrganizationDate desc LIMIT {0})",
+ query.StartIndex.Value.ToString(_usCulture));
+ }
+
+ commandText += " ORDER BY OrganizationDate desc";
+
+ if (query.Limit.HasValue)
+ {
+ commandText += " LIMIT " + query.Limit.Value.ToString(_usCulture);
+ }
+
+ var list = new List<FileOrganizationResult>();
+
+ using (var statement = connection.PrepareStatement(commandText))
+ {
+ foreach (var row in statement.ExecuteQuery())
+ {
+ list.Add(GetResult(row));
+ }
+ }
+
+ int count;
+ using (var statement = connection.PrepareStatement("select count (ResultId) from FileOrganizerResults"))
+ {
+ count = statement.ExecuteQuery().SelectScalarInt().First();
+ }
+
+ return new QueryResult<FileOrganizationResult>()
+ {
+ Items = list.ToArray(),
+ TotalRecordCount = count
+ };
+ }
+ }
+ }
+
+ public FileOrganizationResult GetResult(string id)
+ {
+ if (string.IsNullOrEmpty(id))
+ {
+ throw new ArgumentNullException("id");
+ }
+
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
+ {
+ using (var statement = connection.PrepareStatement("select ResultId, OriginalPath, TargetPath, FileLength, OrganizationDate, Status, OrganizationType, StatusMessage, ExtractedName, ExtractedYear, ExtractedSeasonNumber, ExtractedEpisodeNumber, ExtractedEndingEpisodeNumber, DuplicatePaths from FileOrganizerResults where ResultId=@ResultId"))
+ {
+ statement.TryBind("@ResultId", id.ToGuidParamValue());
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ return GetResult(row);
+ }
+ }
+
+ return null;
+ }
+ }
+ }
+
+ public FileOrganizationResult GetResult(IReadOnlyList<IResultSetValue> reader)
+ {
+ var index = 0;
+
+ var result = new FileOrganizationResult
+ {
+ Id = reader[0].ReadGuid().ToString("N")
+ };
+
+ index++;
+ if (reader[index].SQLiteType != SQLiteType.Null)
+ {
+ result.OriginalPath = reader[index].ToString();
+ }
+
+ index++;
+ if (reader[index].SQLiteType != SQLiteType.Null)
+ {
+ result.TargetPath = reader[index].ToString();
+ }
+
+ index++;
+ result.FileSize = reader[index].ToInt64();
+
+ index++;
+ result.Date = reader[index].ReadDateTime();
+
+ index++;
+ result.Status = (FileSortingStatus)Enum.Parse(typeof(FileSortingStatus), reader[index].ToString(), true);
+
+ index++;
+ result.Type = (FileOrganizerType)Enum.Parse(typeof(FileOrganizerType), reader[index].ToString(), true);
+
+ index++;
+ if (reader[index].SQLiteType != SQLiteType.Null)
+ {
+ result.StatusMessage = reader[index].ToString();
+ }
+
+ result.OriginalFileName = Path.GetFileName(result.OriginalPath);
+
+ index++;
+ if (reader[index].SQLiteType != SQLiteType.Null)
+ {
+ result.ExtractedName = reader[index].ToString();
+ }
+
+ index++;
+ if (reader[index].SQLiteType != SQLiteType.Null)
+ {
+ result.ExtractedYear = reader[index].ToInt();
+ }
+
+ index++;
+ if (reader[index].SQLiteType != SQLiteType.Null)
+ {
+ result.ExtractedSeasonNumber = reader[index].ToInt();
+ }
+
+ index++;
+ if (reader[index].SQLiteType != SQLiteType.Null)
+ {
+ result.ExtractedEpisodeNumber = reader[index].ToInt();
+ }
+
+ index++;
+ if (reader[index].SQLiteType != SQLiteType.Null)
+ {
+ result.ExtractedEndingEpisodeNumber = reader[index].ToInt();
+ }
+
+ index++;
+ if (reader[index].SQLiteType != SQLiteType.Null)
+ {
+ result.DuplicatePaths = reader[index].ToString().Split('|').Where(i => !string.IsNullOrEmpty(i)).ToList();
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index f33b18389a..1517029050 100644
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -1,16 +1,5 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Serialization;
using System;
using System.Collections.Generic;
-using System.Data;
using System.Globalization;
using System.IO;
using System.Linq;
@@ -18,27 +7,39 @@ using System.Runtime.Serialization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Collections;
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.Extensions;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Serialization;
using MediaBrowser.Server.Implementations.Devices;
using MediaBrowser.Server.Implementations.Playlists;
+using MediaBrowser.Model.Reflection;
+using SQLitePCL.pretty;
+using MediaBrowser.Model.System;
+using MediaBrowser.Model.Threading;
-namespace MediaBrowser.Server.Implementations.Persistence
+namespace Emby.Server.Implementations.Data
{
/// <summary>
/// Class SQLiteItemRepository
/// </summary>
public class SqliteItemRepository : BaseSqliteRepository, IItemRepository
{
- private IDbConnection _connection;
-
- private readonly TypeMapper _typeMapper = new TypeMapper();
+ private readonly TypeMapper _typeMapper;
/// <summary>
/// Gets the name of the repository
@@ -63,49 +64,19 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// </summary>
private readonly IServerConfigurationManager _config;
- /// <summary>
- /// The _save item command
- /// </summary>
- private IDbCommand _saveItemCommand;
-
private readonly string _criticReviewsPath;
- private IDbCommand _deleteItemCommand;
-
- private IDbCommand _deletePeopleCommand;
- private IDbCommand _savePersonCommand;
-
- private IDbCommand _deleteChaptersCommand;
- private IDbCommand _saveChapterCommand;
-
- private IDbCommand _deleteStreamsCommand;
- private IDbCommand _saveStreamCommand;
-
- private IDbCommand _deleteAncestorsCommand;
- private IDbCommand _saveAncestorCommand;
-
- private IDbCommand _deleteUserDataKeysCommand;
- private IDbCommand _saveUserDataKeysCommand;
-
- private IDbCommand _deleteItemValuesCommand;
- private IDbCommand _saveItemValuesCommand;
-
- private IDbCommand _deleteProviderIdsCommand;
- private IDbCommand _saveProviderIdsCommand;
-
- private IDbCommand _deleteImagesCommand;
- private IDbCommand _saveImagesCommand;
-
- private IDbCommand _updateInheritedTagsCommand;
-
- public const int LatestSchemaVersion = 109;
- private readonly IMemoryStreamProvider _memoryStreamProvider;
+ private readonly IMemoryStreamFactory _memoryStreamProvider;
+ private readonly IFileSystem _fileSystem;
+ private readonly IEnvironmentInfo _environmentInfo;
+ private readonly ITimerFactory _timerFactory;
+ private ITimer _shrinkMemoryTimer;
/// <summary>
/// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
/// </summary>
- public SqliteItemRepository(IServerConfigurationManager config, IJsonSerializer jsonSerializer, ILogManager logManager, IDbConnector connector, IMemoryStreamProvider memoryStreamProvider)
- : base(logManager, connector)
+ public SqliteItemRepository(IServerConfigurationManager config, IJsonSerializer jsonSerializer, ILogger logger, IMemoryStreamFactory memoryStreamProvider, IAssemblyInfo assemblyInfo, IFileSystem fileSystem, IEnvironmentInfo environmentInfo, ITimerFactory timerFactory)
+ : base(logger)
{
if (config == null)
{
@@ -119,6 +90,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
_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");
@@ -126,25 +101,31 @@ namespace MediaBrowser.Server.Implementations.Persistence
private const string ChaptersTableName = "Chapters2";
- protected override async Task<IDbConnection> CreateConnection(bool isReadOnly = false)
+ protected override int? CacheSize
{
- var cacheSize = _config.Configuration.SqliteCacheSize;
- if (cacheSize <= 0)
+ get
{
- cacheSize = Math.Min(Environment.ProcessorCount * 50000, 100000);
+ return 20000;
}
+ }
- var connection = await DbConnector.Connect(DbFilePath, false, false, 0 - cacheSize).ConfigureAwait(false);
-
- connection.RunQueries(new[]
+ protected override bool EnableTempStoreMemory
+ {
+ get
{
- "pragma temp_store = memory",
- "pragma default_temp_store = memory",
- "PRAGMA locking_mode=EXCLUSIVE"
+ return true;
+ }
+ }
- }, Logger);
+ protected override void CloseConnection()
+ {
+ base.CloseConnection();
- return connection;
+ if (_shrinkMemoryTimer != null)
+ {
+ _shrinkMemoryTimer.Dispose();
+ _shrinkMemoryTimer = null;
+ }
}
/// <summary>
@@ -153,30 +134,24 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// <returns>Task.</returns>
public async Task Initialize(SqliteUserDataRepository userDataRepo)
{
- _connection = await CreateConnection(false).ConfigureAwait(false);
+ 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))";
+ 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))";
- string[] queries = {
+ string[] queries = {
+ "PRAGMA locking_mode=NORMAL",
- "create table if not exists TypedBaseItems (guid GUID primary key, type TEXT, data BLOB, ParentId GUID, Path TEXT)",
+ "create table if not exists TypedBaseItems (guid GUID primary key NOT NULL, type TEXT NOT NULL, data BLOB NULL, ParentId GUID NULL, Path TEXT NULL)",
"create table if not exists AncestorIds (ItemId GUID, AncestorId GUID, AncestorIdText TEXT, PRIMARY KEY (ItemId, AncestorId))",
"create index if not exists idx_AncestorIds1 on AncestorIds(AncestorId)",
"create index if not exists idx_AncestorIds2 on AncestorIds(AncestorIdText)",
- "create table if not exists UserDataKeys (ItemId GUID, UserDataKey TEXT Priority INT, PRIMARY KEY (ItemId, UserDataKey))",
-
"create table if not exists ItemValues (ItemId GUID, Type INT, Value TEXT, CleanValue TEXT)",
- "create table if not exists ProviderIds (ItemId GUID, Name TEXT, Value TEXT, PRIMARY KEY (ItemId, Name))",
- // covering index
- "create index if not exists Idx_ProviderIds1 on ProviderIds(ItemId,Name,Value)",
-
- "create table if not exists Images (ItemId GUID NOT NULL, Path TEXT NOT NULL, ImageType INT NOT NULL, DateModified DATETIME, IsPlaceHolder BIT NOT NULL, SortOrder INT)",
- "create index if not exists idx_Images on Images(ItemId)",
-
"create table if not exists People (ItemId GUID, Name TEXT NOT NULL, Role TEXT, PersonType TEXT, SortOrder int, ListOrder int)",
"drop index if exists idxPeopleItemId",
@@ -189,111 +164,138 @@ namespace MediaBrowser.Server.Implementations.Persistence
"create index if not exists idx_mediastreams1 on mediastreams(ItemId)",
- };
+ "pragma shrink_memory"
- _connection.RunQueries(queries, Logger);
-
- _connection.AddColumn(Logger, "AncestorIds", "AncestorIdText", "Text");
-
- _connection.AddColumn(Logger, "TypedBaseItems", "Path", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "StartDate", "DATETIME");
- _connection.AddColumn(Logger, "TypedBaseItems", "EndDate", "DATETIME");
- _connection.AddColumn(Logger, "TypedBaseItems", "ChannelId", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "IsMovie", "BIT");
- _connection.AddColumn(Logger, "TypedBaseItems", "IsSports", "BIT");
- _connection.AddColumn(Logger, "TypedBaseItems", "IsKids", "BIT");
- _connection.AddColumn(Logger, "TypedBaseItems", "CommunityRating", "Float");
- _connection.AddColumn(Logger, "TypedBaseItems", "CustomRating", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "IndexNumber", "INT");
- _connection.AddColumn(Logger, "TypedBaseItems", "IsLocked", "BIT");
- _connection.AddColumn(Logger, "TypedBaseItems", "Name", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "OfficialRating", "Text");
-
- _connection.AddColumn(Logger, "TypedBaseItems", "MediaType", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "Overview", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "ParentIndexNumber", "INT");
- _connection.AddColumn(Logger, "TypedBaseItems", "PremiereDate", "DATETIME");
- _connection.AddColumn(Logger, "TypedBaseItems", "ProductionYear", "INT");
- _connection.AddColumn(Logger, "TypedBaseItems", "ParentId", "GUID");
- _connection.AddColumn(Logger, "TypedBaseItems", "Genres", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "SchemaVersion", "INT");
- _connection.AddColumn(Logger, "TypedBaseItems", "SortName", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "RunTimeTicks", "BIGINT");
-
- _connection.AddColumn(Logger, "TypedBaseItems", "OfficialRatingDescription", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "HomePageUrl", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "VoteCount", "INT");
- _connection.AddColumn(Logger, "TypedBaseItems", "DisplayMediaType", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "DateCreated", "DATETIME");
- _connection.AddColumn(Logger, "TypedBaseItems", "DateModified", "DATETIME");
-
- _connection.AddColumn(Logger, "TypedBaseItems", "ForcedSortName", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "IsOffline", "BIT");
- _connection.AddColumn(Logger, "TypedBaseItems", "LocationType", "Text");
-
- _connection.AddColumn(Logger, "TypedBaseItems", "IsSeries", "BIT");
- _connection.AddColumn(Logger, "TypedBaseItems", "IsLive", "BIT");
- _connection.AddColumn(Logger, "TypedBaseItems", "IsNews", "BIT");
- _connection.AddColumn(Logger, "TypedBaseItems", "IsPremiere", "BIT");
-
- _connection.AddColumn(Logger, "TypedBaseItems", "EpisodeTitle", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "IsRepeat", "BIT");
-
- _connection.AddColumn(Logger, "TypedBaseItems", "PreferredMetadataLanguage", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "PreferredMetadataCountryCode", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "IsHD", "BIT");
- _connection.AddColumn(Logger, "TypedBaseItems", "ExternalEtag", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "DateLastRefreshed", "DATETIME");
-
- _connection.AddColumn(Logger, "TypedBaseItems", "DateLastSaved", "DATETIME");
- _connection.AddColumn(Logger, "TypedBaseItems", "IsInMixedFolder", "BIT");
- _connection.AddColumn(Logger, "TypedBaseItems", "LockedFields", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "Studios", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "Audio", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "ExternalServiceId", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "Tags", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "IsFolder", "BIT");
- _connection.AddColumn(Logger, "TypedBaseItems", "InheritedParentalRatingValue", "INT");
- _connection.AddColumn(Logger, "TypedBaseItems", "UnratedType", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "TopParentId", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "IsItemByName", "BIT");
- _connection.AddColumn(Logger, "TypedBaseItems", "SourceType", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "TrailerTypes", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "CriticRating", "Float");
- _connection.AddColumn(Logger, "TypedBaseItems", "CriticRatingSummary", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "InheritedTags", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "CleanName", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "PresentationUniqueKey", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "SlugName", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "OriginalTitle", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "PrimaryVersionId", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "DateLastMediaAdded", "DATETIME");
- _connection.AddColumn(Logger, "TypedBaseItems", "Album", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "IsVirtualItem", "BIT");
- _connection.AddColumn(Logger, "TypedBaseItems", "SeriesName", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "UserDataKey", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "SeasonName", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "SeasonId", "GUID");
- _connection.AddColumn(Logger, "TypedBaseItems", "SeriesId", "GUID");
- _connection.AddColumn(Logger, "TypedBaseItems", "SeriesSortName", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "ExternalSeriesId", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "ShortOverview", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "Tagline", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "Keywords", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "ProviderIds", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "Images", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "ProductionLocations", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "ThemeSongIds", "Text");
- _connection.AddColumn(Logger, "TypedBaseItems", "ThemeVideoIds", "Text");
-
- _connection.AddColumn(Logger, "UserDataKeys", "Priority", "INT");
- _connection.AddColumn(Logger, "ItemValues", "CleanValue", "Text");
-
- _connection.AddColumn(Logger, ChaptersTableName, "ImageDateModified", "DATETIME");
-
- string[] postQueries =
+ };
- {
+ connection.RunQueries(queries);
+
+ connection.RunInTransaction(db =>
+ {
+ var existingColumnNames = GetColumnNames(db, "AncestorIds");
+ AddColumn(db, "AncestorIds", "AncestorIdText", "Text", existingColumnNames);
+
+ existingColumnNames = GetColumnNames(db, "TypedBaseItems");
+
+ AddColumn(db, "TypedBaseItems", "Path", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "StartDate", "DATETIME", existingColumnNames);
+ 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);
+ AddColumn(db, "TypedBaseItems", "IsLocked", "BIT", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "Name", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "OfficialRating", "Text", existingColumnNames);
+
+ AddColumn(db, "TypedBaseItems", "MediaType", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "Overview", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "ParentIndexNumber", "INT", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "PremiereDate", "DATETIME", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "ProductionYear", "INT", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "ParentId", "GUID", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "Genres", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "SortName", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "RunTimeTicks", "BIGINT", existingColumnNames);
+
+ AddColumn(db, "TypedBaseItems", "OfficialRatingDescription", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "HomePageUrl", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "VoteCount", "INT", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "DisplayMediaType", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "DateCreated", "DATETIME", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "DateModified", "DATETIME", existingColumnNames);
+
+ AddColumn(db, "TypedBaseItems", "ForcedSortName", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "LocationType", "Text", existingColumnNames);
+
+ AddColumn(db, "TypedBaseItems", "IsSeries", "BIT", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "IsLive", "BIT", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "IsNews", "BIT", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "IsPremiere", "BIT", existingColumnNames);
+
+ AddColumn(db, "TypedBaseItems", "EpisodeTitle", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "IsRepeat", "BIT", existingColumnNames);
+
+ AddColumn(db, "TypedBaseItems", "PreferredMetadataLanguage", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "PreferredMetadataCountryCode", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "IsHD", "BIT", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "ExternalEtag", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "DateLastRefreshed", "DATETIME", existingColumnNames);
+
+ AddColumn(db, "TypedBaseItems", "DateLastSaved", "DATETIME", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "IsInMixedFolder", "BIT", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "LockedFields", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "Studios", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "Audio", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "ExternalServiceId", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "Tags", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "IsFolder", "BIT", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "InheritedParentalRatingValue", "INT", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "UnratedType", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "TopParentId", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "IsItemByName", "BIT", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "SourceType", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "TrailerTypes", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "CriticRating", "Float", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "CriticRatingSummary", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "InheritedTags", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "CleanName", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "PresentationUniqueKey", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "SlugName", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "OriginalTitle", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "PrimaryVersionId", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "DateLastMediaAdded", "DATETIME", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "Album", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "IsVirtualItem", "BIT", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "SeriesName", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "UserDataKey", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "SeasonName", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "SeasonId", "GUID", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "SeriesId", "GUID", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "SeriesSortName", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "ExternalSeriesId", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "ShortOverview", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "Tagline", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "Keywords", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "ProviderIds", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "Images", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "ProductionLocations", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "ThemeSongIds", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "ThemeVideoIds", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "TotalBitrate", "INT", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "ExtraType", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "Artists", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "AlbumArtists", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "ExternalId", "Text", existingColumnNames);
+ AddColumn(db, "TypedBaseItems", "SeriesPresentationUniqueKey", "Text", existingColumnNames);
+
+ existingColumnNames = GetColumnNames(db, "ItemValues");
+ AddColumn(db, "ItemValues", "CleanValue", "Text", existingColumnNames);
+
+ existingColumnNames = GetColumnNames(db, ChaptersTableName);
+ AddColumn(db, ChaptersTableName, "ImageDateModified", "DATETIME", existingColumnNames);
+
+ existingColumnNames = GetColumnNames(db, "MediaStreams");
+ AddColumn(db, "MediaStreams", "IsAvc", "BIT", existingColumnNames);
+ AddColumn(db, "MediaStreams", "TimeBase", "TEXT", existingColumnNames);
+ AddColumn(db, "MediaStreams", "CodecTimeBase", "TEXT", existingColumnNames);
+ AddColumn(db, "MediaStreams", "Title", "TEXT", existingColumnNames);
+ AddColumn(db, "MediaStreams", "NalLengthSize", "TEXT", existingColumnNames);
+ AddColumn(db, "MediaStreams", "Comment", "TEXT", existingColumnNames);
+ AddColumn(db, "MediaStreams", "CodecTag", "TEXT", existingColumnNames);
+ AddColumn(db, "MediaStreams", "PixelFormat", "TEXT", existingColumnNames);
+ AddColumn(db, "MediaStreams", "BitDepth", "INT", existingColumnNames);
+ AddColumn(db, "MediaStreams", "RefFrames", "INT", existingColumnNames);
+ AddColumn(db, "MediaStreams", "KeyFrames", "TEXT", existingColumnNames);
+ AddColumn(db, "MediaStreams", "IsAnamorphic", "BIT", existingColumnNames);
+ }, TransactionMode);
+
+ string[] postQueries =
+
+ {
// obsolete
"drop index if exists idx_TypedBaseItems",
"drop index if exists idx_mediastreams",
@@ -313,6 +315,15 @@ namespace MediaBrowser.Server.Implementations.Persistence
"drop index if exists idx_ItemValues3",
"drop index if exists idx_ItemValues4",
"drop index if exists idx_ItemValues5",
+ "drop index if exists idx_UserDataKeys3",
+ "drop table if exists UserDataKeys",
+ "drop table if exists ProviderIds",
+ "drop index if exists Idx_ProviderIds1",
+ "drop table if exists Images",
+ "drop index if exists idx_Images",
+ "drop index if exists idx_TypeSeriesPresentationUniqueKey",
+ "drop index if exists idx_SeriesPresentationUniqueKey",
+ "drop index if exists idx_TypeSeriesPresentationUniqueKey2",
"create index if not exists idx_PathTypedBaseItems on TypedBaseItems(Path)",
"create index if not exists idx_ParentIdTypedBaseItems on TypedBaseItems(ParentId)",
@@ -325,6 +336,13 @@ namespace MediaBrowser.Server.Implementations.Persistence
// covering index
"create index if not exists idx_TopParentIdGuid on TypedBaseItems(TopParentId,Guid)",
+ // series
+ "create index if not exists idx_TypeSeriesPresentationUniqueKey1 on TypedBaseItems(Type,SeriesPresentationUniqueKey,PresentationUniqueKey,SortName)",
+
+ // series counts
+ // seriesdateplayed sort order
+ "create index if not exists idx_TypeSeriesPresentationUniqueKey3 on TypedBaseItems(SeriesPresentationUniqueKey,Type,IsFolder,IsVirtualItem)",
+
// live tv programs
"create index if not exists idx_TypeTopParentIdStartDate on TypedBaseItems(Type,TopParentId,StartDate)",
@@ -344,21 +362,40 @@ namespace MediaBrowser.Server.Implementations.Persistence
// items by name
"create index if not exists idx_ItemValues6 on ItemValues(ItemId,Type,CleanValue)",
- "create index if not exists idx_ItemValues7 on ItemValues(Type,CleanValue,ItemId)",
-
- // covering index
- "create index if not exists idx_UserDataKeys3 on UserDataKeys(ItemId,Priority,UserDataKey)"
+ "create index if not exists idx_ItemValues7 on ItemValues(Type,CleanValue,ItemId)"
};
- _connection.RunQueries(postQueries, Logger);
+ connection.RunQueries(postQueries);
- PrepareStatements();
+ //await Vacuum(_connection).ConfigureAwait(false);
+ }
- new MediaStreamColumns(_connection, Logger).AddColumns();
+ userDataRepo.Initialize(WriteLock, _connection);
- DataExtensions.Attach(_connection, Path.Combine(_config.ApplicationPaths.DataPath, "userdata_v2.db"), "UserDataDb");
- await userDataRepo.Initialize(_connection, WriteLock).ConfigureAwait(false);
- //await Vacuum(_connection).ConfigureAwait(false);
+ _shrinkMemoryTimer = _timerFactory.Create(OnShrinkMemoryTimerCallback, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(15));
+ }
+
+ 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);
+ }
}
private readonly string[] _retriveItemColumns =
@@ -367,7 +404,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
"data",
"StartDate",
"EndDate",
- "IsOffline",
"ChannelId",
"IsMovie",
"IsSports",
@@ -437,7 +473,13 @@ namespace MediaBrowser.Server.Implementations.Persistence
"Images",
"ProductionLocations",
"ThemeSongIds",
- "ThemeVideoIds"
+ "ThemeVideoIds",
+ "TotalBitrate",
+ "ExtraType",
+ "Artists",
+ "AlbumArtists",
+ "ExternalId",
+ "SeriesPresentationUniqueKey"
};
private readonly string[] _mediaStreamSaveColumns =
@@ -476,10 +518,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
"CodecTimeBase"
};
- /// <summary>
- /// Prepares the statements.
- /// </summary>
- private void PrepareStatements()
+ private string GetSaveItemCommandText()
{
var saveColumns = new List<string>
{
@@ -513,7 +552,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
"ParentId",
"Genres",
"InheritedParentalRatingValue",
- "SchemaVersion",
"SortName",
"RunTimeTicks",
"OfficialRatingDescription",
@@ -523,7 +561,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
"DateCreated",
"DateModified",
"ForcedSortName",
- "IsOffline",
"LocationType",
"PreferredMetadataLanguage",
"PreferredMetadataCountryCode",
@@ -568,135 +605,27 @@ namespace MediaBrowser.Server.Implementations.Persistence
"Images",
"ProductionLocations",
"ThemeSongIds",
- "ThemeVideoIds"
+ "ThemeVideoIds",
+ "TotalBitrate",
+ "ExtraType",
+ "Artists",
+ "AlbumArtists",
+ "ExternalId",
+ "SeriesPresentationUniqueKey"
};
- _saveItemCommand = _connection.CreateCommand();
- _saveItemCommand.CommandText = "replace into TypedBaseItems (" + string.Join(",", saveColumns.ToArray()) + ") values (";
- for (var i = 1; i <= saveColumns.Count; i++)
+ var saveItemCommandCommandText = "replace into TypedBaseItems (" + string.Join(",", saveColumns.ToArray()) + ") values (";
+
+ for (var i = 0; i < saveColumns.Count; i++)
{
- if (i > 1)
+ if (i > 0)
{
- _saveItemCommand.CommandText += ",";
+ saveItemCommandCommandText += ",";
}
- _saveItemCommand.CommandText += "@" + i.ToString(CultureInfo.InvariantCulture);
-
- _saveItemCommand.Parameters.Add(_saveItemCommand, "@" + i.ToString(CultureInfo.InvariantCulture));
- }
- _saveItemCommand.CommandText += ")";
-
- _deleteItemCommand = _connection.CreateCommand();
- _deleteItemCommand.CommandText = "delete from TypedBaseItems where guid=@Id";
- _deleteItemCommand.Parameters.Add(_deleteItemCommand, "@Id");
-
- // People
- _deletePeopleCommand = _connection.CreateCommand();
- _deletePeopleCommand.CommandText = "delete from People where ItemId=@Id";
- _deletePeopleCommand.Parameters.Add(_deletePeopleCommand, "@Id");
-
- _savePersonCommand = _connection.CreateCommand();
- _savePersonCommand.CommandText = "insert into People (ItemId, Name, Role, PersonType, SortOrder, ListOrder) values (@ItemId, @Name, @Role, @PersonType, @SortOrder, @ListOrder)";
- _savePersonCommand.Parameters.Add(_savePersonCommand, "@ItemId");
- _savePersonCommand.Parameters.Add(_savePersonCommand, "@Name");
- _savePersonCommand.Parameters.Add(_savePersonCommand, "@Role");
- _savePersonCommand.Parameters.Add(_savePersonCommand, "@PersonType");
- _savePersonCommand.Parameters.Add(_savePersonCommand, "@SortOrder");
- _savePersonCommand.Parameters.Add(_savePersonCommand, "@ListOrder");
-
- // Ancestors
- _deleteAncestorsCommand = _connection.CreateCommand();
- _deleteAncestorsCommand.CommandText = "delete from AncestorIds where ItemId=@Id";
- _deleteAncestorsCommand.Parameters.Add(_deleteAncestorsCommand, "@Id");
-
- _saveAncestorCommand = _connection.CreateCommand();
- _saveAncestorCommand.CommandText = "insert into AncestorIds (ItemId, AncestorId, AncestorIdText) values (@ItemId, @AncestorId, @AncestorIdText)";
- _saveAncestorCommand.Parameters.Add(_saveAncestorCommand, "@ItemId");
- _saveAncestorCommand.Parameters.Add(_saveAncestorCommand, "@AncestorId");
- _saveAncestorCommand.Parameters.Add(_saveAncestorCommand, "@AncestorIdText");
-
- // Chapters
- _deleteChaptersCommand = _connection.CreateCommand();
- _deleteChaptersCommand.CommandText = "delete from " + ChaptersTableName + " where ItemId=@ItemId";
- _deleteChaptersCommand.Parameters.Add(_deleteChaptersCommand, "@ItemId");
-
- _saveChapterCommand = _connection.CreateCommand();
- _saveChapterCommand.CommandText = "replace into " + ChaptersTableName + " (ItemId, ChapterIndex, StartPositionTicks, Name, ImagePath) values (@ItemId, @ChapterIndex, @StartPositionTicks, @Name, @ImagePath)";
-
- _saveChapterCommand.Parameters.Add(_saveChapterCommand, "@ItemId");
- _saveChapterCommand.Parameters.Add(_saveChapterCommand, "@ChapterIndex");
- _saveChapterCommand.Parameters.Add(_saveChapterCommand, "@StartPositionTicks");
- _saveChapterCommand.Parameters.Add(_saveChapterCommand, "@Name");
- _saveChapterCommand.Parameters.Add(_saveChapterCommand, "@ImagePath");
- _saveChapterCommand.Parameters.Add(_saveChapterCommand, "@ImageDateModified");
-
- // MediaStreams
- _deleteStreamsCommand = _connection.CreateCommand();
- _deleteStreamsCommand.CommandText = "delete from mediastreams where ItemId=@ItemId";
- _deleteStreamsCommand.Parameters.Add(_deleteStreamsCommand, "@ItemId");
-
- _saveStreamCommand = _connection.CreateCommand();
-
- _saveStreamCommand.CommandText = string.Format("replace into mediastreams ({0}) values ({1})",
- string.Join(",", _mediaStreamSaveColumns),
- string.Join(",", _mediaStreamSaveColumns.Select(i => "@" + i).ToArray()));
-
- foreach (var col in _mediaStreamSaveColumns)
- {
- _saveStreamCommand.Parameters.Add(_saveStreamCommand, "@" + col);
- }
-
- _updateInheritedTagsCommand = _connection.CreateCommand();
- _updateInheritedTagsCommand.CommandText = "Update TypedBaseItems set InheritedTags=@InheritedTags where Guid=@Guid";
- _updateInheritedTagsCommand.Parameters.Add(_updateInheritedTagsCommand, "@Guid");
- _updateInheritedTagsCommand.Parameters.Add(_updateInheritedTagsCommand, "@InheritedTags");
-
- // user data
- _deleteUserDataKeysCommand = _connection.CreateCommand();
- _deleteUserDataKeysCommand.CommandText = "delete from UserDataKeys where ItemId=@Id";
- _deleteUserDataKeysCommand.Parameters.Add(_deleteUserDataKeysCommand, "@Id");
-
- _saveUserDataKeysCommand = _connection.CreateCommand();
- _saveUserDataKeysCommand.CommandText = "insert into UserDataKeys (ItemId, UserDataKey, Priority) values (@ItemId, @UserDataKey, @Priority)";
- _saveUserDataKeysCommand.Parameters.Add(_saveUserDataKeysCommand, "@ItemId");
- _saveUserDataKeysCommand.Parameters.Add(_saveUserDataKeysCommand, "@UserDataKey");
- _saveUserDataKeysCommand.Parameters.Add(_saveUserDataKeysCommand, "@Priority");
-
- // item values
- _deleteItemValuesCommand = _connection.CreateCommand();
- _deleteItemValuesCommand.CommandText = "delete from ItemValues where ItemId=@Id";
- _deleteItemValuesCommand.Parameters.Add(_deleteItemValuesCommand, "@Id");
-
- _saveItemValuesCommand = _connection.CreateCommand();
- _saveItemValuesCommand.CommandText = "insert into ItemValues (ItemId, Type, Value, CleanValue) values (@ItemId, @Type, @Value, @CleanValue)";
- _saveItemValuesCommand.Parameters.Add(_saveItemValuesCommand, "@ItemId");
- _saveItemValuesCommand.Parameters.Add(_saveItemValuesCommand, "@Type");
- _saveItemValuesCommand.Parameters.Add(_saveItemValuesCommand, "@Value");
- _saveItemValuesCommand.Parameters.Add(_saveItemValuesCommand, "@CleanValue");
-
- // provider ids
- _deleteProviderIdsCommand = _connection.CreateCommand();
- _deleteProviderIdsCommand.CommandText = "delete from ProviderIds where ItemId=@Id";
- _deleteProviderIdsCommand.Parameters.Add(_deleteProviderIdsCommand, "@Id");
-
- _saveProviderIdsCommand = _connection.CreateCommand();
- _saveProviderIdsCommand.CommandText = "insert into ProviderIds (ItemId, Name, Value) values (@ItemId, @Name, @Value)";
- _saveProviderIdsCommand.Parameters.Add(_saveProviderIdsCommand, "@ItemId");
- _saveProviderIdsCommand.Parameters.Add(_saveProviderIdsCommand, "@Name");
- _saveProviderIdsCommand.Parameters.Add(_saveProviderIdsCommand, "@Value");
-
- // images
- _deleteImagesCommand = _connection.CreateCommand();
- _deleteImagesCommand.CommandText = "delete from Images where ItemId=@Id";
- _deleteImagesCommand.Parameters.Add(_deleteImagesCommand, "@Id");
-
- _saveImagesCommand = _connection.CreateCommand();
- _saveImagesCommand.CommandText = "insert into Images (ItemId, ImageType, Path, DateModified, IsPlaceHolder, SortOrder) values (@ItemId, @ImageType, @Path, @DateModified, @IsPlaceHolder, @SortOrder)";
- _saveImagesCommand.Parameters.Add(_saveImagesCommand, "@ItemId");
- _saveImagesCommand.Parameters.Add(_saveImagesCommand, "@ImageType");
- _saveImagesCommand.Parameters.Add(_saveImagesCommand, "@Path");
- _saveImagesCommand.Parameters.Add(_saveImagesCommand, "@DateModified");
- _saveImagesCommand.Parameters.Add(_saveImagesCommand, "@IsPlaceHolder");
- _saveImagesCommand.Parameters.Add(_saveImagesCommand, "@SortOrder");
+ saveItemCommandCommandText += "@" + saveColumns[i];
+ }
+ saveItemCommandCommandText += ")";
+ return saveItemCommandCommandText;
}
/// <summary>
@@ -713,7 +642,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
throw new ArgumentNullException("item");
}
- return SaveItems(new[] { item }, cancellationToken);
+ return SaveItems(new List<BaseItem> { item }, cancellationToken);
}
/// <summary>
@@ -727,7 +656,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// or
/// cancellationToken
/// </exception>
- public async Task SaveItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken)
+ public async Task SaveItems(List<BaseItem> items, CancellationToken cancellationToken)
{
if (items == null)
{
@@ -738,378 +667,461 @@ namespace MediaBrowser.Server.Implementations.Persistence
CheckDisposed();
- await WriteLock.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- IDbTransaction transaction = null;
-
- try
+ var tuples = new List<Tuple<BaseItem, List<Guid>, BaseItem, string>>();
+ foreach (var item in items)
{
- transaction = _connection.BeginTransaction();
+ var ancestorIds = item.SupportsAncestors ?
+ item.GetAncestorIds().Distinct().ToList() :
+ null;
- foreach (var item in items)
- {
- cancellationToken.ThrowIfCancellationRequested();
+ var topParent = item.GetTopParent();
- var index = 0;
+ var userdataKey = item.GetUserDataKeys().FirstOrDefault();
- _saveItemCommand.GetParameter(index++).Value = item.Id;
- _saveItemCommand.GetParameter(index++).Value = item.GetType().FullName;
+ tuples.Add(new Tuple<BaseItem, List<Guid>, BaseItem, string>(item, ancestorIds, topParent, userdataKey));
+ }
- if (TypeRequiresDeserialization(item.GetType()))
- {
- _saveItemCommand.GetParameter(index++).Value = _jsonSerializer.SerializeToBytes(item, _memoryStreamProvider);
- }
- else
+ using (WriteLock.Write())
+ {
+ using (var connection = CreateConnection())
+ {
+ connection.RunInTransaction(db =>
{
- _saveItemCommand.GetParameter(index++).Value = null;
- }
-
- _saveItemCommand.GetParameter(index++).Value = item.Path;
+ SaveItemsInTranscation(db, tuples);
+ }, TransactionMode);
+ }
+ }
+ }
- var hasStartDate = item as IHasStartDate;
- if (hasStartDate != null)
- {
- _saveItemCommand.GetParameter(index++).Value = hasStartDate.StartDate;
- }
- else
- {
- _saveItemCommand.GetParameter(index++).Value = null;
- }
+ private void SaveItemsInTranscation(IDatabaseConnection db, List<Tuple<BaseItem, List<Guid>, BaseItem, string>> tuples)
+ {
+ var requiresReset = false;
- _saveItemCommand.GetParameter(index++).Value = item.EndDate;
- _saveItemCommand.GetParameter(index++).Value = item.ChannelId;
+ var statements = PrepareAllSafe(db, new string[]
+ {
+ GetSaveItemCommandText(),
+ "delete from AncestorIds where ItemId=@ItemId",
+ "insert into AncestorIds (ItemId, AncestorId, AncestorIdText) values (@ItemId, @AncestorId, @AncestorIdText)"
+ }).ToList();
- var hasProgramAttributes = item as IHasProgramAttributes;
- if (hasProgramAttributes != null)
- {
- _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.IsKids;
- _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.IsMovie;
- _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.IsSports;
- _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.IsSeries;
- _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.IsLive;
- _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.IsNews;
- _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.IsPremiere;
- _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.EpisodeTitle;
- _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.IsRepeat;
- }
- else
+ using (var saveItemStatement = statements[0])
+ {
+ using (var deleteAncestorsStatement = statements[1])
+ {
+ using (var updateAncestorsStatement = statements[2])
{
- _saveItemCommand.GetParameter(index++).Value = null;
- _saveItemCommand.GetParameter(index++).Value = null;
- _saveItemCommand.GetParameter(index++).Value = null;
- _saveItemCommand.GetParameter(index++).Value = null;
- _saveItemCommand.GetParameter(index++).Value = null;
- _saveItemCommand.GetParameter(index++).Value = null;
- _saveItemCommand.GetParameter(index++).Value = null;
- _saveItemCommand.GetParameter(index++).Value = null;
- _saveItemCommand.GetParameter(index++).Value = null;
- }
+ foreach (var tuple in tuples)
+ {
+ if (requiresReset)
+ {
+ saveItemStatement.Reset();
+ }
- _saveItemCommand.GetParameter(index++).Value = item.CommunityRating;
- _saveItemCommand.GetParameter(index++).Value = item.CustomRating;
+ var item = tuple.Item1;
+ var topParent = tuple.Item3;
+ var userDataKey = tuple.Item4;
- _saveItemCommand.GetParameter(index++).Value = item.IndexNumber;
- _saveItemCommand.GetParameter(index++).Value = item.IsLocked;
+ SaveItem(item, topParent, userDataKey, saveItemStatement);
+ //Logger.Debug(_saveItemCommand.CommandText);
- _saveItemCommand.GetParameter(index++).Value = item.Name;
- _saveItemCommand.GetParameter(index++).Value = item.OfficialRating;
+ if (item.SupportsAncestors)
+ {
+ UpdateAncestors(item.Id, tuple.Item2, db, deleteAncestorsStatement, updateAncestorsStatement);
+ }
- _saveItemCommand.GetParameter(index++).Value = item.MediaType;
- _saveItemCommand.GetParameter(index++).Value = item.Overview;
- _saveItemCommand.GetParameter(index++).Value = item.ParentIndexNumber;
- _saveItemCommand.GetParameter(index++).Value = item.PremiereDate;
- _saveItemCommand.GetParameter(index++).Value = item.ProductionYear;
+ UpdateItemValues(item.Id, GetItemValuesToSave(item), db);
- if (item.ParentId == Guid.Empty)
- {
- _saveItemCommand.GetParameter(index++).Value = null;
- }
- else
- {
- _saveItemCommand.GetParameter(index++).Value = item.ParentId;
+ requiresReset = true;
+ }
}
+ }
+ }
+ }
- _saveItemCommand.GetParameter(index++).Value = string.Join("|", item.Genres.ToArray());
- _saveItemCommand.GetParameter(index++).Value = item.GetInheritedParentalRatingValue() ?? 0;
+ private void SaveItem(BaseItem item, BaseItem topParent, string userDataKey, IStatement saveItemStatement)
+ {
+ saveItemStatement.TryBind("@guid", item.Id);
+ saveItemStatement.TryBind("@type", item.GetType().FullName);
- _saveItemCommand.GetParameter(index++).Value = LatestSchemaVersion;
- _saveItemCommand.GetParameter(index++).Value = item.SortName;
- _saveItemCommand.GetParameter(index++).Value = item.RunTimeTicks;
+ if (TypeRequiresDeserialization(item.GetType()))
+ {
+ saveItemStatement.TryBind("@data", _jsonSerializer.SerializeToBytes(item, _memoryStreamProvider));
+ }
+ else
+ {
+ saveItemStatement.TryBindNull("@data");
+ }
- _saveItemCommand.GetParameter(index++).Value = item.OfficialRatingDescription;
- _saveItemCommand.GetParameter(index++).Value = item.HomePageUrl;
- _saveItemCommand.GetParameter(index++).Value = item.VoteCount;
- _saveItemCommand.GetParameter(index++).Value = item.DisplayMediaType;
- _saveItemCommand.GetParameter(index++).Value = item.DateCreated;
- _saveItemCommand.GetParameter(index++).Value = item.DateModified;
+ saveItemStatement.TryBind("@Path", item.Path);
- _saveItemCommand.GetParameter(index++).Value = item.ForcedSortName;
- _saveItemCommand.GetParameter(index++).Value = item.IsOffline;
- _saveItemCommand.GetParameter(index++).Value = item.LocationType.ToString();
+ var hasStartDate = item as IHasStartDate;
+ if (hasStartDate != null)
+ {
+ saveItemStatement.TryBind("@StartDate", hasStartDate.StartDate);
+ }
+ else
+ {
+ saveItemStatement.TryBindNull("@StartDate");
+ }
- _saveItemCommand.GetParameter(index++).Value = item.PreferredMetadataLanguage;
- _saveItemCommand.GetParameter(index++).Value = item.PreferredMetadataCountryCode;
- _saveItemCommand.GetParameter(index++).Value = item.IsHD;
- _saveItemCommand.GetParameter(index++).Value = item.ExternalEtag;
+ if (item.EndDate.HasValue)
+ {
+ saveItemStatement.TryBind("@EndDate", item.EndDate.Value);
+ }
+ else
+ {
+ saveItemStatement.TryBindNull("@EndDate");
+ }
- if (item.DateLastRefreshed == default(DateTime))
- {
- _saveItemCommand.GetParameter(index++).Value = null;
- }
- else
- {
- _saveItemCommand.GetParameter(index++).Value = item.DateLastRefreshed;
- }
+ saveItemStatement.TryBind("@ChannelId", item.ChannelId);
- if (item.DateLastSaved == default(DateTime))
- {
- _saveItemCommand.GetParameter(index++).Value = null;
- }
- else
- {
- _saveItemCommand.GetParameter(index++).Value = item.DateLastSaved;
- }
+ 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");
+ }
+
+ saveItemStatement.TryBind("@CommunityRating", item.CommunityRating);
+ saveItemStatement.TryBind("@CustomRating", item.CustomRating);
+ saveItemStatement.TryBind("@IndexNumber", item.IndexNumber);
+ saveItemStatement.TryBind("@IsLocked", item.IsLocked);
+ saveItemStatement.TryBind("@Name", item.Name);
+ saveItemStatement.TryBind("@OfficialRating", item.OfficialRating);
+ saveItemStatement.TryBind("@MediaType", item.MediaType);
+ saveItemStatement.TryBind("@Overview", item.Overview);
+ saveItemStatement.TryBind("@ParentIndexNumber", item.ParentIndexNumber);
+ saveItemStatement.TryBind("@PremiereDate", item.PremiereDate);
+ saveItemStatement.TryBind("@ProductionYear", item.ProductionYear);
+
+ if (item.ParentId == Guid.Empty)
+ {
+ saveItemStatement.TryBindNull("@ParentId");
+ }
+ else
+ {
+ saveItemStatement.TryBind("@ParentId", item.ParentId);
+ }
- _saveItemCommand.GetParameter(index++).Value = item.IsInMixedFolder;
- _saveItemCommand.GetParameter(index++).Value = string.Join("|", item.LockedFields.Select(i => i.ToString()).ToArray());
- _saveItemCommand.GetParameter(index++).Value = string.Join("|", item.Studios.ToArray());
+ if (item.Genres.Count > 0)
+ {
+ saveItemStatement.TryBind("@Genres", string.Join("|", item.Genres.ToArray()));
+ }
+ else
+ {
+ saveItemStatement.TryBindNull("@Genres");
+ }
- if (item.Audio.HasValue)
- {
- _saveItemCommand.GetParameter(index++).Value = item.Audio.Value.ToString();
- }
- else
- {
- _saveItemCommand.GetParameter(index++).Value = null;
- }
+ saveItemStatement.TryBind("@InheritedParentalRatingValue", item.InheritedParentalRatingValue);
- _saveItemCommand.GetParameter(index++).Value = item.ServiceName;
+ saveItemStatement.TryBind("@SortName", item.SortName);
+ saveItemStatement.TryBind("@RunTimeTicks", item.RunTimeTicks);
- if (item.Tags.Count > 0)
- {
- _saveItemCommand.GetParameter(index++).Value = string.Join("|", item.Tags.ToArray());
- }
- else
- {
- _saveItemCommand.GetParameter(index++).Value = null;
- }
+ saveItemStatement.TryBind("@OfficialRatingDescription", item.OfficialRatingDescription);
+ saveItemStatement.TryBind("@HomePageUrl", item.HomePageUrl);
+ saveItemStatement.TryBind("@VoteCount", item.VoteCount);
+ saveItemStatement.TryBind("@DisplayMediaType", item.DisplayMediaType);
+ saveItemStatement.TryBind("@DateCreated", item.DateCreated);
+ saveItemStatement.TryBind("@DateModified", item.DateModified);
- _saveItemCommand.GetParameter(index++).Value = item.IsFolder;
+ saveItemStatement.TryBind("@ForcedSortName", item.ForcedSortName);
+ saveItemStatement.TryBind("@LocationType", item.LocationType.ToString());
- _saveItemCommand.GetParameter(index++).Value = item.GetBlockUnratedType().ToString();
+ saveItemStatement.TryBind("@PreferredMetadataLanguage", item.PreferredMetadataLanguage);
+ saveItemStatement.TryBind("@PreferredMetadataCountryCode", item.PreferredMetadataCountryCode);
+ saveItemStatement.TryBind("@IsHD", item.IsHD);
+ saveItemStatement.TryBind("@ExternalEtag", item.ExternalEtag);
- var topParent = item.GetTopParent();
- if (topParent != null)
- {
- //Logger.Debug("Item {0} has top parent {1}", item.Id, topParent.Id);
- _saveItemCommand.GetParameter(index++).Value = topParent.Id.ToString("N");
- }
- else
- {
- //Logger.Debug("Item {0} has null top parent", item.Id);
- _saveItemCommand.GetParameter(index++).Value = null;
- }
+ if (item.DateLastRefreshed != default(DateTime))
+ {
+ saveItemStatement.TryBind("@DateLastRefreshed", item.DateLastRefreshed);
+ }
+ else
+ {
+ saveItemStatement.TryBindNull("@DateLastRefreshed");
+ }
- var isByName = false;
- var byName = item as IItemByName;
- if (byName != null)
- {
- var dualAccess = item as IHasDualAccess;
- isByName = dualAccess == null || dualAccess.IsAccessedByName;
- }
- _saveItemCommand.GetParameter(index++).Value = isByName;
+ if (item.DateLastSaved != default(DateTime))
+ {
+ saveItemStatement.TryBind("@DateLastSaved", item.DateLastSaved);
+ }
+ else
+ {
+ saveItemStatement.TryBindNull("@DateLastSaved");
+ }
- _saveItemCommand.GetParameter(index++).Value = item.SourceType.ToString();
+ saveItemStatement.TryBind("@IsInMixedFolder", item.IsInMixedFolder);
- var trailer = item as Trailer;
- if (trailer != null && trailer.TrailerTypes.Count > 0)
- {
- _saveItemCommand.GetParameter(index++).Value = string.Join("|", trailer.TrailerTypes.Select(i => i.ToString()).ToArray());
- }
- else
- {
- _saveItemCommand.GetParameter(index++).Value = null;
- }
+ if (item.LockedFields.Count > 0)
+ {
+ saveItemStatement.TryBind("@LockedFields", string.Join("|", item.LockedFields.Select(i => i.ToString()).ToArray()));
+ }
+ else
+ {
+ saveItemStatement.TryBindNull("@LockedFields");
+ }
- _saveItemCommand.GetParameter(index++).Value = item.CriticRating;
- _saveItemCommand.GetParameter(index++).Value = item.CriticRatingSummary;
+ if (item.Studios.Count > 0)
+ {
+ saveItemStatement.TryBind("@Studios", string.Join("|", item.Studios.ToArray()));
+ }
+ else
+ {
+ saveItemStatement.TryBindNull("@Studios");
+ }
- var inheritedTags = item.GetInheritedTags();
- if (inheritedTags.Count > 0)
- {
- _saveItemCommand.GetParameter(index++).Value = string.Join("|", inheritedTags.ToArray());
- }
- else
- {
- _saveItemCommand.GetParameter(index++).Value = null;
- }
+ if (item.Audio.HasValue)
+ {
+ saveItemStatement.TryBind("@Audio", item.Audio.Value.ToString());
+ }
+ else
+ {
+ saveItemStatement.TryBindNull("@Audio");
+ }
- if (string.IsNullOrWhiteSpace(item.Name))
- {
- _saveItemCommand.GetParameter(index++).Value = null;
- }
- else
- {
- _saveItemCommand.GetParameter(index++).Value = GetCleanValue(item.Name);
- }
+ saveItemStatement.TryBind("@ExternalServiceId", item.ServiceName);
- _saveItemCommand.GetParameter(index++).Value = item.GetPresentationUniqueKey();
- _saveItemCommand.GetParameter(index++).Value = item.SlugName;
- _saveItemCommand.GetParameter(index++).Value = item.OriginalTitle;
+ if (item.Tags.Count > 0)
+ {
+ saveItemStatement.TryBind("@Tags", string.Join("|", item.Tags.ToArray()));
+ }
+ else
+ {
+ saveItemStatement.TryBindNull("@Tags");
+ }
- var video = item as Video;
- if (video != null)
- {
- _saveItemCommand.GetParameter(index++).Value = video.PrimaryVersionId;
- }
- else
- {
- _saveItemCommand.GetParameter(index++).Value = null;
- }
+ saveItemStatement.TryBind("@IsFolder", item.IsFolder);
- var folder = item as Folder;
- if (folder != null && folder.DateLastMediaAdded.HasValue)
- {
- _saveItemCommand.GetParameter(index++).Value = folder.DateLastMediaAdded.Value;
- }
- else
- {
- _saveItemCommand.GetParameter(index++).Value = null;
- }
+ saveItemStatement.TryBind("@UnratedType", item.GetBlockUnratedType().ToString());
- _saveItemCommand.GetParameter(index++).Value = item.Album;
+ if (topParent != null)
+ {
+ //Logger.Debug("Item {0} has top parent {1}", item.Id, topParent.Id);
+ saveItemStatement.TryBind("@TopParentId", topParent.Id.ToString("N"));
+ }
+ else
+ {
+ //Logger.Debug("Item {0} has null top parent", item.Id);
+ saveItemStatement.TryBindNull("@TopParentId");
+ }
- _saveItemCommand.GetParameter(index++).Value = item.IsVirtualItem;
+ var isByName = false;
+ var byName = item as IItemByName;
+ if (byName != null)
+ {
+ var dualAccess = item as IHasDualAccess;
+ isByName = dualAccess == null || dualAccess.IsAccessedByName;
+ }
+ saveItemStatement.TryBind("@IsItemByName", isByName);
+ saveItemStatement.TryBind("@SourceType", item.SourceType.ToString());
- var hasSeries = item as IHasSeries;
- if (hasSeries != null)
- {
- _saveItemCommand.GetParameter(index++).Value = hasSeries.FindSeriesName();
- }
- else
- {
- _saveItemCommand.GetParameter(index++).Value = null;
- }
+ var trailer = item as Trailer;
+ if (trailer != null && trailer.TrailerTypes.Count > 0)
+ {
+ saveItemStatement.TryBind("@TrailerTypes", string.Join("|", trailer.TrailerTypes.Select(i => i.ToString()).ToArray()));
+ }
+ else
+ {
+ saveItemStatement.TryBindNull("@TrailerTypes");
+ }
- _saveItemCommand.GetParameter(index++).Value = item.GetUserDataKeys().FirstOrDefault();
+ saveItemStatement.TryBind("@CriticRating", item.CriticRating);
+ saveItemStatement.TryBind("@CriticRatingSummary", item.CriticRatingSummary);
- var episode = item as Episode;
- if (episode != null)
- {
- _saveItemCommand.GetParameter(index++).Value = episode.FindSeasonName();
- _saveItemCommand.GetParameter(index++).Value = episode.FindSeasonId();
- }
- else
- {
- _saveItemCommand.GetParameter(index++).Value = null;
- _saveItemCommand.GetParameter(index++).Value = null;
- }
+ var inheritedTags = item.InheritedTags;
+ if (inheritedTags.Count > 0)
+ {
+ saveItemStatement.TryBind("@InheritedTags", string.Join("|", inheritedTags.ToArray()));
+ }
+ else
+ {
+ saveItemStatement.TryBindNull("@InheritedTags");
+ }
- if (hasSeries != null)
- {
- _saveItemCommand.GetParameter(index++).Value = hasSeries.FindSeriesId();
- _saveItemCommand.GetParameter(index++).Value = hasSeries.FindSeriesSortName();
- }
- else
- {
- _saveItemCommand.GetParameter(index++).Value = null;
- _saveItemCommand.GetParameter(index++).Value = null;
- }
+ if (string.IsNullOrWhiteSpace(item.Name))
+ {
+ saveItemStatement.TryBindNull("@CleanName");
+ }
+ else
+ {
+ saveItemStatement.TryBind("@CleanName", GetCleanValue(item.Name));
+ }
- _saveItemCommand.GetParameter(index++).Value = item.ExternalSeriesId;
- _saveItemCommand.GetParameter(index++).Value = item.ShortOverview;
- _saveItemCommand.GetParameter(index++).Value = item.Tagline;
+ saveItemStatement.TryBind("@PresentationUniqueKey", item.PresentationUniqueKey);
+ saveItemStatement.TryBind("@SlugName", item.SlugName);
+ saveItemStatement.TryBind("@OriginalTitle", item.OriginalTitle);
- if (item.Keywords.Count > 0)
- {
- _saveItemCommand.GetParameter(index++).Value = string.Join("|", item.Keywords.ToArray());
- }
- else
- {
- _saveItemCommand.GetParameter(index++).Value = null;
- }
+ var video = item as Video;
+ if (video != null)
+ {
+ saveItemStatement.TryBind("@PrimaryVersionId", video.PrimaryVersionId);
+ }
+ else
+ {
+ saveItemStatement.TryBindNull("@PrimaryVersionId");
+ }
- _saveItemCommand.GetParameter(index++).Value = SerializeProviderIds(item);
- _saveItemCommand.GetParameter(index++).Value = SerializeImages(item);
+ var folder = item as Folder;
+ if (folder != null && folder.DateLastMediaAdded.HasValue)
+ {
+ saveItemStatement.TryBind("@DateLastMediaAdded", folder.DateLastMediaAdded.Value);
+ }
+ else
+ {
+ saveItemStatement.TryBindNull("@DateLastMediaAdded");
+ }
- if (item.ProductionLocations.Count > 0)
- {
- _saveItemCommand.GetParameter(index++).Value = string.Join("|", item.ProductionLocations.ToArray());
- }
- else
- {
- _saveItemCommand.GetParameter(index++).Value = null;
- }
+ saveItemStatement.TryBind("@Album", item.Album);
+ saveItemStatement.TryBind("@IsVirtualItem", item.IsVirtualItem);
- if (item.ThemeSongIds.Count > 0)
- {
- _saveItemCommand.GetParameter(index++).Value = string.Join("|", item.ThemeSongIds.Select(i => i.ToString("N")).ToArray());
- }
- else
- {
- _saveItemCommand.GetParameter(index++).Value = null;
- }
+ var hasSeries = item as IHasSeries;
+ if (hasSeries != null)
+ {
+ saveItemStatement.TryBind("@SeriesName", hasSeries.SeriesName);
+ }
+ else
+ {
+ saveItemStatement.TryBindNull("@SeriesName");
+ }
- if (item.ThemeVideoIds.Count > 0)
- {
- _saveItemCommand.GetParameter(index++).Value = string.Join("|", item.ThemeVideoIds.Select(i => i.ToString("N")).ToArray());
- }
- else
- {
- _saveItemCommand.GetParameter(index++).Value = null;
- }
+ if (string.IsNullOrWhiteSpace(userDataKey))
+ {
+ saveItemStatement.TryBindNull("@UserDataKey");
+ }
+ else
+ {
+ saveItemStatement.TryBind("@UserDataKey", userDataKey);
+ }
+
+ var episode = item as Episode;
+ if (episode != null)
+ {
+ saveItemStatement.TryBind("@SeasonName", episode.SeasonName);
+ saveItemStatement.TryBind("@SeasonId", episode.SeasonId);
+ }
+ else
+ {
+ saveItemStatement.TryBindNull("@SeasonName");
+ saveItemStatement.TryBindNull("@SeasonId");
+ }
- _saveItemCommand.Transaction = transaction;
+ if (hasSeries != null)
+ {
+ saveItemStatement.TryBind("@SeriesId", hasSeries.SeriesId);
+ saveItemStatement.TryBind("@SeriesSortName", hasSeries.SeriesSortName);
+ saveItemStatement.TryBind("@SeriesPresentationUniqueKey", hasSeries.SeriesPresentationUniqueKey);
+ }
+ else
+ {
+ saveItemStatement.TryBindNull("@SeriesId");
+ saveItemStatement.TryBindNull("@SeriesSortName");
+ saveItemStatement.TryBindNull("@SeriesPresentationUniqueKey");
+ }
- _saveItemCommand.ExecuteNonQuery();
+ saveItemStatement.TryBind("@ExternalSeriesId", item.ExternalSeriesId);
+ saveItemStatement.TryBind("@ShortOverview", item.ShortOverview);
+ saveItemStatement.TryBind("@Tagline", item.Tagline);
- if (item.SupportsAncestors)
- {
- UpdateAncestors(item.Id, item.GetAncestorIds().Distinct().ToList(), transaction);
- }
+ if (item.Keywords.Count > 0)
+ {
+ saveItemStatement.TryBind("@Keywords", string.Join("|", item.Keywords.ToArray()));
+ }
+ else
+ {
+ saveItemStatement.TryBindNull("@Keywords");
+ }
- UpdateUserDataKeys(item.Id, item.GetUserDataKeys().Distinct(StringComparer.OrdinalIgnoreCase).ToList(), transaction);
- UpdateImages(item.Id, item.ImageInfos, transaction);
- UpdateProviderIds(item.Id, item.ProviderIds, transaction);
- UpdateItemValues(item.Id, GetItemValuesToSave(item), transaction);
- }
+ saveItemStatement.TryBind("@ProviderIds", SerializeProviderIds(item));
+ saveItemStatement.TryBind("@Images", SerializeImages(item));
- transaction.Commit();
+ if (item.ProductionLocations.Count > 0)
+ {
+ saveItemStatement.TryBind("@ProductionLocations", string.Join("|", item.ProductionLocations.ToArray()));
}
- catch (OperationCanceledException)
+ else
{
- if (transaction != null)
- {
- transaction.Rollback();
- }
+ saveItemStatement.TryBindNull("@ProductionLocations");
+ }
- throw;
+ if (item.ThemeSongIds.Count > 0)
+ {
+ saveItemStatement.TryBind("@ThemeSongIds", string.Join("|", item.ThemeSongIds.ToArray()));
}
- catch (Exception e)
+ else
{
- Logger.ErrorException("Failed to save items:", e);
+ saveItemStatement.TryBindNull("@ThemeSongIds");
+ }
- if (transaction != null)
- {
- transaction.Rollback();
- }
+ if (item.ThemeVideoIds.Count > 0)
+ {
+ saveItemStatement.TryBind("@ThemeVideoIds", string.Join("|", item.ThemeVideoIds.ToArray()));
+ }
+ else
+ {
+ saveItemStatement.TryBindNull("@ThemeVideoIds");
+ }
- throw;
+ saveItemStatement.TryBind("@TotalBitrate", item.TotalBitrate);
+ if (item.ExtraType.HasValue)
+ {
+ saveItemStatement.TryBind("@ExtraType", item.ExtraType.Value.ToString());
}
- finally
+ else
+ {
+ saveItemStatement.TryBindNull("@ExtraType");
+ }
+
+ string artists = null;
+ var hasArtists = item as IHasArtist;
+ if (hasArtists != null)
{
- if (transaction != null)
+ if (hasArtists.Artists.Count > 0)
{
- transaction.Dispose();
+ artists = string.Join("|", hasArtists.Artists.ToArray());
}
+ }
+ saveItemStatement.TryBind("@Artists", artists);
- WriteLock.Release();
+ string albumArtists = null;
+ var hasAlbumArtists = item as IHasAlbumArtist;
+ if (hasAlbumArtists != null)
+ {
+ if (hasAlbumArtists.AlbumArtists.Count > 0)
+ {
+ albumArtists = string.Join("|", hasAlbumArtists.AlbumArtists.ToArray());
+ }
}
+ saveItemStatement.TryBind("@AlbumArtists", albumArtists);
+ saveItemStatement.TryBind("@ExternalId", item.ExternalId);
+
+ saveItemStatement.MoveNext();
}
private string SerializeProviderIds(BaseItem item)
{
- var ids = item.ProviderIds.ToList();
+ // Ideally we shouldn't need this IsNullOrWhiteSpace check but we're seeing some cases of bad data slip through
+ var ids = item.ProviderIds
+ .Where(i => !string.IsNullOrWhiteSpace(i.Value))
+ .ToList();
if (ids.Count == 0)
{
@@ -1137,7 +1149,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
var idParts = part.Split('=');
- item.SetProviderId(idParts[0], idParts[1]);
+ if (idParts.Length == 2)
+ {
+ item.SetProviderId(idParts[0], idParts[1]);
+ }
}
}
@@ -1150,7 +1165,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
return null;
}
- return string.Join("|", images.Select(ToValueString).ToArray());
+ var imageStrings = images.Where(i => !string.IsNullOrWhiteSpace(i.Path)).Select(ToValueString).ToArray();
+
+ return string.Join("|", imageStrings);
}
private void DeserializeImages(string value, BaseItem item)
@@ -1169,7 +1186,12 @@ namespace MediaBrowser.Server.Implementations.Persistence
foreach (var part in parts)
{
- item.ImageInfos.Add(ItemImageInfoFromValueString(part));
+ var image = ItemImageInfoFromValueString(part);
+
+ if (image != null)
+ {
+ item.ImageInfos.Add(image);
+ }
}
}
@@ -1177,7 +1199,14 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
var delimeter = "*";
- return (image.Path ?? string.Empty) +
+ var path = image.Path;
+
+ if (path == null)
+ {
+ path = string.Empty;
+ }
+
+ return path +
delimeter +
image.DateModified.Ticks.ToString(CultureInfo.InvariantCulture) +
delimeter +
@@ -1190,6 +1219,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
var parts = value.Split(new[] { '*' }, StringSplitOptions.None);
+ if (parts.Length != 4)
+ {
+ return null;
+ }
+
var image = new ItemImageInfo();
image.Path = parts[0];
@@ -1215,24 +1249,27 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
CheckDisposed();
-
- using (var cmd = _connection.CreateCommand())
+ //Logger.Info("Retrieving item {0}", id.ToString("N"));
+ using (WriteLock.Read())
{
- cmd.CommandText = "select " + string.Join(",", _retriveItemColumns) + " from TypedBaseItems where guid = @guid";
- cmd.Parameters.Add(cmd, "@guid", DbType.Guid).Value = id;
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
+ using (var connection = CreateConnection(true))
{
- if (reader.Read())
+ using (var statement = PrepareStatementSafe(connection, "select " + string.Join(",", _retriveItemColumns) + " from TypedBaseItems where guid = @guid"))
{
- return GetItem(reader);
+ statement.TryBind("@guid", id);
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ return GetItem(row);
+ }
}
+
+ return null;
}
- return null;
}
}
- private BaseItem GetItem(IDataReader reader)
+ private BaseItem GetItem(IReadOnlyList<IResultSetValue> reader)
{
return GetItem(reader, new InternalItemsQuery());
}
@@ -1313,11 +1350,34 @@ namespace MediaBrowser.Server.Implementations.Persistence
return false;
}
}
+ if (_config.Configuration.SkipDeserializationForAudio)
+ {
+ if (type == typeof(Audio))
+ {
+ return false;
+ }
+ if (type == typeof(LiveTvAudioRecording))
+ {
+ return false;
+ }
+ if (type == typeof(AudioPodcast))
+ {
+ return false;
+ }
+ if (type == typeof(AudioBook))
+ {
+ return false;
+ }
+ if (type == typeof(MusicAlbum))
+ {
+ return false;
+ }
+ }
return true;
}
- private BaseItem GetItem(IDataReader reader, InternalItemsQuery query)
+ private BaseItem GetItem(IReadOnlyList<IResultSetValue> reader, InternalItemsQuery query)
{
var typeString = reader.GetString(0);
@@ -1334,8 +1394,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (TypeRequiresDeserialization(type))
{
- using (var stream = reader.GetMemoryStream(1, _memoryStreamProvider))
+ using (var stream = _memoryStreamProvider.CreateNew(reader[1].ToBlob()))
{
+ stream.Position = 0;
+
try
{
item = _jsonSerializer.DeserializeFromStream(stream, type) as BaseItem;
@@ -1368,75 +1430,83 @@ namespace MediaBrowser.Server.Implementations.Persistence
var hasStartDate = item as IHasStartDate;
if (hasStartDate != null)
{
- hasStartDate.StartDate = reader.GetDateTime(2).ToUniversalTime();
+ hasStartDate.StartDate = reader[2].ReadDateTime();
}
}
if (!reader.IsDBNull(3))
{
- item.EndDate = reader.GetDateTime(3).ToUniversalTime();
+ item.EndDate = reader[3].ReadDateTime();
}
if (!reader.IsDBNull(4))
{
- item.IsOffline = reader.GetBoolean(4);
+ item.ChannelId = reader.GetString(4);
}
- if (!reader.IsDBNull(5))
- {
- item.ChannelId = reader.GetString(5);
- }
+ var index = 5;
var hasProgramAttributes = item as IHasProgramAttributes;
if (hasProgramAttributes != null)
{
- if (!reader.IsDBNull(6))
+ if (!reader.IsDBNull(index))
{
- hasProgramAttributes.IsMovie = reader.GetBoolean(6);
+ hasProgramAttributes.IsMovie = reader.GetBoolean(index);
}
+ index++;
- if (!reader.IsDBNull(7))
+ if (!reader.IsDBNull(index))
{
- hasProgramAttributes.IsSports = reader.GetBoolean(7);
+ hasProgramAttributes.IsSports = reader.GetBoolean(index);
}
+ index++;
- if (!reader.IsDBNull(8))
+ if (!reader.IsDBNull(index))
{
- hasProgramAttributes.IsKids = reader.GetBoolean(8);
+ hasProgramAttributes.IsKids = reader.GetBoolean(index);
}
+ index++;
- if (!reader.IsDBNull(9))
+ if (!reader.IsDBNull(index))
{
- hasProgramAttributes.IsSeries = reader.GetBoolean(9);
+ hasProgramAttributes.IsSeries = reader.GetBoolean(index);
}
+ index++;
- if (!reader.IsDBNull(10))
+ if (!reader.IsDBNull(index))
{
- hasProgramAttributes.IsLive = reader.GetBoolean(10);
+ hasProgramAttributes.IsLive = reader.GetBoolean(index);
}
+ index++;
- if (!reader.IsDBNull(11))
+ if (!reader.IsDBNull(index))
{
- hasProgramAttributes.IsNews = reader.GetBoolean(11);
+ hasProgramAttributes.IsNews = reader.GetBoolean(index);
}
+ index++;
- if (!reader.IsDBNull(12))
+ if (!reader.IsDBNull(index))
{
- hasProgramAttributes.IsPremiere = reader.GetBoolean(12);
+ hasProgramAttributes.IsPremiere = reader.GetBoolean(index);
}
+ index++;
- if (!reader.IsDBNull(13))
+ if (!reader.IsDBNull(index))
{
- hasProgramAttributes.EpisodeTitle = reader.GetString(13);
+ hasProgramAttributes.EpisodeTitle = reader.GetString(index);
}
+ index++;
- if (!reader.IsDBNull(14))
+ if (!reader.IsDBNull(index))
{
- hasProgramAttributes.IsRepeat = reader.GetBoolean(14);
+ hasProgramAttributes.IsRepeat = reader.GetBoolean(index);
}
+ index++;
+ }
+ else
+ {
+ index += 9;
}
-
- var index = 15;
if (!reader.IsDBNull(index))
{
@@ -1494,7 +1564,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (!reader.IsDBNull(index))
{
- item.DateLastRefreshed = reader.GetDateTime(index).ToUniversalTime();
+ item.DateLastRefreshed = reader[index].ReadDateTime();
}
index++;
@@ -1512,7 +1582,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (!reader.IsDBNull(index))
{
- item.PremiereDate = reader.GetDateTime(index).ToUniversalTime();
+ item.PremiereDate = reader[index].ReadDateTime();
}
index++;
@@ -1598,14 +1668,14 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
if (!reader.IsDBNull(index))
{
- item.DateCreated = reader.GetDateTime(index).ToUniversalTime();
+ item.DateCreated = reader[index].ReadDateTime();
}
index++;
}
if (!reader.IsDBNull(index))
{
- item.DateModified = reader.GetDateTime(index).ToUniversalTime();
+ item.DateModified = reader[index].ReadDateTime();
}
index++;
@@ -1649,7 +1719,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (!reader.IsDBNull(index))
{
- item.DateLastSaved = reader.GetDateTime(index).ToUniversalTime();
+ item.DateLastSaved = reader[index].ReadDateTime();
}
index++;
@@ -1720,7 +1790,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
var folder = item as Folder;
if (folder != null && !reader.IsDBNull(index))
{
- folder.DateLastMediaAdded = reader.GetDateTime(index).ToUniversalTime();
+ folder.DateLastMediaAdded = reader[index].ReadDateTime();
}
index++;
}
@@ -1892,6 +1962,47 @@ namespace MediaBrowser.Server.Implementations.Persistence
index++;
}
+ if (!reader.IsDBNull(index))
+ {
+ item.TotalBitrate = reader.GetInt32(index);
+ }
+ index++;
+
+ if (!reader.IsDBNull(index))
+ {
+ item.ExtraType = (ExtraType)Enum.Parse(typeof(ExtraType), reader.GetString(index), true);
+ }
+ index++;
+
+ var hasArtists = item as IHasArtist;
+ if (hasArtists != null && !reader.IsDBNull(index))
+ {
+ hasArtists.Artists = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+ }
+ index++;
+
+ var hasAlbumArtists = item as IHasAlbumArtist;
+ if (hasAlbumArtists != null && !reader.IsDBNull(index))
+ {
+ hasAlbumArtists.AlbumArtists = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+ }
+ index++;
+
+ if (!reader.IsDBNull(index))
+ {
+ item.ExternalId = reader.GetString(index);
+ }
+ index++;
+
+ if (hasSeries != null)
+ {
+ if (!reader.IsDBNull(index))
+ {
+ hasSeries.SeriesPresentationUniqueKey = reader.GetString(index);
+ }
+ }
+ index++;
+
if (string.IsNullOrWhiteSpace(item.Tagline))
{
var movie = item as Movie;
@@ -1926,11 +2037,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
return _jsonSerializer.DeserializeFromFile<List<ItemReview>>(path);
}
- catch (DirectoryNotFoundException)
+ catch (FileNotFoundException)
{
return new List<ItemReview>();
}
- catch (FileNotFoundException)
+ catch (IOException)
{
return new List<ItemReview>();
}
@@ -1945,7 +2056,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// <returns>Task.</returns>
public Task SaveCriticReviews(Guid itemId, IEnumerable<ItemReview> criticReviews)
{
- Directory.CreateDirectory(_criticReviewsPath);
+ _fileSystem.CreateDirectory(_criticReviewsPath);
var path = Path.Combine(_criticReviewsPath, itemId + ".json");
@@ -1967,24 +2078,26 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
throw new ArgumentNullException("id");
}
- var list = new List<ChapterInfo>();
- using (var cmd = _connection.CreateCommand())
+ using (WriteLock.Read())
{
- cmd.CommandText = "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId order by ChapterIndex asc";
-
- cmd.Parameters.Add(cmd, "@ItemId", DbType.Guid).Value = id;
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
+ using (var connection = CreateConnection(true))
{
- while (reader.Read())
+ var list = new List<ChapterInfo>();
+
+ using (var statement = PrepareStatementSafe(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId order by ChapterIndex asc"))
{
- list.Add(GetChapter(reader));
+ statement.TryBind("@ItemId", id);
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ list.Add(GetChapter(row));
+ }
}
+
+ return list;
}
}
-
- return list;
}
/// <summary>
@@ -2002,22 +2115,23 @@ namespace MediaBrowser.Server.Implementations.Persistence
throw new ArgumentNullException("id");
}
- using (var cmd = _connection.CreateCommand())
+ using (WriteLock.Read())
{
- cmd.CommandText = "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId and ChapterIndex=@ChapterIndex";
-
- cmd.Parameters.Add(cmd, "@ItemId", DbType.Guid).Value = id;
- cmd.Parameters.Add(cmd, "@ChapterIndex", DbType.Int32).Value = index;
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
+ using (var connection = CreateConnection(true))
{
- if (reader.Read())
+ using (var statement = PrepareStatementSafe(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId and ChapterIndex=@ChapterIndex"))
{
- return GetChapter(reader);
+ statement.TryBind("@ItemId", id);
+ statement.TryBind("@ChapterIndex", index);
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ return GetChapter(row);
+ }
}
}
- return null;
}
+ return null;
}
/// <summary>
@@ -2025,7 +2139,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// </summary>
/// <param name="reader">The reader.</param>
/// <returns>ChapterInfo.</returns>
- private ChapterInfo GetChapter(IDataReader reader)
+ private ChapterInfo GetChapter(IReadOnlyList<IResultSetValue> reader)
{
var chapter = new ChapterInfo
{
@@ -2044,7 +2158,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (!reader.IsDBNull(3))
{
- chapter.ImageDateModified = reader.GetDateTime(3).ToUniversalTime();
+ chapter.ImageDateModified = reader[3].ReadDateTime();
}
return chapter;
@@ -2080,85 +2194,40 @@ namespace MediaBrowser.Server.Implementations.Persistence
cancellationToken.ThrowIfCancellationRequested();
- await WriteLock.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- IDbTransaction transaction = null;
-
- try
- {
- transaction = _connection.BeginTransaction();
-
- // First delete chapters
- _deleteChaptersCommand.GetParameter(0).Value = id;
-
- _deleteChaptersCommand.Transaction = transaction;
-
- _deleteChaptersCommand.ExecuteNonQuery();
-
- var index = 0;
-
- foreach (var chapter in chapters)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- _saveChapterCommand.GetParameter(0).Value = id;
- _saveChapterCommand.GetParameter(1).Value = index;
- _saveChapterCommand.GetParameter(2).Value = chapter.StartPositionTicks;
- _saveChapterCommand.GetParameter(3).Value = chapter.Name;
- _saveChapterCommand.GetParameter(4).Value = chapter.ImagePath;
- _saveChapterCommand.GetParameter(5).Value = chapter.ImageDateModified;
-
- _saveChapterCommand.Transaction = transaction;
-
- _saveChapterCommand.ExecuteNonQuery();
-
- index++;
- }
+ var index = 0;
- transaction.Commit();
- }
- catch (OperationCanceledException)
+ using (WriteLock.Write())
{
- if (transaction != null)
+ using (var connection = CreateConnection())
{
- transaction.Rollback();
- }
+ connection.RunInTransaction(db =>
+ {
+ // First delete chapters
+ db.Execute("delete from " + ChaptersTableName + " where ItemId=@ItemId", id.ToGuidParamValue());
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to save chapters:", e);
+ 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();
+ }
- if (transaction != null)
- {
- transaction.Rollback();
- }
+ saveChapterStatement.TryBind("@ItemId", id.ToGuidParamValue());
+ saveChapterStatement.TryBind("@ChapterIndex", index);
+ saveChapterStatement.TryBind("@StartPositionTicks", chapter.StartPositionTicks);
+ saveChapterStatement.TryBind("@Name", chapter.Name);
+ saveChapterStatement.TryBind("@ImagePath", chapter.ImagePath);
+ saveChapterStatement.TryBind("@ImageDateModified", chapter.ImageDateModified);
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
- }
-
- WriteLock.Release();
- }
- }
+ saveChapterStatement.MoveNext();
- protected override void CloseConnection()
- {
- if (_connection != null)
- {
- if (_connection.IsOpen())
- {
- _connection.Close();
+ index++;
+ }
+ }
+ }, TransactionMode);
}
-
- _connection.Dispose();
- _connection = null;
}
}
@@ -2171,7 +2240,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (query.SimilarTo != null && query.User != null)
{
- return true;
+ //return true;
}
var sortingFields = query.SortBy.ToList();
@@ -2197,6 +2266,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
return true;
}
+ if (sortingFields.Contains(ItemSortBy.SeriesDatePlayed, StringComparer.OrdinalIgnoreCase))
+ {
+ return true;
+ }
if (query.IsFavoriteOrLiked.HasValue)
{
@@ -2252,7 +2325,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
return new[] { field.ToString() };
}
- private string[] GetFinalColumnsToSelect(InternalItemsQuery query, string[] startColumns, IDbCommand cmd)
+ private string[] GetFinalColumnsToSelect(InternalItemsQuery query, string[] startColumns)
{
var list = startColumns.ToList();
@@ -2274,13 +2347,13 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (EnableJoinUserData(query))
{
- list.Add("UserDataDb.UserData.UserId");
- list.Add("UserDataDb.UserData.lastPlayedDate");
- list.Add("UserDataDb.UserData.playbackPositionTicks");
- list.Add("UserDataDb.UserData.playcount");
- list.Add("UserDataDb.UserData.isFavorite");
- list.Add("UserDataDb.UserData.played");
- list.Add("UserDataDb.UserData.rating");
+ 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");
}
if (query.SimilarTo != null)
@@ -2296,15 +2369,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
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 )");
- //// genres
- builder.Append("+ ((Select count(CleanValue) from ItemValues where ItemId=Guid and Type=2 and CleanValue in (select CleanValue from itemvalues where ItemId=@SimilarItemId and type=2)) * 10)");
+ //// 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)");
- //// tags
- builder.Append("+ ((Select count(CleanValue) from ItemValues where ItemId=Guid and Type=4 and CleanValue in (select CleanValue from itemvalues where ItemId=@SimilarItemId and type=4)) * 10)");
-
- builder.Append("+ ((Select count(CleanValue) from ItemValues where ItemId=Guid and Type=5 and CleanValue in (select CleanValue from itemvalues where ItemId=@SimilarItemId and type=5)) * 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)");
+ //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)");
//builder.Append("+ ((Select count(Name) from People where ItemId=Guid and Name in (select Name from People where ItemId=@SimilarItemId)) * 3)");
@@ -2313,9 +2381,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
builder.Append(") as SimilarityScore");
list.Add(builder.ToString());
- cmd.Parameters.Add(cmd, "@ItemOfficialRating", DbType.String).Value = item.OfficialRating;
- cmd.Parameters.Add(cmd, "@ItemProductionYear", DbType.Int32).Value = item.ProductionYear ?? 0;
- cmd.Parameters.Add(cmd, "@SimilarItemId", DbType.Guid).Value = item.Id;
var excludeIds = query.ExcludeItemIds.ToList();
excludeIds.Add(item.Id.ToString("N"));
@@ -2327,19 +2392,28 @@ namespace MediaBrowser.Server.Implementations.Persistence
return list.ToArray();
}
- private string GetJoinUserDataText(InternalItemsQuery query)
+ private void BindSimilarParams(InternalItemsQuery query, IStatement statement)
{
- if (!EnableJoinUserData(query))
+ var item = query.SimilarTo;
+
+ if (item == null)
{
- return string.Empty;
+ return;
}
- if (_config.Configuration.SchemaVersion >= 96)
+ statement.TryBind("@ItemOfficialRating", item.OfficialRating);
+ statement.TryBind("@ItemProductionYear", item.ProductionYear ?? 0);
+ statement.TryBind("@SimilarItemId", item.Id);
+ }
+
+ private string GetJoinUserDataText(InternalItemsQuery query)
+ {
+ if (!EnableJoinUserData(query))
{
- return " left join UserDataDb.UserData on UserDataKey=UserDataDb.UserData.Key And (UserId=@UserId)";
+ return string.Empty;
}
- return " left join UserDataDb.UserData on (select UserDataKey from UserDataKeys where ItemId=Guid order by Priority LIMIT 1)=UserDataDb.UserData.Key And (UserId=@UserId)";
+ return " left join UserData on UserDataKey=UserData.Key And (UserId=@UserId)";
}
private string GetGroupBy(InternalItemsQuery query)
@@ -2364,7 +2438,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
return " from TypedBaseItems " + alias;
}
- public List<BaseItem> GetItemList(InternalItemsQuery query)
+ public int GetCount(InternalItemsQuery query)
{
if (query == null)
{
@@ -2373,9 +2447,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
CheckDisposed();
- var now = DateTime.UtcNow;
+ //Logger.Info("GetItemList: " + _environmentInfo.StackTrace);
- var list = new List<BaseItem>();
+ var now = DateTime.UtcNow;
// Hack for right now since we currently don't support filtering out these duplicates within a query
if (query.Limit.HasValue && query.EnableGroupByMetadataKey)
@@ -2383,79 +2457,150 @@ namespace MediaBrowser.Server.Implementations.Persistence
query.Limit = query.Limit.Value + 4;
}
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, _retriveItemColumns, cmd)) + GetFromText();
- cmd.CommandText += GetJoinUserDataText(query);
+ var commandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, new [] { "count(distinct PresentationUniqueKey)" })) + GetFromText();
+ commandText += GetJoinUserDataText(query);
+
+ var whereClauses = GetWhereClauses(query, null);
+
+ var whereText = whereClauses.Count == 0 ?
+ string.Empty :
+ " where " + string.Join(" AND ", whereClauses.ToArray());
+
+ commandText += whereText;
- if (EnableJoinUserData(query))
+ //commandText += GetGroupBy(query);
+
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
{
- cmd.Parameters.Add(cmd, "@UserId", DbType.Guid).Value = query.User.Id;
+ using (var statement = PrepareStatementSafe(connection, commandText))
+ {
+ if (EnableJoinUserData(query))
+ {
+ statement.TryBind("@UserId", query.User.Id);
+ }
+
+ BindSimilarParams(query, statement);
+
+ // Running this again will bind the params
+ GetWhereClauses(query, statement);
+
+ var count = statement.ExecuteQuery().SelectScalarInt().First();
+ LogQueryTime("GetCount", commandText, now);
+ return count;
+ }
}
- var whereClauses = GetWhereClauses(query, cmd);
+ }
+ }
- var whereText = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
+ public List<BaseItem> GetItemList(InternalItemsQuery query)
+ {
+ if (query == null)
+ {
+ throw new ArgumentNullException("query");
+ }
- cmd.CommandText += whereText;
+ CheckDisposed();
- cmd.CommandText += GetGroupBy(query);
+ //Logger.Info("GetItemList: " + _environmentInfo.StackTrace);
- cmd.CommandText += GetOrderByText(query);
+ var now = DateTime.UtcNow;
- if (query.Limit.HasValue || query.StartIndex.HasValue)
- {
- var offset = query.StartIndex ?? 0;
+ // Hack for right now since we currently don't support filtering out these duplicates within a query
+ if (query.Limit.HasValue && query.EnableGroupByMetadataKey)
+ {
+ query.Limit = query.Limit.Value + 4;
+ }
- if (query.Limit.HasValue || offset > 0)
- {
- cmd.CommandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture);
- }
+ var commandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, _retriveItemColumns)) + GetFromText();
+ commandText += GetJoinUserDataText(query);
- if (offset > 0)
- {
- cmd.CommandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture);
- }
+ var whereClauses = GetWhereClauses(query, null);
+
+ var whereText = whereClauses.Count == 0 ?
+ string.Empty :
+ " where " + string.Join(" AND ", whereClauses.ToArray());
+
+ commandText += whereText;
+
+ commandText += GetGroupBy(query);
+
+ commandText += GetOrderByText(query);
+
+ 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);
}
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
+ if (offset > 0)
{
- LogQueryTime("GetItemList", cmd, now);
+ commandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture);
+ }
+ }
- while (reader.Read())
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
+ {
+ return connection.RunInTransaction(db =>
{
- var item = GetItem(reader, query);
- if (item != null)
+ var list = new List<BaseItem>();
+
+ using (var statement = PrepareStatementSafe(db, commandText))
{
- list.Add(item);
+ if (EnableJoinUserData(query))
+ {
+ statement.TryBind("@UserId", query.User.Id);
+ }
+
+ BindSimilarParams(query, statement);
+
+ // Running this again will bind the params
+ GetWhereClauses(query, statement);
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ var item = GetItem(row, query);
+ if (item != null)
+ {
+ list.Add(item);
+ }
+ }
}
- }
- }
- }
- // Hack for right now since we currently don't support filtering out these duplicates within a query
- if (query.EnableGroupByMetadataKey)
- {
- var limit = query.Limit ?? int.MaxValue;
- limit -= 4;
- var newList = new List<BaseItem>();
+ // Hack for right now since we currently don't support filtering out these duplicates within a query
+ if (query.EnableGroupByMetadataKey)
+ {
+ var limit = query.Limit ?? int.MaxValue;
+ limit -= 4;
+ var newList = new List<BaseItem>();
- foreach (var item in list)
- {
- AddItem(newList, item);
+ foreach (var item in list)
+ {
+ AddItem(newList, item);
- if (newList.Count >= limit)
- {
- break;
- }
- }
+ if (newList.Count >= limit)
+ {
+ break;
+ }
+ }
- list = newList;
- }
+ list = newList;
+ }
- return list;
+ LogQueryTime("GetItemList", commandText, now);
+
+ return list;
+
+ }, ReadTransactionMode);
+ }
+ }
}
private void AddItem(List<BaseItem> items, BaseItem newItem)
@@ -2486,21 +2631,21 @@ namespace MediaBrowser.Server.Implementations.Persistence
items.Add(newItem);
}
- private void LogQueryTime(string methodName, IDbCommand cmd, DateTime startDate)
+ private void LogQueryTime(string methodName, string commandText, DateTime startDate)
{
var elapsed = (DateTime.UtcNow - startDate).TotalMilliseconds;
var slowThreshold = 1000;
#if DEBUG
- slowThreshold = 50;
+ slowThreshold = 2;
#endif
if (elapsed >= slowThreshold)
{
Logger.Debug("{2} query time (slow): {0}ms. Query: {1}",
Convert.ToInt32(elapsed),
- cmd.CommandText,
+ commandText,
methodName);
}
else
@@ -2523,115 +2668,142 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (!query.EnableTotalRecordCount || (!query.Limit.HasValue && (query.StartIndex ?? 0) == 0))
{
- var list = GetItemList(query);
+ var returnList = GetItemList(query);
return new QueryResult<BaseItem>
{
- Items = list.ToArray(),
- TotalRecordCount = list.Count
+ Items = returnList.ToArray(),
+ TotalRecordCount = returnList.Count
};
}
+ //Logger.Info("GetItems: " + _environmentInfo.StackTrace);
var now = DateTime.UtcNow;
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, _retriveItemColumns, cmd)) + GetFromText();
- cmd.CommandText += GetJoinUserDataText(query);
+ var list = new List<BaseItem>();
- if (EnableJoinUserData(query))
- {
- cmd.Parameters.Add(cmd, "@UserId", DbType.Guid).Value = query.User.Id;
- }
+ // Hack for right now since we currently don't support filtering out these duplicates within a query
+ if (query.Limit.HasValue && query.EnableGroupByMetadataKey)
+ {
+ query.Limit = query.Limit.Value + 4;
+ }
- var whereClauses = GetWhereClauses(query, cmd);
+ var commandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, _retriveItemColumns)) + GetFromText();
+ commandText += GetJoinUserDataText(query);
- var whereTextWithoutPaging = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
+ var whereClauses = GetWhereClauses(query, null);
- var whereText = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
+ var whereText = whereClauses.Count == 0 ?
+ string.Empty :
+ " where " + string.Join(" AND ", whereClauses.ToArray());
- cmd.CommandText += whereText;
+ var whereTextWithoutPaging = whereText;
- cmd.CommandText += GetGroupBy(query);
+ commandText += whereText;
- cmd.CommandText += GetOrderByText(query);
+ commandText += GetGroupBy(query);
- if (query.Limit.HasValue || query.StartIndex.HasValue)
- {
- var offset = query.StartIndex ?? 0;
+ commandText += GetOrderByText(query);
- if (query.Limit.HasValue || offset > 0)
- {
- cmd.CommandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture);
- }
+ if (query.Limit.HasValue || query.StartIndex.HasValue)
+ {
+ var offset = query.StartIndex ?? 0;
- if (offset > 0)
- {
- cmd.CommandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture);
- }
+ if (query.Limit.HasValue || offset > 0)
+ {
+ commandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture);
}
- cmd.CommandText += ";";
-
- var isReturningZeroItems = query.Limit.HasValue && query.Limit <= 0;
-
- if (isReturningZeroItems)
+ if (offset > 0)
{
- cmd.CommandText = "";
+ commandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture);
}
+ }
+
+ var isReturningZeroItems = query.Limit.HasValue && query.Limit <= 0;
+
+ var statementTexts = new List<string>();
+ if (!isReturningZeroItems)
+ {
+ statementTexts.Add(commandText);
+ }
+ if (query.EnableTotalRecordCount)
+ {
+ commandText = string.Empty;
if (EnableGroupByPresentationUniqueKey(query))
{
- cmd.CommandText += " select count (distinct PresentationUniqueKey)" + GetFromText();
+ commandText += " select count (distinct PresentationUniqueKey)" + GetFromText();
}
else
{
- cmd.CommandText += " select count (guid)" + GetFromText();
+ commandText += " select count (guid)" + GetFromText();
}
- cmd.CommandText += GetJoinUserDataText(query);
- cmd.CommandText += whereTextWithoutPaging;
-
- var list = new List<BaseItem>();
- var count = 0;
+ commandText += GetJoinUserDataText(query);
+ commandText += whereTextWithoutPaging;
+ statementTexts.Add(commandText);
+ }
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
{
- LogQueryTime("GetItems", cmd, now);
-
- if (isReturningZeroItems)
+ return connection.RunInTransaction(db =>
{
- if (reader.Read())
- {
- count = reader.GetInt32(0);
- }
- }
- else
- {
- while (reader.Read())
+ var result = new QueryResult<BaseItem>();
+ var statements = PrepareAllSafe(db, statementTexts)
+ .ToList();
+
+ if (!isReturningZeroItems)
{
- var item = GetItem(reader, query);
- if (item != null)
+ using (var statement = statements[0])
{
- list.Add(item);
+ if (EnableJoinUserData(query))
+ {
+ statement.TryBind("@UserId", query.User.Id);
+ }
+
+ BindSimilarParams(query, statement);
+
+ // Running this again will bind the params
+ GetWhereClauses(query, statement);
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ var item = GetItem(row, query);
+ if (item != null)
+ {
+ list.Add(item);
+ }
+ }
}
}
- if (reader.NextResult() && reader.Read())
+ if (query.EnableTotalRecordCount)
{
- count = reader.GetInt32(0);
+ using (var statement = statements[statements.Count - 1])
+ {
+ if (EnableJoinUserData(query))
+ {
+ statement.TryBind("@UserId", query.User.Id);
+ }
+
+ BindSimilarParams(query, statement);
+
+ // Running this again will bind the params
+ GetWhereClauses(query, statement);
+
+ result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
+ }
}
- }
- }
- return new QueryResult<BaseItem>()
- {
- Items = list.ToArray(),
- TotalRecordCount = count
- };
+ LogQueryTime("GetItems", commandText, now);
+
+ result.Items = list.ToArray();
+ return result;
+
+ }, ReadTransactionMode);
+ }
}
}
@@ -2743,7 +2915,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
if (string.Equals(name, ItemSortBy.SeriesDatePlayed, StringComparison.OrdinalIgnoreCase))
{
- return new Tuple<string, bool>("(Select MAX(LastPlayedDate) from TypedBaseItems B" + GetJoinUserDataText(query) + " where B.Guid in (Select ItemId from AncestorIds where AncestorId in (select guid from typedbaseitems c where C.Type = 'MediaBrowser.Controller.Entities.TV.Series' And C.Guid in (Select AncestorId from AncestorIds where ItemId=A.Guid))))", false);
+ return new Tuple<string, bool>("(Select MAX(LastPlayedDate) from TypedBaseItems B" + GetJoinUserDataText(query) + " where Played=1 and B.SeriesPresentationUniqueKey=A.PresentationUniqueKey)", false);
}
return new Tuple<string, bool>(name, false);
@@ -2757,63 +2929,72 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
CheckDisposed();
+ //Logger.Info("GetItemIdsList: " + _environmentInfo.StackTrace);
var now = DateTime.UtcNow;
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "guid" }, cmd)) + GetFromText();
- cmd.CommandText += GetJoinUserDataText(query);
+ var commandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "guid" })) + GetFromText();
+ commandText += GetJoinUserDataText(query);
- if (EnableJoinUserData(query))
- {
- cmd.Parameters.Add(cmd, "@UserId", DbType.Guid).Value = query.User.Id;
- }
+ var whereClauses = GetWhereClauses(query, null);
- var whereClauses = GetWhereClauses(query, cmd);
+ var whereText = whereClauses.Count == 0 ?
+ string.Empty :
+ " where " + string.Join(" AND ", whereClauses.ToArray());
- var whereText = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
+ commandText += whereText;
- cmd.CommandText += whereText;
+ commandText += GetGroupBy(query);
- cmd.CommandText += GetGroupBy(query);
+ commandText += GetOrderByText(query);
- cmd.CommandText += GetOrderByText(query);
+ if (query.Limit.HasValue || query.StartIndex.HasValue)
+ {
+ var offset = query.StartIndex ?? 0;
- if (query.Limit.HasValue || query.StartIndex.HasValue)
+ if (query.Limit.HasValue || offset > 0)
{
- var offset = query.StartIndex ?? 0;
-
- if (query.Limit.HasValue || offset > 0)
- {
- cmd.CommandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture);
- }
-
- if (offset > 0)
- {
- cmd.CommandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture);
- }
+ commandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture);
}
- var list = new List<Guid>();
+ if (offset > 0)
+ {
+ commandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture);
+ }
+ }
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
{
- LogQueryTime("GetItemIdsList", cmd, now);
+ var list = new List<Guid>();
- while (reader.Read())
+ using (var statement = PrepareStatementSafe(connection, commandText))
{
- list.Add(reader.GetGuid(0));
+ if (EnableJoinUserData(query))
+ {
+ statement.TryBind("@UserId", query.User.Id);
+ }
+
+ BindSimilarParams(query, statement);
+
+ // Running this again will bind the params
+ GetWhereClauses(query, statement);
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ list.Add(row[0].ReadGuid());
+ }
}
- }
- return list;
+ LogQueryTime("GetItemList", commandText, now);
+
+ return list;
+ }
}
}
- public QueryResult<Tuple<Guid, string>> GetItemIdsWithPath(InternalItemsQuery query)
+ public List<Tuple<Guid, string>> GetItemIdsWithPath(InternalItemsQuery query)
{
if (query == null)
{
@@ -2822,73 +3003,70 @@ namespace MediaBrowser.Server.Implementations.Persistence
CheckDisposed();
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "select guid,path from TypedBaseItems";
+ var now = DateTime.UtcNow;
- var whereClauses = GetWhereClauses(query, cmd);
+ var commandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "guid", "path" })) + GetFromText();
- var whereTextWithoutPaging = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
+ var whereClauses = GetWhereClauses(query, null);
- var whereText = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
-
- cmd.CommandText += whereText;
+ var whereText = whereClauses.Count == 0 ?
+ string.Empty :
+ " where " + string.Join(" AND ", whereClauses.ToArray());
- cmd.CommandText += GetGroupBy(query);
+ commandText += whereText;
- cmd.CommandText += GetOrderByText(query);
+ commandText += GetGroupBy(query);
- if (query.Limit.HasValue || query.StartIndex.HasValue)
- {
- var offset = query.StartIndex ?? 0;
+ commandText += GetOrderByText(query);
- if (query.Limit.HasValue || offset > 0)
- {
- cmd.CommandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture);
- }
+ if (query.Limit.HasValue || query.StartIndex.HasValue)
+ {
+ var offset = query.StartIndex ?? 0;
- if (offset > 0)
- {
- cmd.CommandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture);
- }
+ if (query.Limit.HasValue || offset > 0)
+ {
+ commandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture);
}
- cmd.CommandText += "; select count (guid) from TypedBaseItems" + whereTextWithoutPaging;
-
- var list = new List<Tuple<Guid, string>>();
- var count = 0;
+ if (offset > 0)
+ {
+ commandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture);
+ }
+ }
- Logger.Debug(cmd.CommandText);
+ var list = new List<Tuple<Guid, string>>();
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
{
- while (reader.Read())
+ using (var statement = PrepareStatementSafe(connection, commandText))
{
- var id = reader.GetGuid(0);
- string path = null;
-
- if (!reader.IsDBNull(1))
+ if (EnableJoinUserData(query))
{
- path = reader.GetString(1);
+ statement.TryBind("@UserId", query.User.Id);
}
- list.Add(new Tuple<Guid, string>(id, path));
- }
- if (reader.NextResult() && reader.Read())
- {
- count = reader.GetInt32(0);
+ // Running this again will bind the params
+ GetWhereClauses(query, statement);
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ var id = row.GetGuid(0);
+ string path = null;
+
+ if (!row.IsDBNull(1))
+ {
+ path = row.GetString(1);
+ }
+ list.Add(new Tuple<Guid, string>(id, path));
+ }
}
}
- return new QueryResult<Tuple<Guid, string>>()
- {
- Items = list.ToArray(),
- TotalRecordCount = count
- };
+ LogQueryTime("GetItemIdsWithPath", commandText, now);
+
+ return list;
}
}
@@ -2903,92 +3081,136 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (!query.EnableTotalRecordCount || (!query.Limit.HasValue && (query.StartIndex ?? 0) == 0))
{
- var list = GetItemIdsList(query);
+ var returnList = GetItemIdsList(query);
return new QueryResult<Guid>
{
- Items = list.ToArray(),
- TotalRecordCount = list.Count
+ Items = returnList.ToArray(),
+ TotalRecordCount = returnList.Count
};
}
+ //Logger.Info("GetItemIds: " + _environmentInfo.StackTrace);
var now = DateTime.UtcNow;
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "guid" }, cmd)) + GetFromText();
+ var commandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "guid" })) + GetFromText();
+ commandText += GetJoinUserDataText(query);
- var whereClauses = GetWhereClauses(query, cmd);
- cmd.CommandText += GetJoinUserDataText(query);
+ var whereClauses = GetWhereClauses(query, null);
- if (EnableJoinUserData(query))
- {
- cmd.Parameters.Add(cmd, "@UserId", DbType.Guid).Value = query.User.Id;
- }
+ var whereText = whereClauses.Count == 0 ?
+ string.Empty :
+ " where " + string.Join(" AND ", whereClauses.ToArray());
- var whereText = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
+ var whereTextWithoutPaging = whereText;
- cmd.CommandText += whereText;
+ commandText += whereText;
- cmd.CommandText += GetGroupBy(query);
+ commandText += GetGroupBy(query);
- cmd.CommandText += GetOrderByText(query);
+ commandText += GetOrderByText(query);
- if (query.Limit.HasValue || query.StartIndex.HasValue)
- {
- var offset = query.StartIndex ?? 0;
+ if (query.Limit.HasValue || query.StartIndex.HasValue)
+ {
+ var offset = query.StartIndex ?? 0;
- if (query.Limit.HasValue || offset > 0)
- {
- cmd.CommandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture);
- }
+ if (query.Limit.HasValue || offset > 0)
+ {
+ commandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture);
+ }
- if (offset > 0)
- {
- cmd.CommandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture);
- }
+ if (offset > 0)
+ {
+ commandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture);
}
+ }
+
+ var list = new List<Guid>();
+ var isReturningZeroItems = query.Limit.HasValue && query.Limit <= 0;
+
+ var statementTexts = new List<string>();
+ if (!isReturningZeroItems)
+ {
+ statementTexts.Add(commandText);
+ }
+ if (query.EnableTotalRecordCount)
+ {
+ commandText = string.Empty;
if (EnableGroupByPresentationUniqueKey(query))
{
- cmd.CommandText += "; select count (distinct PresentationUniqueKey)" + GetFromText();
+ commandText += " select count (distinct PresentationUniqueKey)" + GetFromText();
}
else
{
- cmd.CommandText += "; select count (guid)" + GetFromText();
+ commandText += " select count (guid)" + GetFromText();
}
- cmd.CommandText += GetJoinUserDataText(query);
- cmd.CommandText += whereText;
-
- var list = new List<Guid>();
- var count = 0;
+ commandText += GetJoinUserDataText(query);
+ commandText += whereTextWithoutPaging;
+ statementTexts.Add(commandText);
+ }
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
{
- LogQueryTime("GetItemIds", cmd, now);
-
- while (reader.Read())
+ return connection.RunInTransaction(db =>
{
- list.Add(reader.GetGuid(0));
- }
+ var result = new QueryResult<Guid>();
- if (reader.NextResult() && reader.Read())
- {
- count = reader.GetInt32(0);
- }
- }
+ var statements = PrepareAllSafe(db, statementTexts)
+ .ToList();
- return new QueryResult<Guid>()
- {
- Items = list.ToArray(),
- TotalRecordCount = count
- };
+ if (!isReturningZeroItems)
+ {
+ using (var statement = statements[0])
+ {
+ if (EnableJoinUserData(query))
+ {
+ statement.TryBind("@UserId", query.User.Id);
+ }
+
+ BindSimilarParams(query, statement);
+
+ // Running this again will bind the params
+ GetWhereClauses(query, statement);
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ list.Add(row[0].ReadGuid());
+ }
+ }
+ }
+
+ if (query.EnableTotalRecordCount)
+ {
+ using (var statement = statements[statements.Count - 1])
+ {
+ if (EnableJoinUserData(query))
+ {
+ statement.TryBind("@UserId", query.User.Id);
+ }
+
+ BindSimilarParams(query, statement);
+
+ // Running this again will bind the params
+ GetWhereClauses(query, statement);
+
+ result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
+ }
+ }
+
+ LogQueryTime("GetItemIds", commandText, now);
+
+ result.Items = list.ToArray();
+ return result;
+
+ }, ReadTransactionMode);
+ }
}
}
- private List<string> GetWhereClauses(InternalItemsQuery query, IDbCommand cmd, string paramSuffix = "")
+ private List<string> GetWhereClauses(InternalItemsQuery query, IStatement statement, string paramSuffix = "")
{
var whereClauses = new List<string>();
@@ -2996,32 +3218,21 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
//whereClauses.Add("(UserId is null or UserId=@UserId)");
}
- if (query.IsCurrentSchema.HasValue)
- {
- if (query.IsCurrentSchema.Value)
- {
- whereClauses.Add("(SchemaVersion not null AND SchemaVersion=@SchemaVersion)");
- }
- else
- {
- whereClauses.Add("(SchemaVersion is null or SchemaVersion<>@SchemaVersion)");
- }
- cmd.Parameters.Add(cmd, "@SchemaVersion", DbType.Int32).Value = LatestSchemaVersion;
- }
if (query.IsHD.HasValue)
{
whereClauses.Add("IsHD=@IsHD");
- cmd.Parameters.Add(cmd, "@IsHD", DbType.Boolean).Value = query.IsHD;
+ if (statement != null)
+ {
+ statement.TryBind("@IsHD", query.IsHD);
+ }
}
if (query.IsLocked.HasValue)
{
whereClauses.Add("IsLocked=@IsLocked");
- cmd.Parameters.Add(cmd, "@IsLocked", DbType.Boolean).Value = query.IsLocked;
- }
- if (query.IsOffline.HasValue)
- {
- whereClauses.Add("IsOffline=@IsOffline");
- cmd.Parameters.Add(cmd, "@IsOffline", DbType.Boolean).Value = query.IsOffline;
+ if (statement != null)
+ {
+ statement.TryBind("@IsLocked", query.IsLocked);
+ }
}
var exclusiveProgramAttribtues = !(query.IsMovie ?? true) ||
@@ -3047,33 +3258,51 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (alternateTypes.Count == 0)
{
whereClauses.Add("IsMovie=@IsMovie");
- cmd.Parameters.Add(cmd, "@IsMovie", DbType.Boolean).Value = query.IsMovie;
+ if (statement != null)
+ {
+ statement.TryBind("@IsMovie", query.IsMovie);
+ }
}
else
{
whereClauses.Add("(IsMovie is null OR IsMovie=@IsMovie)");
- cmd.Parameters.Add(cmd, "@IsMovie", DbType.Boolean).Value = query.IsMovie;
+ if (statement != null)
+ {
+ statement.TryBind("@IsMovie", query.IsMovie);
+ }
}
}
if (query.IsSeries.HasValue)
{
whereClauses.Add("IsSeries=@IsSeries");
- cmd.Parameters.Add(cmd, "@IsSeries", DbType.Boolean).Value = query.IsSeries;
+ if (statement != null)
+ {
+ statement.TryBind("@IsSeries", query.IsSeries);
+ }
}
if (query.IsNews.HasValue)
{
whereClauses.Add("IsNews=@IsNews");
- cmd.Parameters.Add(cmd, "@IsNews", DbType.Boolean).Value = query.IsNews;
+ if (statement != null)
+ {
+ statement.TryBind("@IsNews", query.IsNews);
+ }
}
if (query.IsKids.HasValue)
{
whereClauses.Add("IsKids=@IsKids");
- cmd.Parameters.Add(cmd, "@IsKids", DbType.Boolean).Value = query.IsKids;
+ if (statement != null)
+ {
+ statement.TryBind("@IsKids", query.IsKids);
+ }
}
if (query.IsSports.HasValue)
{
whereClauses.Add("IsSports=@IsSports");
- cmd.Parameters.Add(cmd, "@IsSports", DbType.Boolean).Value = query.IsSports;
+ if (statement != null)
+ {
+ statement.TryBind("@IsSports", query.IsSports);
+ }
}
}
else
@@ -3100,27 +3329,42 @@ namespace MediaBrowser.Server.Implementations.Persistence
programAttribtues.Add("(IsMovie is null OR IsMovie=@IsMovie)");
}
- cmd.Parameters.Add(cmd, "@IsMovie", DbType.Boolean).Value = true;
+ if (statement != null)
+ {
+ statement.TryBind("@IsMovie", true);
+ }
}
if (query.IsSports ?? false)
{
programAttribtues.Add("IsSports=@IsSports");
- cmd.Parameters.Add(cmd, "@IsSports", DbType.Boolean).Value = true;
+ if (statement != null)
+ {
+ statement.TryBind("@IsSports", query.IsSports);
+ }
}
if (query.IsNews ?? false)
{
programAttribtues.Add("IsNews=@IsNews");
- cmd.Parameters.Add(cmd, "@IsNews", DbType.Boolean).Value = true;
+ if (statement != null)
+ {
+ statement.TryBind("@IsNews", query.IsNews);
+ }
}
if (query.IsSeries ?? false)
{
programAttribtues.Add("IsSeries=@IsSeries");
- cmd.Parameters.Add(cmd, "@IsSeries", DbType.Boolean).Value = true;
+ if (statement != null)
+ {
+ statement.TryBind("@IsSeries", query.IsSeries);
+ }
}
if (query.IsKids ?? false)
{
programAttribtues.Add("IsKids=@IsKids");
- cmd.Parameters.Add(cmd, "@IsKids", DbType.Boolean).Value = true;
+ if (statement != null)
+ {
+ statement.TryBind("@IsKids", query.IsKids);
+ }
}
if (programAttribtues.Count > 0)
{
@@ -3136,14 +3380,20 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (query.IsFolder.HasValue)
{
whereClauses.Add("IsFolder=@IsFolder");
- cmd.Parameters.Add(cmd, "@IsFolder", DbType.Boolean).Value = query.IsFolder;
+ if (statement != null)
+ {
+ statement.TryBind("@IsFolder", query.IsFolder);
+ }
}
var includeTypes = query.IncludeItemTypes.SelectMany(MapIncludeItemTypes).ToArray();
if (includeTypes.Length == 1)
{
whereClauses.Add("type=@type" + paramSuffix);
- cmd.Parameters.Add(cmd, "@type" + paramSuffix, DbType.String).Value = includeTypes[0];
+ if (statement != null)
+ {
+ statement.TryBind("@type" + paramSuffix, includeTypes[0]);
+ }
}
else if (includeTypes.Length > 1)
{
@@ -3155,7 +3405,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (excludeTypes.Length == 1)
{
whereClauses.Add("type<>@type");
- cmd.Parameters.Add(cmd, "@type", DbType.String).Value = excludeTypes[0];
+ if (statement != null)
+ {
+ statement.TryBind("@type", excludeTypes[0]);
+ }
}
else if (excludeTypes.Length > 1)
{
@@ -3166,7 +3419,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (query.ChannelIds.Length == 1)
{
whereClauses.Add("ChannelId=@ChannelId");
- cmd.Parameters.Add(cmd, "@ChannelId", DbType.String).Value = query.ChannelIds[0];
+ if (statement != null)
+ {
+ statement.TryBind("@ChannelId", query.ChannelIds[0]);
+ }
}
if (query.ChannelIds.Length > 1)
{
@@ -3177,43 +3433,64 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (query.ParentId.HasValue)
{
whereClauses.Add("ParentId=@ParentId");
- cmd.Parameters.Add(cmd, "@ParentId", DbType.Guid).Value = query.ParentId.Value;
+ if (statement != null)
+ {
+ statement.TryBind("@ParentId", query.ParentId.Value);
+ }
}
if (!string.IsNullOrWhiteSpace(query.Path))
{
whereClauses.Add("Path=@Path");
- cmd.Parameters.Add(cmd, "@Path", DbType.String).Value = query.Path;
+ if (statement != null)
+ {
+ statement.TryBind("@Path", query.Path);
+ }
}
if (!string.IsNullOrWhiteSpace(query.PresentationUniqueKey))
{
whereClauses.Add("PresentationUniqueKey=@PresentationUniqueKey");
- cmd.Parameters.Add(cmd, "@PresentationUniqueKey", DbType.String).Value = query.PresentationUniqueKey;
+ if (statement != null)
+ {
+ statement.TryBind("@PresentationUniqueKey", query.PresentationUniqueKey);
+ }
}
if (query.MinCommunityRating.HasValue)
{
whereClauses.Add("CommunityRating>=@MinCommunityRating");
- cmd.Parameters.Add(cmd, "@MinCommunityRating", DbType.Double).Value = query.MinCommunityRating.Value;
+ if (statement != null)
+ {
+ statement.TryBind("@MinCommunityRating", query.MinCommunityRating.Value);
+ }
}
if (query.MinIndexNumber.HasValue)
{
whereClauses.Add("IndexNumber>=@MinIndexNumber");
- cmd.Parameters.Add(cmd, "@MinIndexNumber", DbType.Int32).Value = query.MinIndexNumber.Value;
+ if (statement != null)
+ {
+ statement.TryBind("@MinIndexNumber", query.MinIndexNumber.Value);
+ }
}
if (query.MinDateCreated.HasValue)
{
whereClauses.Add("DateCreated>=@MinDateCreated");
- cmd.Parameters.Add(cmd, "@MinDateCreated", DbType.DateTime).Value = query.MinDateCreated.Value;
+ if (statement != null)
+ {
+ statement.TryBind("@MinDateCreated", query.MinDateCreated.Value);
+ }
}
if (query.MinDateLastSaved.HasValue)
{
whereClauses.Add("DateLastSaved>=@MinDateLastSaved");
- cmd.Parameters.Add(cmd, "@MinDateLastSaved", DbType.DateTime).Value = query.MinDateLastSaved.Value;
+ if (statement != null)
+ {
+ statement.TryBind("@MinDateLastSaved", query.MinDateLastSaved.Value);
+ }
}
//if (query.MinPlayers.HasValue)
@@ -3231,57 +3508,87 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (query.IndexNumber.HasValue)
{
whereClauses.Add("IndexNumber=@IndexNumber");
- cmd.Parameters.Add(cmd, "@IndexNumber", DbType.Int32).Value = query.IndexNumber.Value;
+ if (statement != null)
+ {
+ statement.TryBind("@IndexNumber", query.IndexNumber.Value);
+ }
}
if (query.ParentIndexNumber.HasValue)
{
whereClauses.Add("ParentIndexNumber=@ParentIndexNumber");
- cmd.Parameters.Add(cmd, "@ParentIndexNumber", DbType.Int32).Value = query.ParentIndexNumber.Value;
+ if (statement != null)
+ {
+ statement.TryBind("@ParentIndexNumber", query.ParentIndexNumber.Value);
+ }
}
if (query.ParentIndexNumberNotEquals.HasValue)
{
whereClauses.Add("(ParentIndexNumber<>@ParentIndexNumberNotEquals or ParentIndexNumber is null)");
- cmd.Parameters.Add(cmd, "@ParentIndexNumberNotEquals", DbType.Int32).Value = query.ParentIndexNumberNotEquals.Value;
+ if (statement != null)
+ {
+ statement.TryBind("@ParentIndexNumberNotEquals", query.ParentIndexNumberNotEquals.Value);
+ }
}
if (query.MinEndDate.HasValue)
{
whereClauses.Add("EndDate>=@MinEndDate");
- cmd.Parameters.Add(cmd, "@MinEndDate", DbType.Date).Value = query.MinEndDate.Value;
+ if (statement != null)
+ {
+ statement.TryBind("@MinEndDate", query.MinEndDate.Value);
+ }
}
if (query.MaxEndDate.HasValue)
{
whereClauses.Add("EndDate<=@MaxEndDate");
- cmd.Parameters.Add(cmd, "@MaxEndDate", DbType.Date).Value = query.MaxEndDate.Value;
+ if (statement != null)
+ {
+ statement.TryBind("@MaxEndDate", query.MaxEndDate.Value);
+ }
}
if (query.MinStartDate.HasValue)
{
whereClauses.Add("StartDate>=@MinStartDate");
- cmd.Parameters.Add(cmd, "@MinStartDate", DbType.Date).Value = query.MinStartDate.Value;
+ if (statement != null)
+ {
+ statement.TryBind("@MinStartDate", query.MinStartDate.Value);
+ }
}
if (query.MaxStartDate.HasValue)
{
whereClauses.Add("StartDate<=@MaxStartDate");
- cmd.Parameters.Add(cmd, "@MaxStartDate", DbType.Date).Value = query.MaxStartDate.Value;
+ if (statement != null)
+ {
+ statement.TryBind("@MaxStartDate", query.MaxStartDate.Value);
+ }
}
if (query.MinPremiereDate.HasValue)
{
whereClauses.Add("PremiereDate>=@MinPremiereDate");
- cmd.Parameters.Add(cmd, "@MinPremiereDate", DbType.Date).Value = query.MinPremiereDate.Value;
+ if (statement != null)
+ {
+ statement.TryBind("@MinPremiereDate", query.MinPremiereDate.Value);
+ }
}
if (query.MaxPremiereDate.HasValue)
{
whereClauses.Add("PremiereDate<=@MaxPremiereDate");
- cmd.Parameters.Add(cmd, "@MaxPremiereDate", DbType.Date).Value = query.MaxPremiereDate.Value;
+ if (statement != null)
+ {
+ statement.TryBind("@MaxPremiereDate", query.MaxPremiereDate.Value);
+ }
}
if (query.SourceTypes.Length == 1)
{
whereClauses.Add("SourceType=@SourceType");
- cmd.Parameters.Add(cmd, "@SourceType", DbType.String).Value = query.SourceTypes[0];
+ if (statement != null)
+ {
+ statement.TryBind("@SourceType", query.SourceTypes[0].ToString());
+ }
}
else if (query.SourceTypes.Length > 1)
{
@@ -3291,8 +3598,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (query.ExcludeSourceTypes.Length == 1)
{
- whereClauses.Add("SourceType<>@SourceType");
- cmd.Parameters.Add(cmd, "@SourceType", DbType.String).Value = query.SourceTypes[0];
+ whereClauses.Add("SourceType<>@ExcludeSourceTypes");
+ if (statement != null)
+ {
+ statement.TryBind("@ExcludeSourceTypes", query.ExcludeSourceTypes[0].ToString());
+ }
}
else if (query.ExcludeSourceTypes.Length > 1)
{
@@ -3307,7 +3617,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
foreach (var type in query.TrailerTypes)
{
clauses.Add("TrailerTypes like @TrailerTypes" + index);
- cmd.Parameters.Add(cmd, "@TrailerTypes" + index, DbType.String).Value = "%" + type + "%";
+ if (statement != null)
+ {
+ statement.TryBind("@TrailerTypes" + index, "%" + type + "%");
+ }
index++;
}
var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
@@ -3319,87 +3632,143 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (query.IsAiring.Value)
{
whereClauses.Add("StartDate<=@MaxStartDate");
- cmd.Parameters.Add(cmd, "@MaxStartDate", DbType.Date).Value = DateTime.UtcNow;
+ if (statement != null)
+ {
+ statement.TryBind("@MaxStartDate", DateTime.UtcNow);
+ }
whereClauses.Add("EndDate>=@MinEndDate");
- cmd.Parameters.Add(cmd, "@MinEndDate", DbType.Date).Value = DateTime.UtcNow;
+ if (statement != null)
+ {
+ statement.TryBind("@MinEndDate", DateTime.UtcNow);
+ }
}
else
{
whereClauses.Add("(StartDate>@IsAiringDate OR EndDate < @IsAiringDate)");
- cmd.Parameters.Add(cmd, "@IsAiringDate", DbType.Date).Value = DateTime.UtcNow;
+ if (statement != null)
+ {
+ statement.TryBind("@IsAiringDate", DateTime.UtcNow);
+ }
}
}
if (query.PersonIds.Length > 0)
{
- // Todo: improve without having to do this
- query.Person = query.PersonIds.Select(i => RetrieveItem(new Guid(i))).Where(i => i != null).Select(i => i.Name).FirstOrDefault();
+ // TODO: Should this query with CleanName ?
+
+ var clauses = new List<string>();
+ var index = 0;
+ foreach (var personId in query.PersonIds)
+ {
+ var paramName = "@PersonId" + index;
+
+ clauses.Add("(select Name from TypedBaseItems where guid=" + paramName + ") in (select Name from People where ItemId=Guid)");
+ if (statement != null)
+ {
+ statement.TryBind(paramName, personId.ToGuidParamValue());
+ }
+ index++;
+ }
+ var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
+ whereClauses.Add(clause);
}
if (!string.IsNullOrWhiteSpace(query.Person))
{
whereClauses.Add("Guid in (select ItemId from People where Name=@PersonName)");
- cmd.Parameters.Add(cmd, "@PersonName", DbType.String).Value = query.Person;
+ if (statement != null)
+ {
+ statement.TryBind("@PersonName", query.Person);
+ }
}
if (!string.IsNullOrWhiteSpace(query.SlugName))
{
whereClauses.Add("SlugName=@SlugName");
- cmd.Parameters.Add(cmd, "@SlugName", DbType.String).Value = query.SlugName;
+ if (statement != null)
+ {
+ statement.TryBind("@SlugName", query.SlugName);
+ }
}
if (!string.IsNullOrWhiteSpace(query.MinSortName))
{
whereClauses.Add("SortName>=@MinSortName");
- cmd.Parameters.Add(cmd, "@MinSortName", DbType.String).Value = query.MinSortName;
+ if (statement != null)
+ {
+ statement.TryBind("@MinSortName", query.MinSortName);
+ }
}
if (!string.IsNullOrWhiteSpace(query.ExternalSeriesId))
{
whereClauses.Add("ExternalSeriesId=@ExternalSeriesId");
- cmd.Parameters.Add(cmd, "@ExternalSeriesId", DbType.String).Value = query.ExternalSeriesId;
+ if (statement != null)
+ {
+ statement.TryBind("@ExternalSeriesId", query.ExternalSeriesId);
+ }
+ }
+
+ if (!string.IsNullOrWhiteSpace(query.ExternalId))
+ {
+ whereClauses.Add("ExternalId=@ExternalId");
+ if (statement != null)
+ {
+ statement.TryBind("@ExternalId", query.ExternalId);
+ }
}
if (!string.IsNullOrWhiteSpace(query.Name))
{
whereClauses.Add("CleanName=@Name");
- cmd.Parameters.Add(cmd, "@Name", DbType.String).Value = GetCleanValue(query.Name);
+
+ if (statement != null)
+ {
+ statement.TryBind("@Name", GetCleanValue(query.Name));
+ }
}
if (!string.IsNullOrWhiteSpace(query.NameContains))
{
whereClauses.Add("CleanName like @NameContains");
- cmd.Parameters.Add(cmd, "@NameContains", DbType.String).Value = "%" + GetCleanValue(query.NameContains) + "%";
+ if (statement != null)
+ {
+ statement.TryBind("@NameContains", "%" + GetCleanValue(query.NameContains) + "%");
+ }
}
if (!string.IsNullOrWhiteSpace(query.NameStartsWith))
{
whereClauses.Add("SortName like @NameStartsWith");
- cmd.Parameters.Add(cmd, "@NameStartsWith", DbType.String).Value = query.NameStartsWith + "%";
+ if (statement != null)
+ {
+ statement.TryBind("@NameStartsWith", query.NameStartsWith + "%");
+ }
}
if (!string.IsNullOrWhiteSpace(query.NameStartsWithOrGreater))
{
whereClauses.Add("SortName >= @NameStartsWithOrGreater");
// lowercase this because SortName is stored as lowercase
- cmd.Parameters.Add(cmd, "@NameStartsWithOrGreater", DbType.String).Value = query.NameStartsWithOrGreater.ToLower();
+ if (statement != null)
+ {
+ statement.TryBind("@NameStartsWithOrGreater", query.NameStartsWithOrGreater.ToLower());
+ }
}
if (!string.IsNullOrWhiteSpace(query.NameLessThan))
{
whereClauses.Add("SortName < @NameLessThan");
// lowercase this because SortName is stored as lowercase
- cmd.Parameters.Add(cmd, "@NameLessThan", DbType.String).Value = query.NameLessThan.ToLower();
+ if (statement != null)
+ {
+ statement.TryBind("@NameLessThan", query.NameLessThan.ToLower());
+ }
}
- if (query.ImageTypes.Length > 0 && _config.Configuration.SchemaVersion >= 87)
+ if (query.ImageTypes.Length > 0)
{
- var requiredImageIndex = 0;
-
foreach (var requiredImage in query.ImageTypes)
{
- var paramName = "@RequiredImageType" + requiredImageIndex;
- whereClauses.Add("(select path from images where ItemId=Guid and ImageType=" + paramName + " limit 1) not null");
- cmd.Parameters.Add(cmd, paramName, DbType.Int32).Value = (int)requiredImage;
- requiredImageIndex++;
+ whereClauses.Add("Images like '%" + requiredImage + "%'");
}
}
@@ -3408,12 +3777,18 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (query.IsLiked.Value)
{
whereClauses.Add("rating>=@UserRating");
- cmd.Parameters.Add(cmd, "@UserRating", DbType.Double).Value = UserItemData.MinLikeValue;
+ if (statement != null)
+ {
+ statement.TryBind("@UserRating", UserItemData.MinLikeValue);
+ }
}
else
{
whereClauses.Add("(rating is null or rating<@UserRating)");
- cmd.Parameters.Add(cmd, "@UserRating", DbType.Double).Value = UserItemData.MinLikeValue;
+ if (statement != null)
+ {
+ statement.TryBind("@UserRating", UserItemData.MinLikeValue);
+ }
}
}
@@ -3427,7 +3802,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
whereClauses.Add("(IsFavorite is null or IsFavorite=@IsFavoriteOrLiked)");
}
- cmd.Parameters.Add(cmd, "@IsFavoriteOrLiked", DbType.Boolean).Value = query.IsFavoriteOrLiked.Value;
+ if (statement != null)
+ {
+ statement.TryBind("@IsFavoriteOrLiked", query.IsFavoriteOrLiked.Value);
+ }
}
if (query.IsFavorite.HasValue)
@@ -3440,7 +3818,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
whereClauses.Add("(IsFavorite is null or IsFavorite=@IsFavorite)");
}
- cmd.Parameters.Add(cmd, "@IsFavorite", DbType.Boolean).Value = query.IsFavorite.Value;
+ if (statement != null)
+ {
+ statement.TryBind("@IsFavorite", query.IsFavorite.Value);
+ }
}
if (EnableJoinUserData(query))
@@ -3455,7 +3836,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
whereClauses.Add("(played is null or played=@IsPlayed)");
}
- cmd.Parameters.Add(cmd, "@IsPlayed", DbType.Boolean).Value = query.IsPlayed.Value;
+ if (statement != null)
+ {
+ statement.TryBind("@IsPlayed", query.IsPlayed.Value);
+ }
}
}
@@ -3478,7 +3862,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
foreach (var artist in query.ArtistNames)
{
clauses.Add("@ArtistName" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type <= 1)");
- cmd.Parameters.Add(cmd, "@ArtistName" + index, DbType.String).Value = GetCleanValue(artist);
+ if (statement != null)
+ {
+ statement.TryBind("@ArtistName" + index, GetCleanValue(artist));
+ }
index++;
}
var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
@@ -3491,22 +3878,36 @@ namespace MediaBrowser.Server.Implementations.Persistence
var index = 0;
foreach (var artistId in query.ExcludeArtistIds)
{
- var artistItem = RetrieveItem(new Guid(artistId));
- if (artistItem != null)
+ 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)");
+ if (statement != null)
{
- clauses.Add("@ExcludeArtistName" + index + " not in (select CleanValue from itemvalues where ItemId=Guid and Type <= 1)");
- cmd.Parameters.Add(cmd, "@ExcludeArtistName" + index, DbType.String).Value = GetCleanValue(artistItem.Name);
- index++;
+ statement.TryBind(paramName, artistId.ToGuidParamValue());
}
+ index++;
}
- var clause = "(" + string.Join(" AND ", clauses.ToArray()) + ")";
+ var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
whereClauses.Add(clause);
}
if (query.GenreIds.Length > 0)
{
- // Todo: improve without having to do this
- query.Genres = query.GenreIds.Select(i => RetrieveItem(new Guid(i))).Where(i => i != null).Select(i => i.Name).ToArray();
+ var clauses = new List<string>();
+ var index = 0;
+ foreach (var genreId in query.GenreIds)
+ {
+ var paramName = "@GenreId" + index;
+
+ clauses.Add("(select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from itemvalues where ItemId=Guid and Type=2)");
+ if (statement != null)
+ {
+ statement.TryBind(paramName, genreId.ToGuidParamValue());
+ }
+ index++;
+ }
+ var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
+ whereClauses.Add(clause);
}
if (query.Genres.Length > 0)
@@ -3516,7 +3917,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
foreach (var item in query.Genres)
{
clauses.Add("@Genre" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type=2)");
- cmd.Parameters.Add(cmd, "@Genre" + index, DbType.String).Value = GetCleanValue(item);
+ if (statement != null)
+ {
+ statement.TryBind("@Genre" + index, GetCleanValue(item));
+ }
index++;
}
var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
@@ -3530,7 +3934,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
foreach (var item in query.Tags)
{
clauses.Add("@Tag" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type=4)");
- cmd.Parameters.Add(cmd, "@Tag" + index, DbType.String).Value = GetCleanValue(item);
+ if (statement != null)
+ {
+ statement.TryBind("@Tag" + index, GetCleanValue(item));
+ }
index++;
}
var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
@@ -3539,8 +3946,21 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (query.StudioIds.Length > 0)
{
- // Todo: improve without having to do this
- query.Studios = query.StudioIds.Select(i => RetrieveItem(new Guid(i))).Where(i => i != null).Select(i => i.Name).ToArray();
+ var clauses = new List<string>();
+ var index = 0;
+ foreach (var studioId in query.StudioIds)
+ {
+ var paramName = "@StudioId" + index;
+
+ clauses.Add("(select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from itemvalues where ItemId=Guid and Type=3)");
+ if (statement != null)
+ {
+ statement.TryBind(paramName, studioId.ToGuidParamValue());
+ }
+ index++;
+ }
+ var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
+ whereClauses.Add(clause);
}
if (query.Studios.Length > 0)
@@ -3550,7 +3970,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
foreach (var item in query.Studios)
{
clauses.Add("@Studio" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type=3)");
- cmd.Parameters.Add(cmd, "@Studio" + index, DbType.String).Value = GetCleanValue(item);
+ if (statement != null)
+ {
+ statement.TryBind("@Studio" + index, GetCleanValue(item));
+ }
index++;
}
var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
@@ -3564,7 +3987,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
foreach (var item in query.Keywords)
{
clauses.Add("@Keyword" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type=5)");
- cmd.Parameters.Add(cmd, "@Keyword" + index, DbType.String).Value = GetCleanValue(item);
+ if (statement != null)
+ {
+ statement.TryBind("@Keyword" + index, GetCleanValue(item));
+ }
index++;
}
var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
@@ -3578,7 +4004,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
foreach (var item in query.OfficialRatings)
{
clauses.Add("OfficialRating=@OfficialRating" + index);
- cmd.Parameters.Add(cmd, "@OfficialRating" + index, DbType.String).Value = item;
+ if (statement != null)
+ {
+ statement.TryBind("@OfficialRating" + index, item);
+ }
index++;
}
var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
@@ -3588,13 +4017,19 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (query.MinParentalRating.HasValue)
{
whereClauses.Add("InheritedParentalRatingValue<=@MinParentalRating");
- cmd.Parameters.Add(cmd, "@MinParentalRating", DbType.Int32).Value = query.MinParentalRating.Value;
+ if (statement != null)
+ {
+ statement.TryBind("@MinParentalRating", query.MinParentalRating.Value);
+ }
}
if (query.MaxParentalRating.HasValue)
{
whereClauses.Add("InheritedParentalRatingValue<=@MaxParentalRating");
- cmd.Parameters.Add(cmd, "@MaxParentalRating", DbType.Int32).Value = query.MaxParentalRating.Value;
+ if (statement != null)
+ {
+ statement.TryBind("@MaxParentalRating", query.MaxParentalRating.Value);
+ }
}
if (query.HasParentalRating.HasValue)
@@ -3632,7 +4067,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (query.Years.Length == 1)
{
whereClauses.Add("ProductionYear=@Years");
- cmd.Parameters.Add(cmd, "@Years", DbType.Int32).Value = query.Years[0].ToString();
+ if (statement != null)
+ {
+ statement.TryBind("@Years", query.Years[0].ToString());
+ }
}
else if (query.Years.Length > 1)
{
@@ -3650,7 +4088,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
else
{
whereClauses.Add("LocationType=@LocationType");
- cmd.Parameters.Add(cmd, "@LocationType", DbType.String).Value = query.LocationTypes[0].ToString();
+ if (statement != null)
+ {
+ statement.TryBind("@LocationType", query.LocationTypes[0].ToString());
+ }
}
}
else if (query.LocationTypes.Length > 1)
@@ -3668,7 +4109,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
else
{
whereClauses.Add("LocationType<>@ExcludeLocationTypes");
- cmd.Parameters.Add(cmd, "@ExcludeLocationTypes", DbType.String).Value = query.ExcludeLocationTypes[0].ToString();
+ if (statement != null)
+ {
+ statement.TryBind("@ExcludeLocationTypes", query.ExcludeLocationTypes[0].ToString());
+ }
}
}
else if (query.ExcludeLocationTypes.Length > 1)
@@ -3679,14 +4123,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
if (query.IsVirtualItem.HasValue)
{
- if (_config.Configuration.SchemaVersion >= 90)
+ whereClauses.Add("IsVirtualItem=@IsVirtualItem");
+ if (statement != null)
{
- whereClauses.Add("IsVirtualItem=@IsVirtualItem");
- cmd.Parameters.Add(cmd, "@IsVirtualItem", DbType.Boolean).Value = query.IsVirtualItem.Value;
- }
- else if (!query.IsVirtualItem.Value)
- {
- whereClauses.Add("LocationType<>'Virtual'");
+ statement.TryBind("@IsVirtualItem", query.IsVirtualItem.Value);
}
}
if (query.IsSpecialSeason.HasValue)
@@ -3711,7 +4151,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
whereClauses.Add("PremiereDate < DATETIME('now')");
}
}
- if (query.IsMissing.HasValue && _config.Configuration.SchemaVersion >= 90)
+ if (query.IsMissing.HasValue)
{
if (query.IsMissing.Value)
{
@@ -3722,7 +4162,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
whereClauses.Add("(IsVirtualItem=0 OR PremiereDate >= DATETIME('now'))");
}
}
- if (query.IsVirtualUnaired.HasValue && _config.Configuration.SchemaVersion >= 90)
+ if (query.IsVirtualUnaired.HasValue)
{
if (query.IsVirtualUnaired.Value)
{
@@ -3736,7 +4176,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (query.MediaTypes.Length == 1)
{
whereClauses.Add("MediaType=@MediaTypes");
- cmd.Parameters.Add(cmd, "@MediaTypes", DbType.String).Value = query.MediaTypes[0];
+ if (statement != null)
+ {
+ statement.TryBind("@MediaTypes", query.MediaTypes[0]);
+ }
}
if (query.MediaTypes.Length > 1)
{
@@ -3752,7 +4195,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
foreach (var id in query.ItemIds)
{
includeIds.Add("Guid = @IncludeId" + index);
- cmd.Parameters.Add(cmd, "@IncludeId" + index, DbType.Guid).Value = new Guid(id);
+ if (statement != null)
+ {
+ statement.TryBind("@IncludeId" + index, new Guid(id));
+ }
index++;
}
@@ -3766,7 +4212,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
foreach (var id in query.ExcludeItemIds)
{
excludeIds.Add("Guid <> @ExcludeId" + index);
- cmd.Parameters.Add(cmd, "@ExcludeId" + index, DbType.Guid).Value = new Guid(id);
+ if (statement != null)
+ {
+ statement.TryBind("@ExcludeId" + index, new Guid(id));
+ }
index++;
}
@@ -3786,9 +4235,15 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
var paramName = "@ExcludeProviderId" + index;
- excludeIds.Add("(COALESCE((select value from ProviderIds where ItemId=Guid and Name = '" + pair.Key + "'), '') <> " + paramName + ")");
- cmd.Parameters.Add(cmd, paramName, DbType.String).Value = pair.Value;
+ //excludeIds.Add("(COALESCE((select value from ProviderIds where ItemId=Guid and Name = '" + pair.Key + "'), '') <> " + paramName + ")");
+ excludeIds.Add("ProviderIds not like " + paramName);
+ if (statement != null)
+ {
+ statement.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%");
+ }
index++;
+
+ break;
}
whereClauses.Add(string.Join(" AND ", excludeIds.ToArray()));
@@ -3796,20 +4251,17 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (query.HasImdbId.HasValue)
{
- var fn = query.HasImdbId.Value ? "<>" : "=";
- whereClauses.Add("(COALESCE((select value from ProviderIds where ItemId=Guid and Name = 'Imdb'), '') " + fn + " '')");
+ whereClauses.Add("ProviderIds like '%imdb=%'");
}
if (query.HasTmdbId.HasValue)
{
- var fn = query.HasTmdbId.Value ? "<>" : "=";
- whereClauses.Add("(COALESCE((select value from ProviderIds where ItemId=Guid and Name = 'Tmdb'), '') " + fn + " '')");
+ whereClauses.Add("ProviderIds like '%tmdb=%'");
}
if (query.HasTvdbId.HasValue)
{
- var fn = query.HasTvdbId.Value ? "<>" : "=";
- whereClauses.Add("(COALESCE((select value from ProviderIds where ItemId=Guid and Name = 'Tvdb'), '') " + fn + " '')");
+ whereClauses.Add("ProviderIds like '%tvdb=%'");
}
if (query.AlbumNames.Length > 0)
@@ -3824,7 +4276,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
clause += " OR ";
}
clause += "Album=@AlbumName" + index;
- cmd.Parameters.Add(cmd, "@AlbumName" + index, DbType.String).Value = name;
+
+ if (statement != null)
+ {
+ statement.TryBind("@AlbumName" + index, name);
+ }
index++;
}
@@ -3862,13 +4318,19 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (enableItemsByName)
{
whereClauses.Add("(TopParentId=@TopParentId or IsItemByName=@IsItemByName)");
- cmd.Parameters.Add(cmd, "@IsItemByName", DbType.Boolean).Value = true;
+ if (statement != null)
+ {
+ statement.TryBind("@IsItemByName", true);
+ }
}
else
{
whereClauses.Add("(TopParentId=@TopParentId)");
}
- cmd.Parameters.Add(cmd, "@TopParentId", DbType.String).Value = query.TopParentIds[0];
+ if (statement != null)
+ {
+ statement.TryBind("@TopParentId", query.TopParentIds[0]);
+ }
}
if (query.TopParentIds.Length > 1)
{
@@ -3877,7 +4339,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (enableItemsByName)
{
whereClauses.Add("(IsItemByName=@IsItemByName or TopParentId in (" + val + "))");
- cmd.Parameters.Add(cmd, "@IsItemByName", DbType.Boolean).Value = true;
+ if (statement != null)
+ {
+ statement.TryBind("@IsItemByName", true);
+ }
}
else
{
@@ -3888,7 +4353,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (query.AncestorIds.Length == 1)
{
whereClauses.Add("Guid in (select itemId from AncestorIds where AncestorId=@AncestorId)");
- cmd.Parameters.Add(cmd, "@AncestorId", DbType.Guid).Value = new Guid(query.AncestorIds[0]);
+
+ if (statement != null)
+ {
+ statement.TryBind("@AncestorId", new Guid(query.AncestorIds[0]));
+ }
}
if (query.AncestorIds.Length > 1)
{
@@ -3899,13 +4368,29 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
var inClause = "select guid from TypedBaseItems where PresentationUniqueKey=@AncestorWithPresentationUniqueKey";
whereClauses.Add(string.Format("Guid in (select itemId from AncestorIds where AncestorId in ({0}))", inClause));
- cmd.Parameters.Add(cmd, "@AncestorWithPresentationUniqueKey", DbType.String).Value = query.AncestorWithPresentationUniqueKey;
+ if (statement != null)
+ {
+ statement.TryBind("@AncestorWithPresentationUniqueKey", query.AncestorWithPresentationUniqueKey);
+ }
+ }
+
+ if (!string.IsNullOrWhiteSpace(query.SeriesPresentationUniqueKey))
+ {
+ whereClauses.Add("SeriesPresentationUniqueKey=@SeriesPresentationUniqueKey");
+
+ if (statement != null)
+ {
+ statement.TryBind("@SeriesPresentationUniqueKey", query.SeriesPresentationUniqueKey);
+ }
}
if (query.BlockUnratedItems.Length == 1)
{
whereClauses.Add("(InheritedParentalRatingValue > 0 or UnratedType <> @UnratedType)");
- cmd.Parameters.Add(cmd, "@UnratedType", DbType.String).Value = query.BlockUnratedItems[0].ToString();
+ if (statement != null)
+ {
+ statement.TryBind("@UnratedType", query.BlockUnratedItems[0].ToString());
+ }
}
if (query.BlockUnratedItems.Length > 1)
{
@@ -3917,7 +4402,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
foreach (var excludeTag in query.ExcludeTags)
{
whereClauses.Add("(Tags is null OR Tags not like @excludeTag" + excludeTagIndex + ")");
- cmd.Parameters.Add(cmd, "@excludeTag" + excludeTagIndex, DbType.String).Value = "%" + excludeTag + "%";
+ if (statement != null)
+ {
+ statement.TryBind("@excludeTag" + excludeTagIndex, "%" + excludeTag + "%");
+ }
excludeTagIndex++;
}
@@ -3925,7 +4413,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
foreach (var excludeTag in query.ExcludeInheritedTags)
{
whereClauses.Add("(InheritedTags is null OR InheritedTags not like @excludeInheritedTag" + excludeTagIndex + ")");
- cmd.Parameters.Add(cmd, "@excludeInheritedTag" + excludeTagIndex, DbType.String).Value = "%" + excludeTag + "%";
+ if (statement != null)
+ {
+ statement.TryBind("@excludeInheritedTag" + excludeTagIndex, "%" + excludeTag + "%");
+ }
excludeTagIndex++;
}
@@ -3995,6 +4486,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
typeof(Movie),
typeof(Playlist),
typeof(AudioPodcast),
+ typeof(AudioBook),
typeof(Trailer),
typeof(BoxSet),
typeof(Episode),
@@ -4028,75 +4520,40 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
var newValues = new List<Tuple<Guid, string>>();
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "select Guid,InheritedTags,(select group_concat(Tags, '|') from TypedBaseItems where (guid=outer.guid) OR (guid in (Select AncestorId from AncestorIds where ItemId=Outer.guid))) as NewInheritedTags from typedbaseitems as Outer where NewInheritedTags <> InheritedTags";
+ var commandText = "select Guid,InheritedTags,(select group_concat(Tags, '|') from TypedBaseItems where (guid=outer.guid) OR (guid in (Select AncestorId from AncestorIds where ItemId=Outer.guid))) as NewInheritedTags from typedbaseitems as Outer where NewInheritedTags <> InheritedTags";
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
+ using (WriteLock.Write())
+ {
+ using (var connection = CreateConnection())
{
- while (reader.Read())
+ foreach (var row in connection.Query(commandText))
{
- var id = reader.GetGuid(0);
- string value = reader.IsDBNull(2) ? null : reader.GetString(2);
+ var id = row.GetGuid(0);
+ string value = row.IsDBNull(2) ? null : row.GetString(2);
newValues.Add(new Tuple<Guid, string>(id, value));
}
- }
- }
-
- Logger.Debug("UpdateInheritedTags - {0} rows", newValues.Count);
- if (newValues.Count == 0)
- {
- return;
- }
-
- await WriteLock.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- IDbTransaction transaction = null;
- try
- {
- transaction = _connection.BeginTransaction();
-
- foreach (var item in newValues)
- {
- _updateInheritedTagsCommand.GetParameter(0).Value = item.Item1;
- _updateInheritedTagsCommand.GetParameter(1).Value = item.Item2;
-
- _updateInheritedTagsCommand.Transaction = transaction;
- _updateInheritedTagsCommand.ExecuteNonQuery();
- }
-
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
- }
+ Logger.Debug("UpdateInheritedTags - {0} rows", newValues.Count);
+ if (newValues.Count == 0)
+ {
+ return;
+ }
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Error running query:", e);
+ // write lock here
+ using (var statement = PrepareStatement(connection, "Update TypedBaseItems set InheritedTags=@InheritedTags where Guid=@Guid"))
+ {
+ foreach (var item in newValues)
+ {
+ var paramList = new List<object>();
- if (transaction != null)
- {
- transaction.Rollback();
- }
+ paramList.Add(item.Item1);
+ paramList.Add(item.Item2);
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
+ statement.Execute(paramList.ToArray());
+ }
+ }
}
-
- WriteLock.Release();
}
}
@@ -4139,89 +4596,41 @@ namespace MediaBrowser.Server.Implementations.Persistence
CheckDisposed();
- await WriteLock.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- IDbTransaction transaction = null;
-
- try
+ using (WriteLock.Write())
{
- transaction = _connection.BeginTransaction();
-
- // Delete people
- _deletePeopleCommand.GetParameter(0).Value = id;
- _deletePeopleCommand.Transaction = transaction;
- _deletePeopleCommand.ExecuteNonQuery();
-
- // Delete chapters
- _deleteChaptersCommand.GetParameter(0).Value = id;
- _deleteChaptersCommand.Transaction = transaction;
- _deleteChaptersCommand.ExecuteNonQuery();
-
- // Delete media streams
- _deleteStreamsCommand.GetParameter(0).Value = id;
- _deleteStreamsCommand.Transaction = transaction;
- _deleteStreamsCommand.ExecuteNonQuery();
-
- // Delete ancestors
- _deleteAncestorsCommand.GetParameter(0).Value = id;
- _deleteAncestorsCommand.Transaction = transaction;
- _deleteAncestorsCommand.ExecuteNonQuery();
-
- // Delete user data keys
- _deleteUserDataKeysCommand.GetParameter(0).Value = id;
- _deleteUserDataKeysCommand.Transaction = transaction;
- _deleteUserDataKeysCommand.ExecuteNonQuery();
+ using (var connection = CreateConnection())
+ {
+ connection.RunInTransaction(db =>
+ {
+ // Delete people
+ ExecuteWithSingleParam(db, "delete from People where ItemId=@Id", id.ToGuidParamValue());
- // Delete item values
- _deleteItemValuesCommand.GetParameter(0).Value = id;
- _deleteItemValuesCommand.Transaction = transaction;
- _deleteItemValuesCommand.ExecuteNonQuery();
+ // Delete chapters
+ ExecuteWithSingleParam(db, "delete from " + ChaptersTableName + " where ItemId=@Id", id.ToGuidParamValue());
- // Delete provider ids
- _deleteProviderIdsCommand.GetParameter(0).Value = id;
- _deleteProviderIdsCommand.Transaction = transaction;
- _deleteProviderIdsCommand.ExecuteNonQuery();
+ // Delete media streams
+ ExecuteWithSingleParam(db, "delete from mediastreams where ItemId=@Id", id.ToGuidParamValue());
- // Delete images
- _deleteImagesCommand.GetParameter(0).Value = id;
- _deleteImagesCommand.Transaction = transaction;
- _deleteImagesCommand.ExecuteNonQuery();
+ // Delete ancestors
+ ExecuteWithSingleParam(db, "delete from AncestorIds where ItemId=@Id", id.ToGuidParamValue());
- // Delete the item
- _deleteItemCommand.GetParameter(0).Value = id;
- _deleteItemCommand.Transaction = transaction;
- _deleteItemCommand.ExecuteNonQuery();
+ // Delete item values
+ ExecuteWithSingleParam(db, "delete from ItemValues where ItemId=@Id", id.ToGuidParamValue());
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
+ // Delete the item
+ ExecuteWithSingleParam(db, "delete from TypedBaseItems where guid=@Id", id.ToGuidParamValue());
+ }, TransactionMode);
}
-
- throw;
}
- catch (Exception e)
- {
- Logger.ErrorException("Failed to save children:", e);
-
- if (transaction != null)
- {
- transaction.Rollback();
- }
+ }
- throw;
- }
- finally
+ private void ExecuteWithSingleParam(IDatabaseConnection db, string query, byte[] value)
+ {
+ using (var statement = PrepareStatement(db, query))
{
- if (transaction != null)
- {
- transaction.Dispose();
- }
+ statement.TryBind("@Id", value);
- WriteLock.Release();
+ statement.MoveNext();
}
}
@@ -4234,30 +4643,34 @@ namespace MediaBrowser.Server.Implementations.Persistence
CheckDisposed();
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "select Distinct Name from People";
+ var commandText = "select Distinct Name from People";
- var whereClauses = GetPeopleWhereClauses(query, cmd);
+ var whereClauses = GetPeopleWhereClauses(query, null);
- if (whereClauses.Count > 0)
- {
- cmd.CommandText += " where " + string.Join(" AND ", whereClauses.ToArray());
- }
-
- cmd.CommandText += " order by ListOrder";
+ if (whereClauses.Count > 0)
+ {
+ commandText += " where " + string.Join(" AND ", whereClauses.ToArray());
+ }
- var list = new List<string>();
+ commandText += " order by ListOrder";
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
{
- while (reader.Read())
+ var list = new List<string>();
+ using (var statement = PrepareStatementSafe(connection, commandText))
{
- list.Add(reader.GetString(0));
+ // Run this again to bind the params
+ GetPeopleWhereClauses(query, statement);
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ list.Add(row.GetString(0));
+ }
}
+ return list;
}
-
- return list;
}
}
@@ -4270,51 +4683,66 @@ namespace MediaBrowser.Server.Implementations.Persistence
CheckDisposed();
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "select ItemId, Name, Role, PersonType, SortOrder from People";
+ var commandText = "select ItemId, Name, Role, PersonType, SortOrder from People";
- var whereClauses = GetPeopleWhereClauses(query, cmd);
-
- if (whereClauses.Count > 0)
- {
- cmd.CommandText += " where " + string.Join(" AND ", whereClauses.ToArray());
- }
+ var whereClauses = GetPeopleWhereClauses(query, null);
- cmd.CommandText += " order by ListOrder";
+ if (whereClauses.Count > 0)
+ {
+ commandText += " where " + string.Join(" AND ", whereClauses.ToArray());
+ }
- var list = new List<PersonInfo>();
+ commandText += " order by ListOrder";
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
{
- while (reader.Read())
+ var list = new List<PersonInfo>();
+
+ using (var statement = PrepareStatementSafe(connection, commandText))
{
- list.Add(GetPerson(reader));
+ // Run this again to bind the params
+ GetPeopleWhereClauses(query, statement);
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ list.Add(GetPerson(row));
+ }
}
- }
- return list;
+ return list;
+ }
}
}
- private List<string> GetPeopleWhereClauses(InternalPeopleQuery query, IDbCommand cmd)
+ private List<string> GetPeopleWhereClauses(InternalPeopleQuery query, IStatement statement)
{
var whereClauses = new List<string>();
if (query.ItemId != Guid.Empty)
{
whereClauses.Add("ItemId=@ItemId");
- cmd.Parameters.Add(cmd, "@ItemId", DbType.Guid).Value = query.ItemId;
+ if (statement != null)
+ {
+ statement.TryBind("@ItemId", query.ItemId.ToGuidParamValue());
+ }
}
if (query.AppearsInItemId != Guid.Empty)
{
whereClauses.Add("Name in (Select Name from People where ItemId=@AppearsInItemId)");
- cmd.Parameters.Add(cmd, "@AppearsInItemId", DbType.Guid).Value = query.AppearsInItemId;
+ if (statement != null)
+ {
+ statement.TryBind("@AppearsInItemId", query.AppearsInItemId.ToGuidParamValue());
+ }
}
if (query.PersonTypes.Count == 1)
{
whereClauses.Add("PersonType=@PersonType");
- cmd.Parameters.Add(cmd, "@PersonType", DbType.String).Value = query.PersonTypes[0];
+ if (statement != null)
+ {
+ statement.TryBind("@PersonType", query.PersonTypes[0]);
+ }
}
if (query.PersonTypes.Count > 1)
{
@@ -4325,7 +4753,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (query.ExcludePersonTypes.Count == 1)
{
whereClauses.Add("PersonType<>@PersonType");
- cmd.Parameters.Add(cmd, "@PersonType", DbType.String).Value = query.ExcludePersonTypes[0];
+ if (statement != null)
+ {
+ statement.TryBind("@PersonType", query.ExcludePersonTypes[0]);
+ }
}
if (query.ExcludePersonTypes.Count > 1)
{
@@ -4336,23 +4767,32 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (query.MaxListOrder.HasValue)
{
whereClauses.Add("ListOrder<=@MaxListOrder");
- cmd.Parameters.Add(cmd, "@MaxListOrder", DbType.Int32).Value = query.MaxListOrder.Value;
+ if (statement != null)
+ {
+ statement.TryBind("@MaxListOrder", query.MaxListOrder.Value);
+ }
}
if (!string.IsNullOrWhiteSpace(query.NameContains))
{
whereClauses.Add("Name like @NameContains");
- cmd.Parameters.Add(cmd, "@NameContains", DbType.String).Value = "%" + query.NameContains + "%";
+ if (statement != null)
+ {
+ statement.TryBind("@NameContains", "%" + query.NameContains + "%");
+ }
}
if (query.SourceTypes.Length == 1)
{
whereClauses.Add("(select sourcetype from typedbaseitems where guid=ItemId) = @SourceTypes");
- cmd.Parameters.Add(cmd, "@SourceTypes", DbType.String).Value = query.SourceTypes[0].ToString();
+ if (statement != null)
+ {
+ statement.TryBind("@SourceTypes", query.SourceTypes[0].ToString());
+ }
}
return whereClauses;
}
- private void UpdateAncestors(Guid itemId, List<Guid> ancestorIds, IDbTransaction transaction)
+ private void UpdateAncestors(Guid itemId, List<Guid> ancestorIds, IDatabaseConnection db, IStatement deleteAncestorsStatement, IStatement updateAncestorsStatement)
{
if (itemId == Guid.Empty)
{
@@ -4367,20 +4807,17 @@ namespace MediaBrowser.Server.Implementations.Persistence
CheckDisposed();
// First delete
- _deleteAncestorsCommand.GetParameter(0).Value = itemId;
- _deleteAncestorsCommand.Transaction = transaction;
-
- _deleteAncestorsCommand.ExecuteNonQuery();
+ deleteAncestorsStatement.Reset();
+ deleteAncestorsStatement.TryBind("@ItemId", itemId.ToGuidParamValue());
+ deleteAncestorsStatement.MoveNext();
foreach (var ancestorId in ancestorIds)
{
- _saveAncestorCommand.GetParameter(0).Value = itemId;
- _saveAncestorCommand.GetParameter(1).Value = ancestorId;
- _saveAncestorCommand.GetParameter(2).Value = ancestorId.ToString("N");
-
- _saveAncestorCommand.Transaction = transaction;
-
- _saveAncestorCommand.ExecuteNonQuery();
+ updateAncestorsStatement.Reset();
+ updateAncestorsStatement.TryBind("@ItemId", itemId.ToGuidParamValue());
+ updateAncestorsStatement.TryBind("@AncestorId", ancestorId.ToGuidParamValue());
+ updateAncestorsStatement.TryBind("@AncestorIdText", ancestorId.ToString("N"));
+ updateAncestorsStatement.MoveNext();
}
}
@@ -4457,43 +4894,43 @@ namespace MediaBrowser.Server.Implementations.Persistence
("Type=" + itemValueTypes[0].ToString(CultureInfo.InvariantCulture)) :
("Type in (" + string.Join(",", itemValueTypes.Select(i => i.ToString(CultureInfo.InvariantCulture)).ToArray()) + ")");
- var list = new List<string>();
+ var commandText = "Select Value From ItemValues where " + typeClause;
- using (var cmd = _connection.CreateCommand())
+ if (withItemTypes.Count > 0)
{
- cmd.CommandText = "Select Value From ItemValues where " + typeClause;
-
- if (withItemTypes.Count > 0)
- {
- var typeString = string.Join(",", withItemTypes.Select(i => "'" + i + "'").ToArray());
- cmd.CommandText += " AND ItemId In (select guid from typedbaseitems where type in (" + typeString + "))";
- }
- if (excludeItemTypes.Count > 0)
- {
- var typeString = string.Join(",", excludeItemTypes.Select(i => "'" + i + "'").ToArray());
- cmd.CommandText += " AND ItemId not In (select guid from typedbaseitems where type in (" + typeString + "))";
- }
-
- cmd.CommandText += " Group By CleanValue";
+ var typeString = string.Join(",", withItemTypes.Select(i => "'" + i + "'").ToArray());
+ commandText += " AND ItemId In (select guid from typedbaseitems where type in (" + typeString + "))";
+ }
+ if (excludeItemTypes.Count > 0)
+ {
+ var typeString = string.Join(",", excludeItemTypes.Select(i => "'" + i + "'").ToArray());
+ commandText += " AND ItemId not In (select guid from typedbaseitems where type in (" + typeString + "))";
+ }
- var commandBehavior = CommandBehavior.SequentialAccess | CommandBehavior.SingleResult;
+ commandText += " Group By CleanValue";
- using (var reader = cmd.ExecuteReader(commandBehavior))
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
{
- LogQueryTime("GetItemValueNames", cmd, now);
+ var list = new List<string>();
- while (reader.Read())
+ using (var statement = PrepareStatementSafe(connection, commandText))
{
- if (!reader.IsDBNull(0))
+ foreach (var row in statement.ExecuteQuery())
{
- list.Add(reader.GetString(0));
+ if (!row.IsDBNull(0))
+ {
+ list.Add(row.GetString(0));
+ }
}
}
- }
- }
+ LogQueryTime("GetItemValueNames", commandText, now);
- return list;
+ return list;
+ }
+ }
}
private QueryResult<Tuple<BaseItem, ItemCounts>> GetItemValues(InternalItemsQuery query, int[] itemValueTypes, string returnType)
@@ -4509,6 +4946,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
CheckDisposed();
+ //Logger.Info("GetItemValues: " + _environmentInfo.StackTrace);
var now = DateTime.UtcNow;
@@ -4516,50 +4954,17 @@ namespace MediaBrowser.Server.Implementations.Persistence
("Type=" + itemValueTypes[0].ToString(CultureInfo.InvariantCulture)) :
("Type in (" + string.Join(",", itemValueTypes.Select(i => i.ToString(CultureInfo.InvariantCulture)).ToArray()) + ")");
- using (var cmd = _connection.CreateCommand())
- {
- var itemCountColumns = new List<Tuple<string, string>>();
-
- var typesToCount = query.IncludeItemTypes.ToList();
-
- if (typesToCount.Count > 0)
- {
- var itemCountColumnQuery = "select group_concat(type, '|')" + GetFromText("B");
-
- var typeSubQuery = 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,
- IsPlayed = query.IsPlayed
- };
- var whereClauses = GetWhereClauses(typeSubQuery, cmd, "itemTypes");
-
- whereClauses.Add("guid in (select ItemId from ItemValues where ItemValues.CleanValue=A.CleanName AND " + typeClause + ")");
-
- var typeWhereText = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
-
- itemCountColumnQuery += typeWhereText;
+ InternalItemsQuery typeSubQuery = null;
- //itemCountColumnQuery += ")";
+ var itemCountColumns = new List<Tuple<string, string>>();
- itemCountColumns.Add(new Tuple<string, string>("itemTypes", "(" + itemCountColumnQuery + ") as itemTypes"));
- }
-
- var columns = _retriveItemColumns.ToList();
- columns.AddRange(itemCountColumns.Select(i => i.Item2).ToArray());
+ var typesToCount = query.IncludeItemTypes.ToList();
- cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, columns.ToArray(), cmd)) + GetFromText();
- cmd.CommandText += GetJoinUserDataText(query);
+ if (typesToCount.Count > 0)
+ {
+ var itemCountColumnQuery = "select group_concat(type, '|')" + GetFromText("B");
- var innerQuery = new InternalItemsQuery(query.User)
+ typeSubQuery = new InternalItemsQuery(query.User)
{
ExcludeItemTypes = query.ExcludeItemTypes,
IncludeItemTypes = query.IncludeItemTypes,
@@ -4571,152 +4976,206 @@ namespace MediaBrowser.Server.Implementations.Persistence
ParentId = query.ParentId,
IsPlayed = query.IsPlayed
};
+ var whereClauses = GetWhereClauses(typeSubQuery, null, "itemTypes");
- var innerWhereClauses = GetWhereClauses(innerQuery, cmd);
+ whereClauses.Add("guid in (select ItemId from ItemValues where ItemValues.CleanValue=A.CleanName AND " + typeClause + ")");
- var innerWhereText = innerWhereClauses.Count == 0 ?
+ var typeWhereText = whereClauses.Count == 0 ?
string.Empty :
- " where " + string.Join(" AND ", innerWhereClauses.ToArray());
+ " where " + string.Join(" AND ", whereClauses.ToArray());
- var whereText = " where Type=@SelectType";
+ itemCountColumnQuery += typeWhereText;
- if (typesToCount.Count == 0)
- {
- whereText += " And CleanName In (Select CleanValue from ItemValues where " + typeClause + " AND ItemId in (select guid from TypedBaseItems" + innerWhereText + "))";
- }
- else
- {
- //whereText += " And itemTypes not null";
- whereText += " And CleanName In (Select CleanValue from ItemValues where " + typeClause + " AND ItemId in (select guid from TypedBaseItems" + innerWhereText + "))";
- }
-
- var outerQuery = new InternalItemsQuery(query.User)
- {
- IsFavorite = query.IsFavorite,
- IsFavoriteOrLiked = query.IsFavoriteOrLiked,
- IsLiked = query.IsLiked,
- IsLocked = query.IsLocked,
- NameLessThan = query.NameLessThan,
- NameStartsWith = query.NameStartsWith,
- NameStartsWithOrGreater = query.NameStartsWithOrGreater,
- AlbumArtistStartsWithOrGreater = query.AlbumArtistStartsWithOrGreater,
- Tags = query.Tags,
- OfficialRatings = query.OfficialRatings,
- GenreIds = query.GenreIds,
- Genres = query.Genres,
- Years = query.Years
- };
+ //itemCountColumnQuery += ")";
- var outerWhereClauses = GetWhereClauses(outerQuery, cmd);
+ itemCountColumns.Add(new Tuple<string, string>("itemTypes", "(" + itemCountColumnQuery + ") as itemTypes"));
+ }
- whereText += outerWhereClauses.Count == 0 ?
- string.Empty :
- " AND " + string.Join(" AND ", outerWhereClauses.ToArray());
- //cmd.CommandText += GetGroupBy(query);
+ var columns = _retriveItemColumns.ToList();
+ columns.AddRange(itemCountColumns.Select(i => i.Item2).ToArray());
- cmd.CommandText += whereText;
- cmd.CommandText += " group by PresentationUniqueKey";
+ var commandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, columns.ToArray())) + GetFromText();
+ commandText += GetJoinUserDataText(query);
- cmd.Parameters.Add(cmd, "@SelectType", DbType.String).Value = returnType;
+ 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,
+ IsPlayed = query.IsPlayed
+ };
- if (EnableJoinUserData(query))
- {
- cmd.Parameters.Add(cmd, "@UserId", DbType.Guid).Value = query.User.Id;
- }
+ var innerWhereClauses = GetWhereClauses(innerQuery, null);
- cmd.CommandText += " order by SortName";
+ var innerWhereText = innerWhereClauses.Count == 0 ?
+ string.Empty :
+ " where " + string.Join(" AND ", innerWhereClauses.ToArray());
- if (query.Limit.HasValue || query.StartIndex.HasValue)
- {
- var offset = query.StartIndex ?? 0;
+ var whereText = " where Type=@SelectType";
- if (query.Limit.HasValue || offset > 0)
- {
- cmd.CommandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture);
- }
+ if (typesToCount.Count == 0)
+ {
+ whereText += " And CleanName In (Select CleanValue from ItemValues where " + typeClause + " AND ItemId in (select guid from TypedBaseItems" + innerWhereText + "))";
+ }
+ else
+ {
+ //whereText += " And itemTypes not null";
+ whereText += " And CleanName In (Select CleanValue from ItemValues where " + typeClause + " AND ItemId in (select guid from TypedBaseItems" + innerWhereText + "))";
+ }
+
+ var outerQuery = new InternalItemsQuery(query.User)
+ {
+ IsFavorite = query.IsFavorite,
+ IsFavoriteOrLiked = query.IsFavoriteOrLiked,
+ IsLiked = query.IsLiked,
+ IsLocked = query.IsLocked,
+ NameLessThan = query.NameLessThan,
+ NameStartsWith = query.NameStartsWith,
+ NameStartsWithOrGreater = query.NameStartsWithOrGreater,
+ AlbumArtistStartsWithOrGreater = query.AlbumArtistStartsWithOrGreater,
+ Tags = query.Tags,
+ OfficialRatings = query.OfficialRatings,
+ GenreIds = query.GenreIds,
+ Genres = query.Genres,
+ Years = query.Years
+ };
- if (offset > 0)
- {
- cmd.CommandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture);
- }
- }
+ var outerWhereClauses = GetWhereClauses(outerQuery, null);
- cmd.CommandText += ";";
+ whereText += outerWhereClauses.Count == 0 ?
+ string.Empty :
+ " AND " + string.Join(" AND ", outerWhereClauses.ToArray());
+ //cmd.CommandText += GetGroupBy(query);
- var isReturningZeroItems = query.Limit.HasValue && query.Limit <= 0;
+ commandText += whereText;
+ commandText += " group by PresentationUniqueKey";
- if (isReturningZeroItems)
- {
- cmd.CommandText = "";
- }
+ commandText += " order by SortName";
- if (query.EnableTotalRecordCount)
- {
- cmd.CommandText += "select count (distinct PresentationUniqueKey)" + GetFromText();
+ if (query.Limit.HasValue || query.StartIndex.HasValue)
+ {
+ var offset = query.StartIndex ?? 0;
- cmd.CommandText += GetJoinUserDataText(query);
- cmd.CommandText += whereText;
+ if (query.Limit.HasValue || offset > 0)
+ {
+ commandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture);
}
- else
+
+ if (offset > 0)
{
- cmd.CommandText = cmd.CommandText.TrimEnd(';');
+ commandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture);
}
+ }
- var list = new List<Tuple<BaseItem, ItemCounts>>();
- var count = 0;
+ var isReturningZeroItems = query.Limit.HasValue && query.Limit <= 0;
- var commandBehavior = isReturningZeroItems || !query.EnableTotalRecordCount
- ? (CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)
- : CommandBehavior.SequentialAccess;
+ var statementTexts = new List<string>();
+ if (!isReturningZeroItems)
+ {
+ statementTexts.Add(commandText);
+ }
+ if (query.EnableTotalRecordCount)
+ {
+ var countText = "select count (distinct PresentationUniqueKey)" + GetFromText();
- //Logger.Debug("GetItemValues: " + cmd.CommandText);
+ countText += GetJoinUserDataText(query);
+ countText += whereText;
+ statementTexts.Add(countText);
+ }
- using (var reader = cmd.ExecuteReader(commandBehavior))
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
{
- LogQueryTime("GetItemValues", cmd, now);
-
- if (isReturningZeroItems)
+ return connection.RunInTransaction(db =>
{
- if (reader.Read())
+ var list = new List<Tuple<BaseItem, ItemCounts>>();
+ var result = new QueryResult<Tuple<BaseItem, ItemCounts>>();
+
+ var statements = PrepareAllSafe(db, statementTexts)
+ .ToList();
+
+ if (!isReturningZeroItems)
{
- count = reader.GetInt32(0);
+ using (var statement = statements[0])
+ {
+ statement.TryBind("@SelectType", returnType);
+ if (EnableJoinUserData(query))
+ {
+ statement.TryBind("@UserId", query.User.Id);
+ }
+
+ if (typeSubQuery != null)
+ {
+ GetWhereClauses(typeSubQuery, null, "itemTypes");
+ }
+ BindSimilarParams(query, statement);
+ GetWhereClauses(innerQuery, statement);
+ GetWhereClauses(outerQuery, statement);
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ var item = GetItem(row);
+ if (item != null)
+ {
+ var countStartColumn = columns.Count - 1;
+
+ list.Add(new Tuple<BaseItem, ItemCounts>(item, GetItemCounts(row, countStartColumn, typesToCount)));
+ }
+ }
+
+ LogQueryTime("GetItemValues", commandText, now);
+ }
}
- }
- else
- {
- while (reader.Read())
+
+ if (query.EnableTotalRecordCount)
{
- var item = GetItem(reader);
- if (item != null)
+ commandText = "select count (distinct PresentationUniqueKey)" + GetFromText();
+
+ commandText += GetJoinUserDataText(query);
+ commandText += whereText;
+
+ using (var statement = statements[statements.Count - 1])
{
- var countStartColumn = columns.Count - 1;
+ statement.TryBind("@SelectType", returnType);
+ if (EnableJoinUserData(query))
+ {
+ statement.TryBind("@UserId", query.User.Id);
+ }
- list.Add(new Tuple<BaseItem, ItemCounts>(item, GetItemCounts(reader, countStartColumn, typesToCount)));
+ if (typeSubQuery != null)
+ {
+ GetWhereClauses(typeSubQuery, null, "itemTypes");
+ }
+ BindSimilarParams(query, statement);
+ GetWhereClauses(innerQuery, statement);
+ GetWhereClauses(outerQuery, statement);
+
+ result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
+
+ LogQueryTime("GetItemValues", commandText, now);
}
}
- if (reader.NextResult() && reader.Read())
+ if (result.TotalRecordCount == 0)
{
- count = reader.GetInt32(0);
+ result.TotalRecordCount = list.Count;
}
- }
- }
+ result.Items = list.ToArray();
- if (count == 0)
- {
- count = list.Count;
- }
-
- return new QueryResult<Tuple<BaseItem, ItemCounts>>
- {
- Items = list.ToArray(),
- TotalRecordCount = count
- };
+ return result;
+ }, ReadTransactionMode);
+ }
}
}
- private ItemCounts GetItemCounts(IDataReader reader, int countStartColumn, List<string> typesToCount)
+ private ItemCounts GetItemCounts(IReadOnlyList<IResultSetValue> reader, int countStartColumn, List<string> typesToCount)
{
var counts = new ItemCounts();
@@ -4802,97 +5261,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
return list;
}
- private void UpdateImages(Guid itemId, List<ItemImageInfo> images, IDbTransaction transaction)
- {
- if (itemId == Guid.Empty)
- {
- throw new ArgumentNullException("itemId");
- }
-
- if (images == null)
- {
- throw new ArgumentNullException("images");
- }
-
- CheckDisposed();
-
- // First delete
- _deleteImagesCommand.GetParameter(0).Value = itemId;
- _deleteImagesCommand.Transaction = transaction;
-
- _deleteImagesCommand.ExecuteNonQuery();
-
- var index = 0;
- foreach (var image in images)
- {
- if (string.IsNullOrWhiteSpace(image.Path))
- {
- // Invalid
- continue;
- }
-
- _saveImagesCommand.GetParameter(0).Value = itemId;
- _saveImagesCommand.GetParameter(1).Value = image.Type;
- _saveImagesCommand.GetParameter(2).Value = image.Path;
-
- if (image.DateModified == default(DateTime))
- {
- _saveImagesCommand.GetParameter(3).Value = null;
- }
- else
- {
- _saveImagesCommand.GetParameter(3).Value = image.DateModified;
- }
-
- _saveImagesCommand.GetParameter(4).Value = image.IsPlaceholder;
- _saveImagesCommand.GetParameter(5).Value = index;
-
- _saveImagesCommand.Transaction = transaction;
-
- _saveImagesCommand.ExecuteNonQuery();
- index++;
- }
- }
-
- private void UpdateProviderIds(Guid itemId, Dictionary<string, string> values, IDbTransaction transaction)
- {
- if (itemId == Guid.Empty)
- {
- throw new ArgumentNullException("itemId");
- }
-
- if (values == null)
- {
- throw new ArgumentNullException("values");
- }
-
- // Just in case there might be case-insensitive duplicates, strip them out now
- var newValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- foreach (var pair in values)
- {
- newValues[pair.Key] = pair.Value;
- }
-
- CheckDisposed();
-
- // First delete
- _deleteProviderIdsCommand.GetParameter(0).Value = itemId;
- _deleteProviderIdsCommand.Transaction = transaction;
-
- _deleteProviderIdsCommand.ExecuteNonQuery();
-
- foreach (var pair in newValues)
- {
- _saveProviderIdsCommand.GetParameter(0).Value = itemId;
- _saveProviderIdsCommand.GetParameter(1).Value = pair.Key;
- _saveProviderIdsCommand.GetParameter(2).Value = pair.Value;
- _saveProviderIdsCommand.Transaction = transaction;
-
- _saveProviderIdsCommand.ExecuteNonQuery();
- }
- }
-
- private void UpdateItemValues(Guid itemId, List<Tuple<int, string>> values, IDbTransaction transaction)
+ private void UpdateItemValues(Guid itemId, List<Tuple<int, string>> values, IDatabaseConnection db)
{
if (itemId == Guid.Empty)
{
@@ -4907,60 +5276,29 @@ namespace MediaBrowser.Server.Implementations.Persistence
CheckDisposed();
// First delete
- _deleteItemValuesCommand.GetParameter(0).Value = itemId;
- _deleteItemValuesCommand.Transaction = transaction;
-
- _deleteItemValuesCommand.ExecuteNonQuery();
+ db.Execute("delete from ItemValues where ItemId=@Id", itemId.ToGuidParamValue());
- foreach (var pair in values)
+ using (var statement = PrepareStatement(db, "insert into ItemValues (ItemId, Type, Value, CleanValue) values (@ItemId, @Type, @Value, @CleanValue)"))
{
- _saveItemValuesCommand.GetParameter(0).Value = itemId;
- _saveItemValuesCommand.GetParameter(1).Value = pair.Item1;
- _saveItemValuesCommand.GetParameter(2).Value = pair.Item2;
- if (pair.Item2 == null)
- {
- _saveItemValuesCommand.GetParameter(3).Value = null;
- }
- else
+ foreach (var pair in values)
{
- _saveItemValuesCommand.GetParameter(3).Value = GetCleanValue(pair.Item2);
- }
- _saveItemValuesCommand.Transaction = transaction;
+ statement.Reset();
- _saveItemValuesCommand.ExecuteNonQuery();
- }
- }
+ statement.TryBind("@ItemId", itemId.ToGuidParamValue());
+ statement.TryBind("@Type", pair.Item1);
+ statement.TryBind("@Value", pair.Item2);
- private void UpdateUserDataKeys(Guid itemId, List<string> keys, IDbTransaction transaction)
- {
- if (itemId == Guid.Empty)
- {
- throw new ArgumentNullException("itemId");
- }
-
- if (keys == null)
- {
- throw new ArgumentNullException("keys");
- }
-
- CheckDisposed();
-
- // First delete
- _deleteUserDataKeysCommand.GetParameter(0).Value = itemId;
- _deleteUserDataKeysCommand.Transaction = transaction;
-
- _deleteUserDataKeysCommand.ExecuteNonQuery();
- var index = 0;
-
- foreach (var key in keys)
- {
- _saveUserDataKeysCommand.GetParameter(0).Value = itemId;
- _saveUserDataKeysCommand.GetParameter(1).Value = key;
- _saveUserDataKeysCommand.GetParameter(2).Value = index;
- index++;
- _saveUserDataKeysCommand.Transaction = transaction;
+ if (pair.Item2 == null)
+ {
+ statement.TryBindNull("@CleanValue");
+ }
+ else
+ {
+ statement.TryBind("@CleanValue", GetCleanValue(pair.Item2));
+ }
- _saveUserDataKeysCommand.ExecuteNonQuery();
+ statement.MoveNext();
+ }
}
}
@@ -4978,75 +5316,42 @@ namespace MediaBrowser.Server.Implementations.Persistence
CheckDisposed();
- var cancellationToken = CancellationToken.None;
-
- await WriteLock.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- IDbTransaction transaction = null;
-
- try
+ using (WriteLock.Write())
{
- transaction = _connection.BeginTransaction();
-
- // First delete
- _deletePeopleCommand.GetParameter(0).Value = itemId;
- _deletePeopleCommand.Transaction = transaction;
-
- _deletePeopleCommand.ExecuteNonQuery();
-
- var listIndex = 0;
-
- foreach (var person in people)
+ using (var connection = CreateConnection())
{
- cancellationToken.ThrowIfCancellationRequested();
-
- _savePersonCommand.GetParameter(0).Value = itemId;
- _savePersonCommand.GetParameter(1).Value = person.Name;
- _savePersonCommand.GetParameter(2).Value = person.Role;
- _savePersonCommand.GetParameter(3).Value = person.Type;
- _savePersonCommand.GetParameter(4).Value = person.SortOrder;
- _savePersonCommand.GetParameter(5).Value = listIndex;
-
- _savePersonCommand.Transaction = transaction;
+ // First delete
+ // "delete from People where ItemId=?"
+ connection.Execute("delete from People where ItemId=?", itemId.ToGuidParamValue());
- _savePersonCommand.ExecuteNonQuery();
- listIndex++;
- }
-
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
- }
+ var listIndex = 0;
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to save people:", e);
+ using (var statement = PrepareStatement(connection,
+ "insert into People (ItemId, Name, Role, PersonType, SortOrder, ListOrder) values (@ItemId, @Name, @Role, @PersonType, @SortOrder, @ListOrder)"))
+ {
+ foreach (var person in people)
+ {
+ if (listIndex > 0)
+ {
+ statement.Reset();
+ }
- if (transaction != null)
- {
- transaction.Rollback();
- }
+ statement.TryBind("@ItemId", itemId.ToGuidParamValue());
+ statement.TryBind("@Name", person.Name);
+ statement.TryBind("@Role", person.Role);
+ statement.TryBind("@PersonType", person.Type);
+ statement.TryBind("@SortOrder", person.SortOrder);
+ statement.TryBind("@ListOrder", listIndex);
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
+ statement.MoveNext();
+ listIndex++;
+ }
+ }
}
-
- WriteLock.Release();
}
}
- private PersonInfo GetPerson(IDataReader reader)
+ private PersonInfo GetPerson(IReadOnlyList<IResultSetValue> reader)
{
var item = new PersonInfo();
@@ -5080,41 +5385,51 @@ namespace MediaBrowser.Server.Implementations.Persistence
throw new ArgumentNullException("query");
}
- var list = new List<MediaStream>();
+ var cmdText = "select " + string.Join(",", _mediaStreamSaveColumns) + " from mediastreams where";
+
+ cmdText += " ItemId=@ItemId";
- using (var cmd = _connection.CreateCommand())
+ if (query.Type.HasValue)
{
- var cmdText = "select " + string.Join(",", _mediaStreamSaveColumns) + " from mediastreams where";
+ cmdText += " AND StreamType=@StreamType";
+ }
- cmdText += " ItemId=@ItemId";
- cmd.Parameters.Add(cmd, "@ItemId", DbType.Guid).Value = query.ItemId;
+ if (query.Index.HasValue)
+ {
+ cmdText += " AND StreamIndex=@StreamIndex";
+ }
- if (query.Type.HasValue)
- {
- cmdText += " AND StreamType=@StreamType";
- cmd.Parameters.Add(cmd, "@StreamType", DbType.String).Value = query.Type.Value.ToString();
- }
+ cmdText += " order by StreamIndex ASC";
- if (query.Index.HasValue)
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
{
- cmdText += " AND StreamIndex=@StreamIndex";
- cmd.Parameters.Add(cmd, "@StreamIndex", DbType.Int32).Value = query.Index.Value;
- }
+ var list = new List<MediaStream>();
- cmdText += " order by StreamIndex ASC";
+ using (var statement = PrepareStatementSafe(connection, cmdText))
+ {
+ statement.TryBind("@ItemId", query.ItemId.ToGuidParamValue());
- cmd.CommandText = cmdText;
+ if (query.Type.HasValue)
+ {
+ statement.TryBind("@StreamType", query.Type.Value.ToString());
+ }
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
- {
- while (reader.Read())
- {
- list.Add(GetMediaStream(reader));
+ if (query.Index.HasValue)
+ {
+ statement.TryBind("@StreamIndex", query.Index.Value);
+ }
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ list.Add(GetMediaStream(row));
+ }
}
+
+ return list;
}
}
-
- return list;
}
public async Task SaveMediaStreams(Guid id, List<MediaStream> streams, CancellationToken cancellationToken)
@@ -5133,100 +5448,63 @@ namespace MediaBrowser.Server.Implementations.Persistence
cancellationToken.ThrowIfCancellationRequested();
- await WriteLock.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- IDbTransaction transaction = null;
-
- try
- {
- transaction = _connection.BeginTransaction();
-
- // First delete chapters
- _deleteStreamsCommand.GetParameter(0).Value = id;
-
- _deleteStreamsCommand.Transaction = transaction;
-
- _deleteStreamsCommand.ExecuteNonQuery();
-
- foreach (var stream in streams)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- var index = 0;
-
- _saveStreamCommand.GetParameter(index++).Value = id;
- _saveStreamCommand.GetParameter(index++).Value = stream.Index;
- _saveStreamCommand.GetParameter(index++).Value = stream.Type.ToString();
- _saveStreamCommand.GetParameter(index++).Value = stream.Codec;
- _saveStreamCommand.GetParameter(index++).Value = stream.Language;
- _saveStreamCommand.GetParameter(index++).Value = stream.ChannelLayout;
- _saveStreamCommand.GetParameter(index++).Value = stream.Profile;
- _saveStreamCommand.GetParameter(index++).Value = stream.AspectRatio;
- _saveStreamCommand.GetParameter(index++).Value = stream.Path;
-
- _saveStreamCommand.GetParameter(index++).Value = stream.IsInterlaced;
-
- _saveStreamCommand.GetParameter(index++).Value = stream.BitRate;
- _saveStreamCommand.GetParameter(index++).Value = stream.Channels;
- _saveStreamCommand.GetParameter(index++).Value = stream.SampleRate;
-
- _saveStreamCommand.GetParameter(index++).Value = stream.IsDefault;
- _saveStreamCommand.GetParameter(index++).Value = stream.IsForced;
- _saveStreamCommand.GetParameter(index++).Value = stream.IsExternal;
-
- _saveStreamCommand.GetParameter(index++).Value = stream.Width;
- _saveStreamCommand.GetParameter(index++).Value = stream.Height;
- _saveStreamCommand.GetParameter(index++).Value = stream.AverageFrameRate;
- _saveStreamCommand.GetParameter(index++).Value = stream.RealFrameRate;
- _saveStreamCommand.GetParameter(index++).Value = stream.Level;
- _saveStreamCommand.GetParameter(index++).Value = stream.PixelFormat;
- _saveStreamCommand.GetParameter(index++).Value = stream.BitDepth;
- _saveStreamCommand.GetParameter(index++).Value = stream.IsAnamorphic;
- _saveStreamCommand.GetParameter(index++).Value = stream.RefFrames;
-
- _saveStreamCommand.GetParameter(index++).Value = stream.CodecTag;
- _saveStreamCommand.GetParameter(index++).Value = stream.Comment;
- _saveStreamCommand.GetParameter(index++).Value = stream.NalLengthSize;
- _saveStreamCommand.GetParameter(index++).Value = stream.IsAVC;
- _saveStreamCommand.GetParameter(index++).Value = stream.Title;
-
- _saveStreamCommand.GetParameter(index++).Value = stream.TimeBase;
- _saveStreamCommand.GetParameter(index++).Value = stream.CodecTimeBase;
-
- _saveStreamCommand.Transaction = transaction;
- _saveStreamCommand.ExecuteNonQuery();
- }
-
- transaction.Commit();
- }
- catch (OperationCanceledException)
+ using (WriteLock.Write())
{
- if (transaction != null)
+ using (var connection = CreateConnection())
{
- transaction.Rollback();
- }
-
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to save media streams:", e);
-
- if (transaction != null)
- {
- transaction.Rollback();
- }
+ // First delete chapters
+ connection.Execute("delete from mediastreams where ItemId=@ItemId", id.ToGuidParamValue());
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
+ using (var statement = PrepareStatement(connection, string.Format("replace into mediastreams ({0}) values ({1})",
+ string.Join(",", _mediaStreamSaveColumns),
+ string.Join(",", _mediaStreamSaveColumns.Select(i => "@" + i).ToArray()))))
+ {
+ foreach (var stream in streams)
+ {
+ var paramList = new List<object>();
+
+ paramList.Add(id.ToGuidParamValue());
+ 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());
+ }
+ }
}
-
- WriteLock.Release();
}
}
@@ -5235,58 +5513,58 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// </summary>
/// <param name="reader">The reader.</param>
/// <returns>ChapterInfo.</returns>
- private MediaStream GetMediaStream(IDataReader reader)
+ private MediaStream GetMediaStream(IReadOnlyList<IResultSetValue> reader)
{
var item = new MediaStream
{
- Index = reader.GetInt32(1)
+ Index = reader[1].ToInt()
};
- item.Type = (MediaStreamType)Enum.Parse(typeof(MediaStreamType), reader.GetString(2), true);
+ item.Type = (MediaStreamType)Enum.Parse(typeof(MediaStreamType), reader[2].ToString(), true);
- if (!reader.IsDBNull(3))
+ if (reader[3].SQLiteType != SQLiteType.Null)
{
- item.Codec = reader.GetString(3);
+ item.Codec = reader[3].ToString();
}
- if (!reader.IsDBNull(4))
+ if (reader[4].SQLiteType != SQLiteType.Null)
{
- item.Language = reader.GetString(4);
+ item.Language = reader[4].ToString();
}
- if (!reader.IsDBNull(5))
+ if (reader[5].SQLiteType != SQLiteType.Null)
{
- item.ChannelLayout = reader.GetString(5);
+ item.ChannelLayout = reader[5].ToString();
}
- if (!reader.IsDBNull(6))
+ if (reader[6].SQLiteType != SQLiteType.Null)
{
- item.Profile = reader.GetString(6);
+ item.Profile = reader[6].ToString();
}
- if (!reader.IsDBNull(7))
+ if (reader[7].SQLiteType != SQLiteType.Null)
{
- item.AspectRatio = reader.GetString(7);
+ item.AspectRatio = reader[7].ToString();
}
- if (!reader.IsDBNull(8))
+ if (reader[8].SQLiteType != SQLiteType.Null)
{
- item.Path = reader.GetString(8);
+ item.Path = reader[8].ToString();
}
item.IsInterlaced = reader.GetBoolean(9);
- if (!reader.IsDBNull(10))
+ if (reader[10].SQLiteType != SQLiteType.Null)
{
item.BitRate = reader.GetInt32(10);
}
- if (!reader.IsDBNull(11))
+ if (reader[11].SQLiteType != SQLiteType.Null)
{
item.Channels = reader.GetInt32(11);
}
- if (!reader.IsDBNull(12))
+ if (reader[12].SQLiteType != SQLiteType.Null)
{
item.SampleRate = reader.GetInt32(12);
}
@@ -5295,84 +5573,84 @@ namespace MediaBrowser.Server.Implementations.Persistence
item.IsForced = reader.GetBoolean(14);
item.IsExternal = reader.GetBoolean(15);
- if (!reader.IsDBNull(16))
+ if (reader[16].SQLiteType != SQLiteType.Null)
{
item.Width = reader.GetInt32(16);
}
- if (!reader.IsDBNull(17))
+ if (reader[17].SQLiteType != SQLiteType.Null)
{
item.Height = reader.GetInt32(17);
}
- if (!reader.IsDBNull(18))
+ if (reader[18].SQLiteType != SQLiteType.Null)
{
item.AverageFrameRate = reader.GetFloat(18);
}
- if (!reader.IsDBNull(19))
+ if (reader[19].SQLiteType != SQLiteType.Null)
{
item.RealFrameRate = reader.GetFloat(19);
}
- if (!reader.IsDBNull(20))
+ if (reader[20].SQLiteType != SQLiteType.Null)
{
item.Level = reader.GetFloat(20);
}
- if (!reader.IsDBNull(21))
+ if (reader[21].SQLiteType != SQLiteType.Null)
{
- item.PixelFormat = reader.GetString(21);
+ item.PixelFormat = reader[21].ToString();
}
- if (!reader.IsDBNull(22))
+ if (reader[22].SQLiteType != SQLiteType.Null)
{
item.BitDepth = reader.GetInt32(22);
}
- if (!reader.IsDBNull(23))
+ if (reader[23].SQLiteType != SQLiteType.Null)
{
item.IsAnamorphic = reader.GetBoolean(23);
}
- if (!reader.IsDBNull(24))
+ if (reader[24].SQLiteType != SQLiteType.Null)
{
item.RefFrames = reader.GetInt32(24);
}
- if (!reader.IsDBNull(25))
+ if (reader[25].SQLiteType != SQLiteType.Null)
{
item.CodecTag = reader.GetString(25);
}
- if (!reader.IsDBNull(26))
+ if (reader[26].SQLiteType != SQLiteType.Null)
{
item.Comment = reader.GetString(26);
}
- if (!reader.IsDBNull(27))
+ if (reader[27].SQLiteType != SQLiteType.Null)
{
item.NalLengthSize = reader.GetString(27);
}
- if (!reader.IsDBNull(28))
+ if (reader[28].SQLiteType != SQLiteType.Null)
{
- item.IsAVC = reader.GetBoolean(28);
+ item.IsAVC = reader[28].ToBool();
}
- if (!reader.IsDBNull(29))
+ if (reader[29].SQLiteType != SQLiteType.Null)
{
- item.Title = reader.GetString(29);
+ item.Title = reader[29].ToString();
}
- if (!reader.IsDBNull(30))
+ if (reader[30].SQLiteType != SQLiteType.Null)
{
- item.TimeBase = reader.GetString(30);
+ item.TimeBase = reader[30].ToString();
}
- if (!reader.IsDBNull(31))
+ if (reader[31].SQLiteType != SQLiteType.Null)
{
- item.CodecTimeBase = reader.GetString(31);
+ item.CodecTimeBase = reader[31].ToString();
}
return item;
diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
new file mode 100644
index 0000000000..2e39b038ae
--- /dev/null
+++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
@@ -0,0 +1,422 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using SQLitePCL.pretty;
+
+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)
+ : base(logger)
+ {
+ _fileSystem = fileSystem;
+ DbFilePath = Path.Combine(appPaths.DataPath, "library.db");
+ _importFile = Path.Combine(appPaths.DataPath, "userdata_v2.db");
+ }
+
+ /// <summary>
+ /// Gets the name of the repository
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name
+ {
+ get
+ {
+ return "SQLite";
+ }
+ }
+
+ /// <summary>
+ /// Opens the connection to the database
+ /// </summary>
+ /// <returns>Task.</returns>
+ public void Initialize(ReaderWriterLockSlim writeLock, ManagedConnection managedConnection)
+ {
+ _connection = managedConnection;
+
+ WriteLock.Dispose();
+ WriteLock = writeLock;
+
+ using (var connection = CreateConnection())
+ {
+ string[] queries = {
+
+ "create table if not exists userdata (key nvarchar, userId GUID, rating float null, played bit, playCount int, isFavorite bit, playbackPositionTicks bigint, 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);
+
+ connection.RunInTransaction(db =>
+ {
+ var existingColumnNames = GetColumnNames(db, "userdata");
+
+ AddColumn(db, "userdata", "AudioStreamIndex", "int", existingColumnNames);
+ AddColumn(db, "userdata", "SubtitleStreamIndex", "int", existingColumnNames);
+ }, TransactionMode);
+
+ ImportUserDataIfNeeded(connection);
+ }
+ }
+
+ protected override bool EnableTempStoreMemory
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ private void ImportUserDataIfNeeded(ManagedConnection connection)
+ {
+ if (!_fileSystem.FileExists(_importFile))
+ {
+ return;
+ }
+
+ var fileToImport = _importFile;
+ var isImported = connection.Query("select IsUserDataImported from DataSettings").SelectScalarBool().FirstOrDefault();
+
+ if (isImported)
+ {
+ return;
+ }
+
+ ImportUserData(connection, fileToImport);
+
+ connection.RunInTransaction(db =>
+ {
+ using (var statement = db.PrepareStatement("replace into DataSettings (IsUserDataImported) values (@IsUserDataImported)"))
+ {
+ statement.TryBind("@IsUserDataImported", true);
+ statement.MoveNext();
+ }
+ }, TransactionMode);
+ }
+
+ private void ImportUserData(ManagedConnection connection, string file)
+ {
+ SqliteExtensions.Attach(connection, file, "UserDataBackup");
+
+ var columns = "key, userId, rating, played, playCount, isFavorite, playbackPositionTicks, lastPlayedDate, AudioStreamIndex, SubtitleStreamIndex";
+
+ connection.RunInTransaction(db =>
+ {
+ db.Execute("REPLACE INTO userdata(" + columns + ") SELECT " + columns + " FROM UserDataBackup.userdata;");
+ }, TransactionMode);
+ }
+
+ /// <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 Task SaveUserData(Guid userId, string key, UserItemData userData, CancellationToken cancellationToken)
+ {
+ if (userData == null)
+ {
+ throw new ArgumentNullException("userData");
+ }
+ if (userId == Guid.Empty)
+ {
+ throw new ArgumentNullException("userId");
+ }
+ if (string.IsNullOrEmpty(key))
+ {
+ throw new ArgumentNullException("key");
+ }
+
+ return PersistUserData(userId, key, userData, cancellationToken);
+ }
+
+ public Task SaveAllUserData(Guid userId, IEnumerable<UserItemData> userData, CancellationToken cancellationToken)
+ {
+ if (userData == null)
+ {
+ throw new ArgumentNullException("userData");
+ }
+ if (userId == Guid.Empty)
+ {
+ throw new ArgumentNullException("userId");
+ }
+
+ return PersistAllUserData(userId, userData.ToList(), cancellationToken);
+ }
+
+ /// <summary>
+ /// Persists 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>
+ public async Task PersistUserData(Guid userId, string key, UserItemData userData, CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ using (WriteLock.Write())
+ {
+ using (var connection = CreateConnection())
+ {
+ connection.RunInTransaction(db =>
+ {
+ SaveUserData(db, userId, key, userData);
+ }, TransactionMode);
+ }
+ }
+ }
+
+ private void SaveUserData(IDatabaseConnection db, Guid userId, 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)"))
+ {
+ statement.TryBind("@userId", userId.ToGuidParamValue());
+ statement.TryBind("@key", key);
+
+ if (userData.Rating.HasValue)
+ {
+ statement.TryBind("@rating", userData.Rating.Value);
+ }
+ else
+ {
+ statement.TryBindNull("@rating");
+ }
+
+ statement.TryBind("@played", userData.Played);
+ statement.TryBind("@playCount", userData.PlayCount);
+ statement.TryBind("@isFavorite", userData.IsFavorite);
+ statement.TryBind("@playbackPositionTicks", userData.PlaybackPositionTicks);
+
+ if (userData.LastPlayedDate.HasValue)
+ {
+ statement.TryBind("@lastPlayedDate", userData.LastPlayedDate.Value.ToDateTimeParamValue());
+ }
+ else
+ {
+ statement.TryBindNull("@lastPlayedDate");
+ }
+
+ if (userData.AudioStreamIndex.HasValue)
+ {
+ statement.TryBind("@AudioStreamIndex", userData.AudioStreamIndex.Value);
+ }
+ else
+ {
+ statement.TryBindNull("@AudioStreamIndex");
+ }
+
+ if (userData.SubtitleStreamIndex.HasValue)
+ {
+ statement.TryBind("@SubtitleStreamIndex", userData.SubtitleStreamIndex.Value);
+ }
+ else
+ {
+ statement.TryBindNull("@SubtitleStreamIndex");
+ }
+
+ statement.MoveNext();
+ }
+ }
+
+ /// <summary>
+ /// Persist all user data for the specified user
+ /// </summary>
+ private async Task PersistAllUserData(Guid userId, List<UserItemData> userDataList, CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ using (WriteLock.Write())
+ {
+ using (var connection = CreateConnection())
+ {
+ connection.RunInTransaction(db =>
+ {
+ foreach (var userItemData in userDataList)
+ {
+ SaveUserData(db, userId, userItemData.Key, userItemData);
+ }
+ }, TransactionMode);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the user data.
+ /// </summary>
+ /// <param name="userId">The user id.</param>
+ /// <param name="key">The key.</param>
+ /// <returns>Task{UserItemData}.</returns>
+ /// <exception cref="System.ArgumentNullException">
+ /// userId
+ /// or
+ /// key
+ /// </exception>
+ public UserItemData GetUserData(Guid userId, string key)
+ {
+ if (userId == Guid.Empty)
+ {
+ throw new ArgumentNullException("userId");
+ }
+ if (string.IsNullOrEmpty(key))
+ {
+ throw new ArgumentNullException("key");
+ }
+
+ using (WriteLock.Read())
+ {
+ 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"))
+ {
+ statement.TryBind("@UserId", userId.ToGuidParamValue());
+ statement.TryBind("@Key", key);
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ return ReadRow(row);
+ }
+ }
+
+ return null;
+ }
+ }
+ }
+
+ public UserItemData GetUserData(Guid userId, List<string> keys)
+ {
+ if (userId == Guid.Empty)
+ {
+ throw new ArgumentNullException("userId");
+ }
+ if (keys == null)
+ {
+ throw new ArgumentNullException("keys");
+ }
+
+ if (keys.Count == 0)
+ {
+ return null;
+ }
+
+ return GetUserData(userId, keys[0]);
+ }
+
+ /// <summary>
+ /// Return all user-data associated with the given user
+ /// </summary>
+ /// <param name="userId"></param>
+ /// <returns></returns>
+ public IEnumerable<UserItemData> GetAllUserData(Guid userId)
+ {
+ if (userId == Guid.Empty)
+ {
+ throw new ArgumentNullException("userId");
+ }
+
+ var list = new List<UserItemData>();
+
+ using (WriteLock.Read())
+ {
+ 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"))
+ {
+ statement.TryBind("@UserId", userId.ToGuidParamValue());
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ list.Add(ReadRow(row));
+ }
+ }
+ }
+ }
+
+ return list;
+ }
+
+ /// <summary>
+ /// Read a row from the specified reader into the provided userData object
+ /// </summary>
+ /// <param name="reader"></param>
+ private UserItemData ReadRow(IReadOnlyList<IResultSetValue> reader)
+ {
+ var userData = new UserItemData();
+
+ userData.Key = reader[0].ToString();
+ userData.UserId = reader[1].ReadGuid();
+
+ if (reader[2].SQLiteType != SQLiteType.Null)
+ {
+ userData.Rating = reader[2].ToDouble();
+ }
+
+ userData.Played = reader[3].ToBool();
+ userData.PlayCount = reader[4].ToInt();
+ userData.IsFavorite = reader[5].ToBool();
+ userData.PlaybackPositionTicks = reader[6].ToInt64();
+
+ if (reader[7].SQLiteType != SQLiteType.Null)
+ {
+ userData.LastPlayedDate = reader[7].ReadDateTime();
+ }
+
+ if (reader[8].SQLiteType != SQLiteType.Null)
+ {
+ userData.AudioStreamIndex = reader[8].ToInt();
+ }
+
+ if (reader[9].SQLiteType != SQLiteType.Null)
+ {
+ userData.SubtitleStreamIndex = reader[9].ToInt();
+ }
+
+ return userData;
+ }
+
+ protected override void Dispose(bool dispose)
+ {
+ // handled by library database
+ }
+
+ protected override void CloseConnection()
+ {
+ // handled by library database
+ }
+ }
+} \ No newline at end of file
diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs
new file mode 100644
index 0000000000..b2b917e5eb
--- /dev/null
+++ b/Emby.Server.Implementations/Data/SqliteUserRepository.cs
@@ -0,0 +1,167 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Serialization;
+using SQLitePCL.pretty;
+
+namespace Emby.Server.Implementations.Data
+{
+ /// <summary>
+ /// Class SQLiteUserRepository
+ /// </summary>
+ public class SqliteUserRepository : BaseSqliteRepository, IUserRepository
+ {
+ private readonly IJsonSerializer _jsonSerializer;
+ private readonly IMemoryStreamFactory _memoryStreamProvider;
+
+ public SqliteUserRepository(ILogger logger, IServerApplicationPaths appPaths, IJsonSerializer jsonSerializer, IMemoryStreamFactory memoryStreamProvider)
+ : base(logger)
+ {
+ _jsonSerializer = jsonSerializer;
+ _memoryStreamProvider = memoryStreamProvider;
+
+ DbFilePath = Path.Combine(appPaths.DataPath, "users.db");
+ }
+
+ /// <summary>
+ /// Gets the name of the repository
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name
+ {
+ get
+ {
+ return "SQLite";
+ }
+ }
+
+ /// <summary>
+ /// Opens the connection to the database
+ /// </summary>
+ /// <returns>Task.</returns>
+ public void Initialize()
+ {
+ using (var connection = CreateConnection())
+ {
+ RunDefaultInitialization(connection);
+
+ string[] queries = {
+
+ "create table if not exists users (guid GUID primary key, data BLOB)",
+ "create index if not exists idx_users on users(guid)",
+ "create table if not exists schema_version (table_name primary key, version)",
+
+ "pragma shrink_memory"
+ };
+
+ connection.RunQueries(queries);
+ }
+ }
+
+ /// <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 async Task SaveUser(User user, CancellationToken cancellationToken)
+ {
+ if (user == null)
+ {
+ throw new ArgumentNullException("user");
+ }
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var serialized = _jsonSerializer.SerializeToBytes(user, _memoryStreamProvider);
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ using (WriteLock.Write())
+ {
+ using (var connection = CreateConnection())
+ {
+ connection.RunInTransaction(db =>
+ {
+ using (var statement = db.PrepareStatement("replace into users (guid, data) values (@guid, @data)"))
+ {
+ statement.TryBind("@guid", user.Id.ToGuidParamValue());
+ statement.TryBind("@data", serialized);
+ statement.MoveNext();
+ }
+ }, TransactionMode);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Retrieve all users from the database
+ /// </summary>
+ /// <returns>IEnumerable{User}.</returns>
+ public IEnumerable<User> RetrieveAllUsers()
+ {
+ var list = new List<User>();
+
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
+ {
+ foreach (var row in connection.Query("select guid,data from users"))
+ {
+ var id = row[0].ReadGuid();
+
+ using (var stream = _memoryStreamProvider.CreateNew(row[1].ToBlob()))
+ {
+ stream.Position = 0;
+ var user = _jsonSerializer.DeserializeFromStream<User>(stream);
+ user.Id = id;
+ list.Add(user);
+ }
+ }
+ }
+ }
+
+ return list;
+ }
+
+ /// <summary>
+ /// Deletes the user.
+ /// </summary>
+ /// <param name="user">The user.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ /// <exception cref="System.ArgumentNullException">user</exception>
+ public async Task DeleteUser(User user, CancellationToken cancellationToken)
+ {
+ 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"))
+ {
+ statement.TryBind("@id", user.Id.ToGuidParamValue());
+ statement.MoveNext();
+ }
+ }, TransactionMode);
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Persistence/TypeMapper.cs b/Emby.Server.Implementations/Data/TypeMapper.cs
index 2de02d8171..f4b37749e7 100644
--- a/MediaBrowser.Server.Implementations/Persistence/TypeMapper.cs
+++ b/Emby.Server.Implementations/Data/TypeMapper.cs
@@ -1,19 +1,27 @@
using System;
using System.Collections.Concurrent;
+using MediaBrowser.Model.Reflection;
using System.Linq;
-namespace MediaBrowser.Server.Implementations.Persistence
+namespace Emby.Server.Implementations.Data
{
/// <summary>
/// Class TypeMapper
/// </summary>
public class TypeMapper
{
+ private readonly IAssemblyInfo _assemblyInfo;
+
/// <summary>
/// This holds all the types in the running assemblies so that we can de-serialize properly when we don't have strong types
/// </summary>
private readonly ConcurrentDictionary<string, Type> _typeMap = new ConcurrentDictionary<string, Type>();
+ public TypeMapper(IAssemblyInfo assemblyInfo)
+ {
+ _assemblyInfo = assemblyInfo;
+ }
+
/// <summary>
/// Gets the type.
/// </summary>
@@ -24,7 +32,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
if (string.IsNullOrEmpty(typeName))
{
- throw new ArgumentNullException();
+ throw new ArgumentNullException("typeName");
}
return _typeMap.GetOrAdd(typeName, LookupType);
@@ -37,11 +45,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// <returns>Type.</returns>
private Type LookupType(string typeName)
{
- return AppDomain
- .CurrentDomain
- .GetAssemblies()
- .Select(a => a.GetType(typeName, false))
- .FirstOrDefault(t => t != null);
+ return _assemblyInfo
+ .GetCurrentAssemblies()
+ .Select(a => a.GetType(typeName))
+ .FirstOrDefault(t => t != null);
}
}
}
diff --git a/Emby.Server.Implementations/Devices/CameraUploadsDynamicFolder.cs b/Emby.Server.Implementations/Devices/CameraUploadsDynamicFolder.cs
new file mode 100644
index 0000000000..e2d5d0272f
--- /dev/null
+++ b/Emby.Server.Implementations/Devices/CameraUploadsDynamicFolder.cs
@@ -0,0 +1,41 @@
+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.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Serialization;
+using MediaBrowser.Server.Implementations.Devices;
+
+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/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs
index c3db9140cf..88c0ea2035 100644
--- a/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs
+++ b/Emby.Server.Implementations/Devices/DeviceManager.cs
@@ -16,10 +16,12 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.IO;
-namespace MediaBrowser.Server.Implementations.Devices
+namespace Emby.Server.Implementations.Devices
{
public class DeviceManager : IDeviceManager
{
@@ -99,13 +101,6 @@ namespace MediaBrowser.Server.Implementations.Devices
{
IEnumerable<DeviceInfo> devices = _repo.GetDevices().OrderByDescending(i => i.DateLastModified);
- if (query.SupportsContentUploading.HasValue)
- {
- var val = query.SupportsContentUploading.Value;
-
- devices = devices.Where(i => GetCapabilities(i.Id).SupportsContentUploading == val);
- }
-
if (query.SupportsSync.HasValue)
{
var val = query.SupportsSync.Value;
@@ -167,7 +162,7 @@ namespace MediaBrowser.Server.Implementations.Devices
try
{
- using (var fs = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
+ using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
{
await stream.CopyToAsync(fs).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Server.Implementations/Devices/DeviceRepository.cs b/Emby.Server.Implementations/Devices/DeviceRepository.cs
index 6e67af82b4..f739765b34 100644
--- a/MediaBrowser.Server.Implementations/Devices/DeviceRepository.cs
+++ b/Emby.Server.Implementations/Devices/DeviceRepository.cs
@@ -1,18 +1,18 @@
-using MediaBrowser.Common.Configuration;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Model.Devices;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Session;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using CommonIO;
-namespace MediaBrowser.Server.Implementations.Devices
+namespace Emby.Server.Implementations.Devices
{
public class DeviceRepository : IDeviceRepository
{
@@ -147,7 +147,7 @@ namespace MediaBrowser.Server.Implementations.Devices
{
_fileSystem.DeleteDirectory(path, true);
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
}
diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index a0f7aa999e..8e6c1263d6 100644
--- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -19,15 +19,17 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Sync;
-using MoreLinq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Extensions;
-namespace MediaBrowser.Server.Implementations.Dto
+namespace Emby.Server.Implementations.Dto
{
public class DtoService : IDtoService
{
@@ -457,12 +459,21 @@ namespace MediaBrowser.Server.Implementations.Dto
if (dtoOptions.EnableUserData)
{
- dto.UserData = await _userDataRepository.GetUserDataDto(item, dto, user).ConfigureAwait(false);
+ dto.UserData = await _userDataRepository.GetUserDataDto(item, dto, user, dtoOptions.Fields).ConfigureAwait(false);
}
if (!dto.ChildCount.HasValue && item.SourceType == SourceType.Library)
{
- dto.ChildCount = GetChildCount(folder, user);
+ // For these types we can try to optimize and assume these values will be equal
+ if (item is MusicAlbum || item is Season)
+ {
+ dto.ChildCount = dto.RecursiveItemCount;
+ }
+
+ if (dtoOptions.Fields.Contains(ItemFields.ChildCount))
+ {
+ dto.ChildCount = dto.ChildCount ?? GetChildCount(folder, user);
+ }
}
if (fields.Contains(ItemFields.CumulativeRunTimeTicks))
@@ -480,7 +491,7 @@ namespace MediaBrowser.Server.Implementations.Dto
{
if (dtoOptions.EnableUserData)
{
- dto.UserData = _userDataRepository.GetUserDataDto(item, user).Result;
+ dto.UserData = await _userDataRepository.GetUserDataDto(item, user).ConfigureAwait(false);
}
}
@@ -1149,28 +1160,29 @@ namespace MediaBrowser.Server.Implementations.Dto
{
dto.Artists = hasArtist.Artists;
- var artistItems = _libraryManager.GetArtists(new InternalItemsQuery
- {
- EnableTotalRecordCount = false,
- ItemIds = new[] { item.Id.ToString("N") }
- });
-
- dto.ArtistItems = artistItems.Items
- .Select(i =>
- {
- var artist = i.Item1;
- return new NameIdPair
- {
- Name = artist.Name,
- Id = artist.Id.ToString("N")
- };
- })
- .ToList();
+ //var artistItems = _libraryManager.GetArtists(new InternalItemsQuery
+ //{
+ // EnableTotalRecordCount = false,
+ // ItemIds = new[] { item.Id.ToString("N") }
+ //});
+
+ //dto.ArtistItems = artistItems.Items
+ // .Select(i =>
+ // {
+ // var artist = i.Item1;
+ // return new NameIdPair
+ // {
+ // Name = artist.Name,
+ // Id = artist.Id.ToString("N")
+ // };
+ // })
+ // .ToList();
// Include artists that are not in the database yet, e.g., just added via metadata editor
- var foundArtists = artistItems.Items.Select(i => i.Item1.Name).ToList();
+ //var foundArtists = artistItems.Items.Select(i => i.Item1.Name).ToList();
+ dto.ArtistItems = new List<NameIdPair>();
dto.ArtistItems.AddRange(hasArtist.Artists
- .Except(foundArtists, new DistinctNameComparer())
+ //.Except(foundArtists, new DistinctNameComparer())
.Select(i =>
{
// This should not be necessary but we're seeing some cases of it
@@ -1199,23 +1211,48 @@ namespace MediaBrowser.Server.Implementations.Dto
{
dto.AlbumArtist = hasAlbumArtist.AlbumArtists.FirstOrDefault();
- var artistItems = _libraryManager.GetAlbumArtists(new InternalItemsQuery
- {
- EnableTotalRecordCount = false,
- ItemIds = new[] { item.Id.ToString("N") }
- });
-
- dto.AlbumArtists = artistItems.Items
+ //var artistItems = _libraryManager.GetAlbumArtists(new InternalItemsQuery
+ //{
+ // EnableTotalRecordCount = false,
+ // ItemIds = new[] { item.Id.ToString("N") }
+ //});
+
+ //dto.AlbumArtists = artistItems.Items
+ // .Select(i =>
+ // {
+ // var artist = i.Item1;
+ // return new NameIdPair
+ // {
+ // Name = artist.Name,
+ // Id = artist.Id.ToString("N")
+ // };
+ // })
+ // .ToList();
+
+ dto.AlbumArtists = new List<NameIdPair>();
+ dto.AlbumArtists.AddRange(hasAlbumArtist.AlbumArtists
+ //.Except(foundArtists, new DistinctNameComparer())
.Select(i =>
{
- var artist = i.Item1;
- return new NameIdPair
+ // This should not be necessary but we're seeing some cases of it
+ if (string.IsNullOrWhiteSpace(i))
+ {
+ return null;
+ }
+
+ var artist = _libraryManager.GetArtist(i);
+ if (artist != null)
{
- Name = artist.Name,
- Id = artist.Id.ToString("N")
- };
- })
- .ToList();
+ return new NameIdPair
+ {
+ Name = artist.Name,
+ Id = artist.Id.ToString("N")
+ };
+ }
+
+ return null;
+
+ }).Where(i => i != null));
}
// Add video info
@@ -1349,6 +1386,27 @@ namespace MediaBrowser.Server.Implementations.Dto
if (episodeSeries != null)
{
dto.SeriesStudio = episodeSeries.Studios.FirstOrDefault();
+ if (!string.IsNullOrWhiteSpace(dto.SeriesStudio))
+ {
+ try
+ {
+ var studio = _libraryManager.GetStudio(dto.SeriesStudio);
+
+ if (studio != null)
+ {
+ dto.SeriesStudioInfo = new StudioDto
+ {
+ Name = dto.SeriesStudio,
+ Id = studio.Id.ToString("N"),
+ PrimaryImageTag = GetImageCacheTag(studio, ImageType.Primary)
+ };
+ }
+ }
+ catch (Exception ex)
+ {
+
+ }
+ }
}
}
}
@@ -1446,13 +1504,35 @@ namespace MediaBrowser.Server.Implementations.Dto
}
}
+ private BaseItem GetImageDisplayParent(BaseItem item)
+ {
+ var musicAlbum = item as MusicAlbum;
+ if (musicAlbum != null)
+ {
+ var artist = musicAlbum.MusicArtist;
+ if (artist != null)
+ {
+ return artist;
+ }
+ }
+ return item.GetParent();
+ }
+
private void AddInheritedImages(BaseItemDto dto, BaseItem item, DtoOptions options, BaseItem owner)
{
+ if (!item.SupportsInheritedParentImages)
+ {
+ return;
+ }
+
var logoLimit = options.GetImageLimit(ImageType.Logo);
var artLimit = options.GetImageLimit(ImageType.Art);
var thumbLimit = options.GetImageLimit(ImageType.Thumb);
var backdropLimit = options.GetImageLimit(ImageType.Backdrop);
+ // For now. Emby apps are not using this
+ artLimit = 0;
+
if (logoLimit == 0 && artLimit == 0 && thumbLimit == 0 && backdropLimit == 0)
{
return;
@@ -1462,7 +1542,7 @@ namespace MediaBrowser.Server.Implementations.Dto
var isFirst = true;
while (((!dto.HasLogo && logoLimit > 0) || (!dto.HasArtImage && artLimit > 0) || (!dto.HasThumb && thumbLimit > 0) || parent is Series) &&
- (parent = parent ?? (isFirst ? item.GetParent() ?? owner : parent)) != null)
+ (parent = parent ?? (isFirst ? GetImageDisplayParent(item) ?? owner : parent)) != null)
{
if (parent == null)
{
@@ -1513,7 +1593,13 @@ namespace MediaBrowser.Server.Implementations.Dto
}
isFirst = false;
- parent = parent.GetParent();
+
+ if (!parent.SupportsInheritedParentImages)
+ {
+ break;
+ }
+
+ parent = GetImageDisplayParent(parent);
}
}
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
new file mode 100644
index 0000000000..d773fbbf73
--- /dev/null
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -0,0 +1,435 @@
+<?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>{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>
+ <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="Activity\ActivityLogEntryPoint.cs" />
+ <Compile Include="Activity\ActivityManager.cs" />
+ <Compile Include="Activity\ActivityRepository.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="Connect\ConnectData.cs" />
+ <Compile Include="Connect\ConnectEntryPoint.cs" />
+ <Compile Include="Connect\ConnectManager.cs" />
+ <Compile Include="Connect\Responses.cs" />
+ <Compile Include="Connect\Validator.cs" />
+ <Compile Include="Data\ManagedConnection.cs" />
+ <Compile Include="Data\SqliteDisplayPreferencesRepository.cs" />
+ <Compile Include="Data\SqliteFileOrganizationRepository.cs" />
+ <Compile Include="Data\SqliteItemRepository.cs" />
+ <Compile Include="Data\SqliteUserDataRepository.cs" />
+ <Compile Include="Data\SqliteUserRepository.cs" />
+ <Compile Include="Data\TypeMapper.cs" />
+ <Compile Include="Devices\CameraUploadsDynamicFolder.cs" />
+ <Compile Include="Devices\DeviceManager.cs" />
+ <Compile Include="Devices\DeviceRepository.cs" />
+ <Compile Include="Dto\DtoService.cs" />
+ <Compile Include="EntryPoints\AutomaticRestartEntryPoint.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="FFMpeg\FFMpegInfo.cs" />
+ <Compile Include="FFMpeg\FFMpegInstallInfo.cs" />
+ <Compile Include="FFMpeg\FFMpegLoader.cs" />
+ <Compile Include="FileOrganization\EpisodeFileOrganizer.cs" />
+ <Compile Include="FileOrganization\Extensions.cs" />
+ <Compile Include="FileOrganization\FileOrganizationNotifier.cs" />
+ <Compile Include="FileOrganization\FileOrganizationService.cs" />
+ <Compile Include="FileOrganization\NameUtils.cs" />
+ <Compile Include="FileOrganization\OrganizerScheduledTask.cs" />
+ <Compile Include="FileOrganization\TvFolderOrganizer.cs" />
+ <Compile Include="HttpServer\GetSwaggerResource.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="HttpServer\SwaggerService.cs" />
+ <Compile Include="Images\BaseDynamicImageProvider.cs" />
+ <Compile Include="Intros\DefaultIntroProvider.cs" />
+ <Compile Include="IO\FileRefresher.cs" />
+ <Compile Include="IO\MbLinkShortcutHandler.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="Library\Validators\YearsPostScanTask.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\ProgramImageProvider.cs" />
+ <Compile Include="LiveTv\RecordingImageProvider.cs" />
+ <Compile Include="LiveTv\RefreshChannelsScheduledTask.cs" />
+ <Compile Include="LiveTv\TunerHosts\BaseTunerHost.cs" />
+ <Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunDiscovery.cs" />
+ <Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunHost.cs" />
+ <Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunLiveStream.cs" />
+ <Compile Include="LiveTv\TunerHosts\M3uParser.cs" />
+ <Compile Include="LiveTv\TunerHosts\M3UTunerHost.cs" />
+ <Compile Include="LiveTv\TunerHosts\MulticastStream.cs" />
+ <Compile Include="LiveTv\TunerHosts\QueueStream.cs" />
+ <Compile Include="Localization\LocalizationManager.cs" />
+ <Compile Include="MediaEncoder\EncodingManager.cs" />
+ <Compile Include="Migrations\IVersionMigration.cs" />
+ <Compile Include="Migrations\LibraryScanMigration.cs" />
+ <Compile Include="Migrations\UpdateLevelMigration.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="Photos\PhotoAlbumImageProvider.cs" />
+ <Compile Include="Playlists\PlaylistImageProvider.cs" />
+ <Compile Include="Playlists\PlaylistManager.cs" />
+ <Compile Include="Playlists\PlaylistsDynamicFolder.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="ScheduledTasks\ChapterImagesTask.cs" />
+ <Compile Include="ScheduledTasks\PeopleValidationTask.cs" />
+ <Compile Include="ScheduledTasks\PluginUpdateTask.cs" />
+ <Compile Include="ScheduledTasks\RefreshIntrosTask.cs" />
+ <Compile Include="ScheduledTasks\RefreshMediaLibraryTask.cs" />
+ <Compile Include="ScheduledTasks\SystemUpdateTask.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="ServerManager\ServerManager.cs" />
+ <Compile Include="ServerManager\WebSocketConnection.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\AirTimeComparer.cs" />
+ <Compile Include="Sorting\AlbumArtistComparer.cs" />
+ <Compile Include="Sorting\AlbumComparer.cs" />
+ <Compile Include="Sorting\AlphanumComparator.cs" />
+ <Compile Include="Sorting\ArtistComparer.cs" />
+ <Compile Include="Sorting\BudgetComparer.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\MetascoreComparer.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\RevenueComparer.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="Sync\AppSyncProvider.cs" />
+ <Compile Include="Sync\CloudSyncProfile.cs" />
+ <Compile Include="Sync\IHasSyncQuality.cs" />
+ <Compile Include="Sync\MediaSync.cs" />
+ <Compile Include="Sync\MultiProviderSync.cs" />
+ <Compile Include="Sync\ServerSyncScheduledTask.cs" />
+ <Compile Include="Sync\SyncConfig.cs" />
+ <Compile Include="Sync\SyncConvertScheduledTask.cs" />
+ <Compile Include="Sync\SyncedMediaSourceProvider.cs" />
+ <Compile Include="Sync\SyncHelper.cs" />
+ <Compile Include="Sync\SyncJobOptions.cs" />
+ <Compile Include="Sync\SyncJobProcessor.cs" />
+ <Compile Include="Sync\SyncManager.cs" />
+ <Compile Include="Sync\SyncNotificationEntryPoint.cs" />
+ <Compile Include="Sync\SyncRegistrationInfo.cs" />
+ <Compile Include="Sync\SyncRepository.cs" />
+ <Compile Include="Sync\TargetDataProvider.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" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Localization\iso6392.txt" />
+ </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="..\MediaBrowser.Providers\MediaBrowser.Providers.csproj">
+ <Project>{442b5058-dcaf-4263-bb6a-f21e31120a1b}</Project>
+ <Name>MediaBrowser.Providers</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\MediaBrowser.Server.Implementations\MediaBrowser.Server.Implementations.csproj">
+ <Project>{2e781478-814d-4a48-9d80-bff206441a65}</Project>
+ <Name>MediaBrowser.Server.Implementations</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\ServiceStack\ServiceStack.csproj">
+ <Project>{680a1709-25eb-4d52-a87f-ee03ffd94baa}</Project>
+ <Name>ServiceStack</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\SocketHttpListener.Portable\SocketHttpListener.Portable.csproj">
+ <Project>{4f26d5d8-a7b0-42b3-ba42-7cb7d245934e}</Project>
+ <Name>SocketHttpListener.Portable</Name>
+ </ProjectReference>
+ <Reference Include="Emby.XmlTv, Version=1.0.6193.39741, Culture=neutral, processorArchitecture=MSIL">
+ <HintPath>..\packages\Emby.XmlTv.1.0.3\lib\portable-net45+win8\Emby.XmlTv.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="MediaBrowser.Naming, Version=1.0.6178.4191, Culture=neutral, processorArchitecture=MSIL">
+ <HintPath>..\packages\MediaBrowser.Naming.1.0.3\lib\portable-net45+win8\MediaBrowser.Naming.dll</HintPath>
+ <Private>True</Private>
+ </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>
+ <Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
+ <HintPath>..\packages\SQLitePCLRaw.core.1.1.1\lib\portable-net45+netcore45+wpa81+MonoAndroid10+MonoTouch10+Xamarin.iOS10\SQLitePCLRaw.core.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="UniversalDetector, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+ <HintPath>..\packages\UniversalDetector.1.0.1\lib\portable-net45+sl4+wp71+win8+wpa81\UniversalDetector.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Localization\Core\ar.json" />
+ <EmbeddedResource Include="Localization\Core\bg-BG.json" />
+ <EmbeddedResource Include="Localization\Core\ca.json" />
+ <EmbeddedResource Include="Localization\Core\core.json" />
+ <EmbeddedResource Include="Localization\Core\cs.json" />
+ <EmbeddedResource Include="Localization\Core\da.json" />
+ <EmbeddedResource Include="Localization\Core\de.json" />
+ <EmbeddedResource Include="Localization\Core\el.json" />
+ <EmbeddedResource Include="Localization\Core\en-GB.json" />
+ <EmbeddedResource Include="Localization\Core\en-US.json" />
+ <EmbeddedResource Include="Localization\Core\es-AR.json" />
+ <EmbeddedResource Include="Localization\Core\es-MX.json" />
+ <EmbeddedResource Include="Localization\Core\es.json" />
+ <EmbeddedResource Include="Localization\Core\fi.json" />
+ <EmbeddedResource Include="Localization\Core\fr-CA.json" />
+ <EmbeddedResource Include="Localization\Core\fr.json" />
+ <EmbeddedResource Include="Localization\Core\gsw.json" />
+ <EmbeddedResource Include="Localization\Core\he.json" />
+ <EmbeddedResource Include="Localization\Core\hr.json" />
+ <EmbeddedResource Include="Localization\Core\hu.json" />
+ <EmbeddedResource Include="Localization\Core\id.json" />
+ <EmbeddedResource Include="Localization\Core\it.json" />
+ <EmbeddedResource Include="Localization\Core\kk.json" />
+ <EmbeddedResource Include="Localization\Core\ko.json" />
+ <EmbeddedResource Include="Localization\Core\ms.json" />
+ <EmbeddedResource Include="Localization\Core\nb.json" />
+ <EmbeddedResource Include="Localization\Core\nl.json" />
+ <EmbeddedResource Include="Localization\Core\pl.json" />
+ <EmbeddedResource Include="Localization\Core\pt-BR.json" />
+ <EmbeddedResource Include="Localization\Core\pt-PT.json" />
+ <EmbeddedResource Include="Localization\Core\ro.json" />
+ <EmbeddedResource Include="Localization\Core\ru.json" />
+ <EmbeddedResource Include="Localization\Core\sl-SI.json" />
+ <EmbeddedResource Include="Localization\Core\sv.json" />
+ <EmbeddedResource Include="Localization\Core\tr.json" />
+ <EmbeddedResource Include="Localization\Core\uk.json" />
+ <EmbeddedResource Include="Localization\Core\vi.json" />
+ <EmbeddedResource Include="Localization\Core\zh-CN.json" />
+ <EmbeddedResource Include="Localization\Core\zh-HK.json" />
+ <EmbeddedResource Include="Localization\Core\zh-TW.json" />
+ <EmbeddedResource Include="Localization\countries.json" />
+ <None Include="packages.config" />
+ </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>
+ <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.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs
index d5f265ddad..38708648fe 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs
+++ b/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs
@@ -1,5 +1,4 @@
-using MediaBrowser.Common.ScheduledTasks;
-using MediaBrowser.Controller;
+using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
@@ -11,8 +10,9 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.LiveTv;
+using MediaBrowser.Model.Threading;
-namespace MediaBrowser.Server.Implementations.EntryPoints
+namespace Emby.Server.Implementations.EntryPoints
{
public class AutomaticRestartEntryPoint : IServerEntryPoint
{
@@ -22,10 +22,11 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
private readonly ISessionManager _sessionManager;
private readonly IServerConfigurationManager _config;
private readonly ILiveTvManager _liveTvManager;
+ private readonly ITimerFactory _timerFactory;
- private Timer _timer;
+ private ITimer _timer;
- public AutomaticRestartEntryPoint(IServerApplicationHost appHost, ILogger logger, ITaskManager iTaskManager, ISessionManager sessionManager, IServerConfigurationManager config, ILiveTvManager liveTvManager)
+ public AutomaticRestartEntryPoint(IServerApplicationHost appHost, ILogger logger, ITaskManager iTaskManager, ISessionManager sessionManager, IServerConfigurationManager config, ILiveTvManager liveTvManager, ITimerFactory timerFactory)
{
_appHost = appHost;
_logger = logger;
@@ -33,6 +34,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
_sessionManager = sessionManager;
_config = config;
_liveTvManager = liveTvManager;
+ _timerFactory = timerFactory;
}
public void Run()
@@ -49,7 +51,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
if (_appHost.HasPendingRestart)
{
- _timer = new Timer(TimerCallback, null, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10));
+ _timer = _timerFactory.Create(TimerCallback, null, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10));
}
}
diff --git a/Emby.Server.Implementations/EntryPoints/KeepServerAwake.cs b/Emby.Server.Implementations/EntryPoints/KeepServerAwake.cs
new file mode 100644
index 0000000000..8ae85e3909
--- /dev/null
+++ b/Emby.Server.Implementations/EntryPoints/KeepServerAwake.cs
@@ -0,0 +1,65 @@
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Logging;
+using System;
+using System.Linq;
+using MediaBrowser.Model.System;
+using MediaBrowser.Model.Threading;
+
+namespace Emby.Server.Implementations.EntryPoints
+{
+ public class KeepServerAwake : IServerEntryPoint
+ {
+ private readonly ISessionManager _sessionManager;
+ private readonly ILogger _logger;
+ private ITimer _timer;
+ private readonly IServerApplicationHost _appHost;
+ private readonly ITimerFactory _timerFactory;
+ private readonly IPowerManagement _powerManagement;
+
+ public KeepServerAwake(ISessionManager sessionManager, ILogger logger, IServerApplicationHost appHost, ITimerFactory timerFactory, IPowerManagement powerManagement)
+ {
+ _sessionManager = sessionManager;
+ _logger = logger;
+ _appHost = appHost;
+ _timerFactory = timerFactory;
+ _powerManagement = powerManagement;
+ }
+
+ public void Run()
+ {
+ _timer = _timerFactory.Create(OnTimerCallback, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
+ }
+
+ private void OnTimerCallback(object state)
+ {
+ var now = DateTime.UtcNow;
+
+ try
+ {
+ if (_sessionManager.Sessions.Any(i => (now - i.LastActivityDate).TotalMinutes < 15))
+ {
+ _powerManagement.PreventSystemStandby();
+ }
+ else
+ {
+ _powerManagement.AllowSystemStandby();
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error resetting system standby timer", ex);
+ }
+ }
+
+ public void Dispose()
+ {
+ if (_timer != null)
+ {
+ _timer.Dispose();
+ _timer = null;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
index afc4e9702e..91142f9284 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
@@ -4,14 +4,15 @@ using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
-using MoreLinq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.Threading;
-namespace MediaBrowser.Server.Implementations.EntryPoints
+namespace Emby.Server.Implementations.EntryPoints
{
public class LibraryChangedNotifier : IServerEntryPoint
{
@@ -23,6 +24,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
private readonly ISessionManager _sessionManager;
private readonly IUserManager _userManager;
private readonly ILogger _logger;
+ private readonly ITimerFactory _timerFactory;
/// <summary>
/// The _library changed sync lock
@@ -40,19 +42,20 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
/// Gets or sets the library update timer.
/// </summary>
/// <value>The library update timer.</value>
- private Timer LibraryUpdateTimer { get; set; }
+ private ITimer LibraryUpdateTimer { get; set; }
/// <summary>
/// The library update duration
/// </summary>
private const int LibraryUpdateDuration = 5000;
- public LibraryChangedNotifier(ILibraryManager libraryManager, ISessionManager sessionManager, IUserManager userManager, ILogger logger)
+ public LibraryChangedNotifier(ILibraryManager libraryManager, ISessionManager sessionManager, IUserManager userManager, ILogger logger, ITimerFactory timerFactory)
{
_libraryManager = libraryManager;
_sessionManager = sessionManager;
_userManager = userManager;
_logger = logger;
+ _timerFactory = timerFactory;
}
public void Run()
@@ -79,7 +82,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
{
if (LibraryUpdateTimer == null)
{
- LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration,
+ LibraryUpdateTimer = _timerFactory.Create(LibraryUpdateTimerCallback, null, LibraryUpdateDuration,
Timeout.Infinite);
}
else
@@ -112,7 +115,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
{
if (LibraryUpdateTimer == null)
{
- LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration,
+ LibraryUpdateTimer = _timerFactory.Create(LibraryUpdateTimerCallback, null, LibraryUpdateDuration,
Timeout.Infinite);
}
else
@@ -140,7 +143,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
{
if (LibraryUpdateTimer == null)
{
- LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration,
+ LibraryUpdateTimer = _timerFactory.Create(LibraryUpdateTimerCallback, null, LibraryUpdateDuration,
Timeout.Infinite);
}
else
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/LoadRegistrations.cs b/Emby.Server.Implementations/EntryPoints/LoadRegistrations.cs
index f41d81137f..0203b51928 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/LoadRegistrations.cs
+++ b/Emby.Server.Implementations/EntryPoints/LoadRegistrations.cs
@@ -3,9 +3,9 @@ using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Logging;
using System;
using System.Threading.Tasks;
-using MediaBrowser.Common.Threading;
+using MediaBrowser.Model.Threading;
-namespace MediaBrowser.Server.Implementations.EntryPoints
+namespace Emby.Server.Implementations.EntryPoints
{
/// <summary>
/// Class LoadRegistrations
@@ -22,16 +22,18 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
/// </summary>
private readonly ILogger _logger;
- private PeriodicTimer _timer;
+ 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)
+ public LoadRegistrations(ISecurityManager securityManager, ILogManager logManager, ITimerFactory timerFactory)
{
_securityManager = securityManager;
+ _timerFactory = timerFactory;
_logger = logManager.GetLogger("Registration Loader");
}
@@ -41,7 +43,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
/// </summary>
public void Run()
{
- _timer = new PeriodicTimer(s => LoadAllRegistrations(), null, TimeSpan.FromMilliseconds(100), TimeSpan.FromHours(12));
+ _timer = _timerFactory.Create(s => LoadAllRegistrations(), null, TimeSpan.FromMilliseconds(100), TimeSpan.FromHours(12));
}
private async Task LoadAllRegistrations()
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/RecordingNotifier.cs b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
index 414fda400b..b674fc39bb 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/RecordingNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs
@@ -7,7 +7,7 @@ using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Logging;
-namespace MediaBrowser.Server.Implementations.EntryPoints
+namespace Emby.Server.Implementations.EntryPoints
{
public class RecordingNotifier : IServerEntryPoint
{
@@ -32,22 +32,22 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
_liveTvManager.SeriesTimerCreated += _liveTvManager_SeriesTimerCreated;
}
- private void _liveTvManager_SeriesTimerCreated(object sender, Model.Events.GenericEventArgs<TimerEventInfo> e)
+ private void _liveTvManager_SeriesTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
{
SendMessage("SeriesTimerCreated", e.Argument);
}
- private void _liveTvManager_TimerCreated(object sender, Model.Events.GenericEventArgs<TimerEventInfo> e)
+ private void _liveTvManager_TimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
{
SendMessage("TimerCreated", e.Argument);
}
- private void _liveTvManager_SeriesTimerCancelled(object sender, Model.Events.GenericEventArgs<TimerEventInfo> e)
+ private void _liveTvManager_SeriesTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
{
SendMessage("SeriesTimerCancelled", e.Argument);
}
- private void _liveTvManager_TimerCancelled(object sender, Model.Events.GenericEventArgs<TimerEventInfo> e)
+ private void _liveTvManager_TimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e)
{
SendMessage("TimerCancelled", e.Argument);
}
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs b/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs
index a0b7ff515c..77de849a17 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs
+++ b/Emby.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs
@@ -2,7 +2,7 @@
using MediaBrowser.Controller.Plugins;
using System.Threading;
-namespace MediaBrowser.Server.Implementations.EntryPoints
+namespace Emby.Server.Implementations.EntryPoints
{
/// <summary>
/// Class RefreshUsersMetadata
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/ServerEventNotifier.cs b/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
index 3ea8417f86..4d640bc95d 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/ServerEventNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
@@ -1,5 +1,4 @@
using MediaBrowser.Common.Plugins;
-using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
@@ -13,8 +12,9 @@ using MediaBrowser.Model.Sync;
using System;
using System.Collections.Generic;
using System.Threading;
+using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Server.Implementations.EntryPoints
+namespace Emby.Server.Implementations.EntryPoints
{
/// <summary>
/// Class WebSocketEvents
diff --git a/MediaBrowser.Server.Startup.Common/EntryPoints/StartupWizard.cs b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
index f9d173c595..424153f220 100644
--- a/MediaBrowser.Server.Startup.Common/EntryPoints/StartupWizard.cs
+++ b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs
@@ -1,9 +1,9 @@
-using MediaBrowser.Controller;
+using Emby.Server.Implementations.Browser;
+using MediaBrowser.Controller;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Logging;
-using MediaBrowser.Server.Startup.Common.Browser;
-namespace MediaBrowser.Server.Startup.Common.EntryPoints
+namespace Emby.Server.Implementations.EntryPoints
{
/// <summary>
/// Class StartupWizard
diff --git a/Emby.Server.Implementations/EntryPoints/SystemEvents.cs b/Emby.Server.Implementations/EntryPoints/SystemEvents.cs
new file mode 100644
index 0000000000..021ae47ec3
--- /dev/null
+++ b/Emby.Server.Implementations/EntryPoints/SystemEvents.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using MediaBrowser.Model.System;
+using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Common;
+
+namespace Emby.Server.Implementations.EntryPoints
+{
+ public class SystemEvents : IServerEntryPoint
+ {
+ private readonly ISystemEvents _systemEvents;
+ private readonly IApplicationHost _appHost;
+
+ public SystemEvents(ISystemEvents systemEvents, IApplicationHost appHost)
+ {
+ _systemEvents = systemEvents;
+ _appHost = appHost;
+ }
+
+ public void Run()
+ {
+ _systemEvents.SessionLogoff += _systemEvents_SessionLogoff;
+ _systemEvents.SystemShutdown += _systemEvents_SystemShutdown;
+ }
+
+ private void _systemEvents_SessionLogoff(object sender, EventArgs e)
+ {
+ if (!_appHost.IsRunningAsService)
+ {
+ _appHost.Shutdown();
+ }
+ }
+
+ private void _systemEvents_SystemShutdown(object sender, EventArgs e)
+ {
+ _appHost.Shutdown();
+ }
+
+ public void Dispose()
+ {
+ _systemEvents.SystemShutdown -= _systemEvents_SystemShutdown;
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
index 386c16513b..df5a7c985d 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
+++ b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
@@ -1,12 +1,12 @@
-using MediaBrowser.Common.Net;
+using System;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Server.Implementations.Udp;
-using System.Net.Sockets;
+using Emby.Server.Implementations.Udp;
+using MediaBrowser.Model.Net;
-namespace MediaBrowser.Server.Implementations.EntryPoints
+namespace Emby.Server.Implementations.EntryPoints
{
/// <summary>
/// Class UdpServerEntryPoint
@@ -23,10 +23,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
/// The _logger
/// </summary>
private readonly ILogger _logger;
- /// <summary>
- /// The _network manager
- /// </summary>
- private readonly INetworkManager _networkManager;
+ private readonly ISocketFactory _socketFactory;
private readonly IServerApplicationHost _appHost;
private readonly IJsonSerializer _json;
@@ -35,16 +32,12 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
/// <summary>
/// Initializes a new instance of the <see cref="UdpServerEntryPoint" /> class.
/// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="networkManager">The network manager.</param>
- /// <param name="appHost">The application host.</param>
- /// <param name="json">The json.</param>
- public UdpServerEntryPoint(ILogger logger, INetworkManager networkManager, IServerApplicationHost appHost, IJsonSerializer json)
+ public UdpServerEntryPoint(ILogger logger, IServerApplicationHost appHost, IJsonSerializer json, ISocketFactory socketFactory)
{
_logger = logger;
- _networkManager = networkManager;
_appHost = appHost;
_json = json;
+ _socketFactory = socketFactory;
}
/// <summary>
@@ -52,7 +45,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
/// </summary>
public void Run()
{
- var udpServer = new UdpServer(_logger, _networkManager, _appHost, _json);
+ var udpServer = new UdpServer(_logger, _appHost, _json, _socketFactory);
try
{
@@ -60,7 +53,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
UdpServer = udpServer;
}
- catch (SocketException ex)
+ catch (Exception ex)
{
_logger.ErrorException("Failed to start UDP Server", ex);
}
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs
index d14bd43689..1b897ca299 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs
+++ b/Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs
@@ -12,7 +12,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration;
-namespace MediaBrowser.Server.Implementations.EntryPoints
+namespace Emby.Server.Implementations.EntryPoints
{
/// <summary>
/// Class UsageEntryPoint
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/UsageReporter.cs b/Emby.Server.Implementations/EntryPoints/UsageReporter.cs
index e445300e4d..be848acb77 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/UsageReporter.cs
+++ b/Emby.Server.Implementations/EntryPoints/UsageReporter.cs
@@ -10,7 +10,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Logging;
-namespace MediaBrowser.Server.Implementations.EntryPoints
+namespace Emby.Server.Implementations.EntryPoints
{
public class UsageReporter
{
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
index 2bb0101330..b934101809 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
@@ -5,14 +5,15 @@ using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Session;
-using MoreLinq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.Threading;
-namespace MediaBrowser.Server.Implementations.EntryPoints
+namespace Emby.Server.Implementations.EntryPoints
{
class UserDataChangeNotifier : IServerEntryPoint
{
@@ -22,17 +23,19 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
private readonly IUserManager _userManager;
private readonly object _syncLock = new object();
- private Timer UpdateTimer { get; set; }
+ private ITimer UpdateTimer { get; set; }
+ private readonly ITimerFactory _timerFactory;
private const int UpdateDuration = 500;
private readonly Dictionary<Guid, List<IHasUserData>> _changedItems = new Dictionary<Guid, List<IHasUserData>>();
- public UserDataChangeNotifier(IUserDataManager userDataManager, ISessionManager sessionManager, ILogger logger, IUserManager userManager)
+ public UserDataChangeNotifier(IUserDataManager userDataManager, ISessionManager sessionManager, ILogger logger, IUserManager userManager, ITimerFactory timerFactory)
{
_userDataManager = userDataManager;
_sessionManager = sessionManager;
_logger = logger;
_userManager = userManager;
+ _timerFactory = timerFactory;
}
public void Run()
@@ -51,7 +54,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
{
if (UpdateTimer == null)
{
- UpdateTimer = new Timer(UpdateTimerCallback, null, UpdateDuration,
+ UpdateTimer = _timerFactory.Create(UpdateTimerCallback, null, UpdateDuration,
Timeout.Infinite);
}
else
diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegInfo.cs b/Emby.Server.Implementations/FFMpeg/FFMpegInfo.cs
index d33b12a9a7..e725d22f51 100644
--- a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegInfo.cs
+++ b/Emby.Server.Implementations/FFMpeg/FFMpegInfo.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Server.Startup.Common.FFMpeg
+namespace Emby.Server.Implementations.FFMpeg
{
/// <summary>
/// Class FFMpegInfo
diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegInstallInfo.cs b/Emby.Server.Implementations/FFMpeg/FFMpegInstallInfo.cs
index a2a44f805c..1d769acec1 100644
--- a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegInstallInfo.cs
+++ b/Emby.Server.Implementations/FFMpeg/FFMpegInstallInfo.cs
@@ -1,5 +1,5 @@

-namespace MediaBrowser.Server.Startup.Common.FFMpeg
+namespace Emby.Server.Implementations.FFMpeg
{
public class FFMpegInstallInfo
{
diff --git a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegLoader.cs b/Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs
index 68e2a49275..2becebb3d3 100644
--- a/MediaBrowser.Server.Startup.Common/FFMpeg/FFMpegLoader.cs
+++ b/Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs
@@ -2,16 +2,16 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
-using Mono.Unix.Native;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using Emby.Server.Implementations;
+using Emby.Server.Implementations.FFMpeg;
-namespace MediaBrowser.Server.Startup.Common.FFMpeg
+namespace Emby.Server.Implementations.FFMpeg
{
public class FFMpegLoader
{
@@ -20,21 +20,19 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
private readonly ILogger _logger;
private readonly IZipClient _zipClient;
private readonly IFileSystem _fileSystem;
- private readonly NativeEnvironment _environment;
private readonly FFMpegInstallInfo _ffmpegInstallInfo;
- public FFMpegLoader(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IZipClient zipClient, IFileSystem fileSystem, NativeEnvironment environment, FFMpegInstallInfo ffmpegInstallInfo)
+ public FFMpegLoader(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IZipClient zipClient, IFileSystem fileSystem, FFMpegInstallInfo ffmpegInstallInfo)
{
_logger = logger;
_appPaths = appPaths;
_httpClient = httpClient;
_zipClient = zipClient;
_fileSystem = fileSystem;
- _environment = environment;
_ffmpegInstallInfo = ffmpegInstallInfo;
}
- public async Task<FFMpegInfo> GetFFMpegInfo(NativeEnvironment environment, StartupOptions options, IProgress<double> progress)
+ public async Task<FFMpegInfo> GetFFMpegInfo(StartupOptions options, IProgress<double> progress)
{
var customffMpegPath = options.GetOption("-ffmpeg");
var customffProbePath = options.GetOption("-ffprobe");
@@ -121,11 +119,11 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
{
var encoderFilename = Path.GetFileName(info.EncoderPath);
var probeFilename = Path.GetFileName(info.ProbePath);
-
- foreach (var directory in Directory.EnumerateDirectories(rootEncoderPath, "*", SearchOption.TopDirectoryOnly)
+
+ foreach (var directory in _fileSystem.GetDirectoryPaths(rootEncoderPath)
.ToList())
{
- var allFiles = Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories).ToList();
+ var allFiles = _fileSystem.GetFilePaths(directory, true).ToList();
var encoder = allFiles.FirstOrDefault(i => string.Equals(Path.GetFileName(i), encoderFilename, StringComparison.OrdinalIgnoreCase));
var probe = allFiles.FirstOrDefault(i => string.Equals(Path.GetFileName(i), probeFilename, StringComparison.OrdinalIgnoreCase));
@@ -184,7 +182,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
{
ExtractArchive(downloadinfo, tempFile, tempFolder);
- var files = Directory.EnumerateFiles(tempFolder, "*", SearchOption.AllDirectories)
+ var files = _fileSystem.GetFilePaths(tempFolder, true)
.ToList();
foreach (var file in files.Where(i =>
@@ -209,13 +207,7 @@ namespace MediaBrowser.Server.Startup.Common.FFMpeg
private void SetFilePermissions(string path)
{
- // Linux: File permission to 666, and user's execute bit
- if (_environment.OperatingSystem == OperatingSystem.Bsd || _environment.OperatingSystem == OperatingSystem.Linux || _environment.OperatingSystem == OperatingSystem.Osx)
- {
- _logger.Info("Syscall.chmod {0} FilePermissions.DEFFILEMODE | FilePermissions.S_IRWXU | FilePermissions.S_IXGRP | FilePermissions.S_IXOTH", path);
-
- Syscall.chmod(path, FilePermissions.DEFFILEMODE | FilePermissions.S_IRWXU | FilePermissions.S_IXGRP | FilePermissions.S_IXOTH);
- }
+ _fileSystem.SetExecutable(path);
}
private void ExtractArchive(FFMpegInstallInfo downloadinfo, string archivePath, string targetPath)
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs b/Emby.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
index 5e01666a9a..5bb21d02ac 100644
--- a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
+++ b/Emby.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
@@ -7,8 +7,6 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.FileOrganization;
using MediaBrowser.Model.Logging;
-using MediaBrowser.Server.Implementations.Library;
-using MediaBrowser.Server.Implementations.Logging;
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -16,9 +14,15 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
-
-namespace MediaBrowser.Server.Implementations.FileOrganization
+using Emby.Server.Implementations.Library;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Naming.TV;
+using EpisodeInfo = MediaBrowser.Controller.Providers.EpisodeInfo;
+
+namespace Emby.Server.Implementations.FileOrganization
{
public class EpisodeFileOrganizer
{
@@ -53,7 +57,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
OriginalPath = path,
OriginalFileName = Path.GetFileName(path),
Type = FileOrganizerType.Episode,
- FileSize = new FileInfo(path).Length
+ FileSize = _fileSystem.GetFileInfo(path).Length
};
try
@@ -66,10 +70,10 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
}
var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
- var resolver = new Naming.TV.EpisodeResolver(namingOptions, new PatternsLogger());
+ var resolver = new EpisodeResolver(namingOptions, new NullLogger());
var episodeInfo = resolver.Resolve(path, false) ??
- new Naming.TV.EpisodeInfo();
+ new MediaBrowser.Naming.TV.EpisodeInfo();
var seriesName = episodeInfo.SeriesName;
@@ -503,7 +507,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
episodePaths.AddRange(filesOfOtherExtensions);
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
// No big deal. Maybe the season folder doesn't already exist.
}
@@ -515,6 +519,12 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
private void PerformFileSorting(TvFileOrganizationOptions options, FileOrganizationResult result)
{
+ // We should probably handle this earlier so that we never even make it this far
+ if (string.Equals(result.OriginalPath, result.TargetPath, StringComparison.OrdinalIgnoreCase))
+ {
+ return;
+ }
+
_libraryMonitor.ReportFileSystemChangeBeginning(result.TargetPath);
_fileSystem.CreateDirectory(Path.GetDirectoryName(result.TargetPath));
@@ -573,7 +583,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
result.ExtractedName = nameWithoutYear;
result.ExtractedYear = yearInName;
- var series = _libraryManager.GetItemList(new Controller.Entities.InternalItemsQuery
+ var series = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = new[] { typeof(Series).Name },
Recursive = true
@@ -591,7 +601,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
if (info != null)
{
- series = _libraryManager.GetItemList(new Controller.Entities.InternalItemsQuery
+ series = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = new[] { typeof(Series).Name },
Recursive = true,
@@ -806,8 +816,8 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
{
try
{
- var sourceFileInfo = new FileInfo(sourcePath);
- var destinationFileInfo = new FileInfo(newPath);
+ var sourceFileInfo = _fileSystem.GetFileInfo(sourcePath);
+ var destinationFileInfo = _fileSystem.GetFileInfo(newPath);
if (sourceFileInfo.Length == destinationFileInfo.Length)
{
@@ -818,7 +828,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
{
return false;
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
return false;
}
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/Extensions.cs b/Emby.Server.Implementations/FileOrganization/Extensions.cs
index c560152dbe..506bc0327e 100644
--- a/MediaBrowser.Server.Implementations/FileOrganization/Extensions.cs
+++ b/Emby.Server.Implementations/FileOrganization/Extensions.cs
@@ -2,7 +2,7 @@
using MediaBrowser.Model.FileOrganization;
using System.Collections.Generic;
-namespace MediaBrowser.Server.Implementations.FileOrganization
+namespace Emby.Server.Implementations.FileOrganization
{
public static class ConfigurationExtension
{
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs b/Emby.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs
index 5c3814f669..2a01765472 100644
--- a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs
+++ b/Emby.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs
@@ -1,5 +1,4 @@
-using MediaBrowser.Common.ScheduledTasks;
-using MediaBrowser.Controller.FileOrganization;
+using MediaBrowser.Controller.FileOrganization;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Events;
@@ -7,8 +6,9 @@ using MediaBrowser.Model.FileOrganization;
using MediaBrowser.Model.Logging;
using System;
using System.Threading;
+using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Server.Implementations.FileOrganization
+namespace Emby.Server.Implementations.FileOrganization
{
/// <summary>
/// Class SessionInfoWebSocketListener
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs b/Emby.Server.Implementations/FileOrganization/FileOrganizationService.cs
index a42eba6cae..4094e6b9b8 100644
--- a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs
+++ b/Emby.Server.Implementations/FileOrganization/FileOrganizationService.cs
@@ -1,5 +1,4 @@
using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.FileOrganization;
using MediaBrowser.Controller.Library;
@@ -14,12 +13,15 @@ using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Events;
using MediaBrowser.Common.Events;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Server.Implementations.FileOrganization
+namespace Emby.Server.Implementations.FileOrganization
{
public class FileOrganizationService : IFileOrganizationService
{
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/NameUtils.cs b/Emby.Server.Implementations/FileOrganization/NameUtils.cs
index 624133d4fb..eb22ca4ea8 100644
--- a/MediaBrowser.Server.Implementations/FileOrganization/NameUtils.cs
+++ b/Emby.Server.Implementations/FileOrganization/NameUtils.cs
@@ -2,10 +2,9 @@
using MediaBrowser.Controller.Entities;
using System;
using System.Globalization;
-using System.Linq;
-using System.Text;
+using MediaBrowser.Controller.Extensions;
-namespace MediaBrowser.Server.Implementations.FileOrganization
+namespace Emby.Server.Implementations.FileOrganization
{
public static class NameUtils
{
@@ -54,7 +53,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
private static string GetComparableName(string name)
{
- name = RemoveDiacritics(name);
+ name = name.RemoveDiacritics();
name = " " + name + " ";
@@ -78,19 +77,5 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
return name.Trim();
}
-
- /// <summary>
- /// Removes the diacritics.
- /// </summary>
- /// <param name="text">The text.</param>
- /// <returns>System.String.</returns>
- private static string RemoveDiacritics(string text)
- {
- return String.Concat(
- text.Normalize(NormalizationForm.FormD)
- .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) !=
- UnicodeCategory.NonSpacingMark)
- ).Normalize(NormalizationForm.FormC);
- }
}
}
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs b/Emby.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs
index de98b83ef2..5be7ba7ada 100644
--- a/MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs
+++ b/Emby.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs
@@ -1,5 +1,4 @@
-using MediaBrowser.Common.ScheduledTasks;
-using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.FileOrganization;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
@@ -9,11 +8,14 @@ using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Server.Implementations.FileOrganization
+namespace Emby.Server.Implementations.FileOrganization
{
- public class OrganizerScheduledTask : IScheduledTask, IConfigurableScheduledTask, IScheduledTaskActivityLog, IHasKey
+ public class OrganizerScheduledTask : IScheduledTask, IConfigurableScheduledTask
{
private readonly ILibraryMonitor _libraryMonitor;
private readonly ILibraryManager _libraryManager;
@@ -63,12 +65,17 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
}
}
- public IEnumerable<ITaskTrigger> GetDefaultTriggers()
+ /// <summary>
+ /// Creates the triggers that define when the task will run
+ /// </summary>
+ /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
+ public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- return new ITaskTrigger[]
- {
- new IntervalTrigger{ Interval = TimeSpan.FromMinutes(5)}
- };
+ return new[] {
+
+ // Every so often
+ new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromMinutes(5).Ticks}
+ };
}
public bool IsHidden
@@ -81,7 +88,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
get { return GetAutoOrganizeOptions().TvOptions.IsEnabled; }
}
- public bool IsActivityLogged
+ public bool IsLogged
{
get { return false; }
}
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs b/Emby.Server.Implementations/FileOrganization/TvFolderOrganizer.cs
index 4f42d8a20d..2850c3a610 100644
--- a/MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs
+++ b/Emby.Server.Implementations/FileOrganization/TvFolderOrganizer.cs
@@ -10,9 +10,11 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
-namespace MediaBrowser.Server.Implementations.FileOrganization
+namespace Emby.Server.Implementations.FileOrganization
{
public class TvFolderOrganizer
{
@@ -189,7 +191,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
_fileSystem.DeleteDirectory(path, false);
}
catch (UnauthorizedAccessException) { }
- catch (DirectoryNotFoundException) { }
+ catch (IOException) { }
}
}
catch (UnauthorizedAccessException) { }
diff --git a/MediaBrowser.Server.Implementations/HttpServer/GetSwaggerResource.cs b/Emby.Server.Implementations/HttpServer/GetSwaggerResource.cs
index 36a257f632..819ede1ab6 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/GetSwaggerResource.cs
+++ b/Emby.Server.Implementations/HttpServer/GetSwaggerResource.cs
@@ -1,6 +1,6 @@
-using ServiceStack;
+using MediaBrowser.Model.Services;
-namespace MediaBrowser.Server.Implementations.HttpServer
+namespace Emby.Server.Implementations.HttpServer
{
/// <summary>
/// Class GetDashboardResource
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
new file mode 100644
index 0000000000..0e1f5a5517
--- /dev/null
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -0,0 +1,729 @@
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.Logging;
+using ServiceStack;
+using ServiceStack.Host;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using Emby.Server.Implementations.HttpServer;
+using Emby.Server.Implementations.HttpServer.SocketSharp;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Common.Security;
+using MediaBrowser.Controller;
+using MediaBrowser.Model.Cryptography;
+using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Services;
+using MediaBrowser.Model.System;
+using MediaBrowser.Model.Text;
+using SocketHttpListener.Net;
+using SocketHttpListener.Primitives;
+
+namespace Emby.Server.Implementations.HttpServer
+{
+ public class HttpListenerHost : ServiceStackHost, IHttpServer
+ {
+ private string DefaultRedirectPath { get; set; }
+
+ private readonly ILogger _logger;
+ public IEnumerable<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;
+
+ 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 IJsonSerializer _jsonSerializer;
+ private readonly IXmlSerializer _xmlSerializer;
+ private readonly ICertificate _certificate;
+ private readonly IEnvironmentInfo _environment;
+ private readonly IStreamFactory _streamFactory;
+ private readonly Func<Type, Func<string, object>> _funcParseFn;
+ private readonly bool _enableDualModeSockets;
+
+ 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, ICertificate certificate, IStreamFactory streamFactory, Func<Type, Func<string, object>> funcParseFn, bool enableDualModeSockets)
+ : base(serviceName)
+ {
+ _appHost = applicationHost;
+ DefaultRedirectPath = defaultRedirectPath;
+ _networkManager = networkManager;
+ _memoryStreamProvider = memoryStreamProvider;
+ _textEncoding = textEncoding;
+ _socketFactory = socketFactory;
+ _cryptoProvider = cryptoProvider;
+ _jsonSerializer = jsonSerializer;
+ _xmlSerializer = xmlSerializer;
+ _environment = environment;
+ _certificate = certificate;
+ _streamFactory = streamFactory;
+ _funcParseFn = funcParseFn;
+ _enableDualModeSockets = enableDualModeSockets;
+ _config = config;
+
+ _logger = logger;
+ }
+
+ public string GlobalResponse { get; set; }
+
+ readonly Dictionary<Type, int> _mapExceptionToStatusCode = new Dictionary<Type, int>
+ {
+ {typeof (InvalidOperationException), 500},
+ {typeof (NotImplementedException), 500},
+ {typeof (ResourceNotFoundException), 404},
+ {typeof (FileNotFoundException), 404},
+ //{typeof (DirectoryNotFoundException), 404},
+ {typeof (SecurityException), 401},
+ {typeof (PaymentRequiredException), 402},
+ {typeof (UnauthorizedAccessException), 500},
+ {typeof (PlatformNotSupportedException), 500},
+ {typeof (NotSupportedException), 500}
+ };
+
+ public override void Configure()
+ {
+ var requestFilters = _appHost.GetExports<IRequestFilter>().ToList();
+ foreach (var filter in requestFilters)
+ {
+ GlobalRequestFilters.Add(filter.Filter);
+ }
+
+ GlobalResponseFilters.Add(new ResponseFilter(_logger).FilterResponse);
+ }
+
+ protected override ILogger Logger
+ {
+ get
+ {
+ return _logger;
+ }
+ }
+
+ public override T Resolve<T>()
+ {
+ return _appHost.Resolve<T>();
+ }
+
+ public override T TryResolve<T>()
+ {
+ return _appHost.TryResolve<T>();
+ }
+
+ public override object CreateInstance(Type type)
+ {
+ return _appHost.CreateInstance(type);
+ }
+
+ protected override ServiceController CreateServiceController()
+ {
+ var types = _restServices.Select(r => r.GetType()).ToArray();
+
+ return new ServiceController(() => types);
+ }
+
+ public override ServiceStackHost Start(string listeningAtUrlBase)
+ {
+ StartListener();
+ return this;
+ }
+
+ /// <summary>
+ /// Starts the Web Service
+ /// </summary>
+ private void StartListener()
+ {
+ WebSocketSharpRequest.HandlerFactoryPath = GetHandlerPathIfAny(UrlPrefixes.First());
+
+ _listener = GetListener();
+
+ _listener.WebSocketConnected = OnWebSocketConnected;
+ _listener.WebSocketConnecting = OnWebSocketConnecting;
+ _listener.ErrorHandler = ErrorHandler;
+ _listener.RequestHandler = RequestHandler;
+
+ _listener.Start(UrlPrefixes);
+ }
+
+ public static string GetHandlerPathIfAny(string listenerUrl)
+ {
+ if (listenerUrl == null) return null;
+ var pos = listenerUrl.IndexOf("://", StringComparison.OrdinalIgnoreCase);
+ if (pos == -1) return null;
+ var startHostUrl = listenerUrl.Substring(pos + "://".Length);
+ var endPos = startHostUrl.IndexOf('/');
+ if (endPos == -1) return null;
+ var endHostUrl = startHostUrl.Substring(endPos + 1);
+ return string.IsNullOrEmpty(endHostUrl) ? null : endHostUrl.TrimEnd('/');
+ }
+
+ private IHttpListener GetListener()
+ {
+ return new WebSocketSharpListener(_logger,
+ _certificate,
+ _memoryStreamProvider,
+ _textEncoding,
+ _networkManager,
+ _socketFactory,
+ _cryptoProvider,
+ _streamFactory,
+ _enableDualModeSockets,
+ GetRequest);
+ }
+
+ private IHttpRequest GetRequest(HttpListenerContext httpContext)
+ {
+ var operationName = httpContext.Request.GetOperationName();
+
+ var req = new WebSocketSharpRequest(httpContext, operationName, _logger, _memoryStreamProvider);
+
+ return req;
+ }
+
+ private void OnWebSocketConnecting(WebSocketConnectingEventArgs args)
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ if (WebSocketConnecting != null)
+ {
+ WebSocketConnecting(this, args);
+ }
+ }
+
+ private void OnWebSocketConnected(WebSocketConnectEventArgs args)
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ if (WebSocketConnected != null)
+ {
+ WebSocketConnected(this, args);
+ }
+ }
+
+ private void ErrorHandler(Exception ex, IRequest httpReq)
+ {
+ try
+ {
+ _logger.ErrorException("Error processing request", ex);
+
+ var httpRes = httpReq.Response;
+
+ if (httpRes.IsClosed)
+ {
+ return;
+ }
+
+ int statusCode;
+ if (!_mapExceptionToStatusCode.TryGetValue(ex.GetType(), out statusCode))
+ {
+ statusCode = 500;
+ }
+ httpRes.StatusCode = statusCode;
+
+ httpRes.ContentType = "text/html";
+ Write(httpRes, ex.Message);
+ }
+ catch
+ {
+ //_logger.ErrorException("Error this.ProcessRequest(context)(Exception while writing error to the response)", errorEx);
+ }
+ }
+
+ /// <summary>
+ /// Shut down the Web Service
+ /// </summary>
+ public void Stop()
+ {
+ if (_listener != null)
+ {
+ _listener.Stop();
+ }
+ }
+
+ private readonly Dictionary<string, int> _skipLogExtensions = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
+ {
+ {".js", 0},
+ {".css", 0},
+ {".woff", 0},
+ {".woff2", 0},
+ {".ttf", 0},
+ {".html", 0}
+ };
+
+ private bool EnableLogging(string url, string localPath)
+ {
+ var extension = GetExtension(url);
+
+ if (string.IsNullOrWhiteSpace(extension) || !_skipLogExtensions.ContainsKey(extension))
+ {
+ if (string.IsNullOrWhiteSpace(localPath) || localPath.IndexOf("system/ping", StringComparison.OrdinalIgnoreCase) == -1)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private string GetExtension(string url)
+ {
+ var parts = url.Split(new[] { '?' }, 2);
+
+ return Path.GetExtension(parts[0]);
+ }
+
+ public static string RemoveQueryStringByKey(string url, string key)
+ {
+ var uri = new Uri(url);
+
+ // this gets all the query string key value pairs as a collection
+ var newQueryString = MyHttpUtility.ParseQueryString(uri.Query);
+
+ var originalCount = newQueryString.Count;
+
+ if (originalCount == 0)
+ {
+ return url;
+ }
+
+ // this removes the key if exists
+ newQueryString.Remove(key);
+
+ if (originalCount == newQueryString.Count)
+ {
+ return url;
+ }
+
+ // this gets the page path from root without QueryString
+ string pagePathWithoutQueryString = url.Split(new[] { '?' }, StringSplitOptions.RemoveEmptyEntries)[0];
+
+ return newQueryString.Count > 0
+ ? String.Format("{0}?{1}", pagePathWithoutQueryString, newQueryString)
+ : pagePathWithoutQueryString;
+ }
+
+ private string GetUrlToLog(string url)
+ {
+ url = RemoveQueryStringByKey(url, "api_key");
+
+ return url;
+ }
+
+ private string NormalizeConfiguredLocalAddress(string address)
+ {
+ var index = address.Trim('/').IndexOf('/');
+
+ if (index != -1)
+ {
+ address = address.Substring(index + 1);
+ }
+
+ return address.Trim('/');
+ }
+
+ private bool ValidateHost(Uri url)
+ {
+ var hosts = _config
+ .Configuration
+ .LocalNetworkAddresses
+ .Select(NormalizeConfiguredLocalAddress)
+ .ToList();
+
+ if (hosts.Count == 0)
+ {
+ return true;
+ }
+
+ var host = url.Host ?? string.Empty;
+
+ _logger.Debug("Validating host {0}", host);
+
+ if (_networkManager.IsInPrivateAddressSpace(host))
+ {
+ hosts.Add("localhost");
+ hosts.Add("127.0.0.1");
+
+ return hosts.Any(i => host.IndexOf(i, StringComparison.OrdinalIgnoreCase) != -1);
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Overridable method that can be used to implement a custom hnandler
+ /// </summary>
+ /// <param name="httpReq">The HTTP req.</param>
+ /// <param name="url">The URL.</param>
+ /// <returns>Task.</returns>
+ protected async Task RequestHandler(IHttpRequest httpReq, Uri url)
+ {
+ var date = DateTime.Now;
+ var httpRes = httpReq.Response;
+ bool enableLog = false;
+ string urlToLog = null;
+ string remoteIp = null;
+
+ try
+ {
+ if (_disposed)
+ {
+ httpRes.StatusCode = 503;
+ httpRes.ContentType = "text/plain";
+ Write(httpRes, "Server shutting down");
+ return;
+ }
+
+ if (!ValidateHost(url))
+ {
+ httpRes.StatusCode = 400;
+ httpRes.ContentType = "text/plain";
+ Write(httpRes, "Invalid host");
+ return;
+ }
+
+ if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase))
+ {
+ httpRes.StatusCode = 200;
+ httpRes.AddHeader("Access-Control-Allow-Origin", "*");
+ 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);
+ return;
+ }
+
+ var operationName = httpReq.OperationName;
+ var localPath = url.LocalPath;
+
+ var urlString = url.OriginalString;
+ enableLog = EnableLogging(urlString, localPath);
+ urlToLog = urlString;
+
+ if (enableLog)
+ {
+ urlToLog = GetUrlToLog(urlString);
+ remoteIp = httpReq.RemoteIp;
+
+ LoggerUtils.LogRequest(_logger, urlToLog, httpReq.HttpMethod, httpReq.UserAgent);
+ }
+
+ if (string.Equals(localPath, "/emby/", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase))
+ {
+ RedirectToUrl(httpRes, DefaultRedirectPath);
+ return;
+ }
+ if (string.Equals(localPath, "/emby", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase))
+ {
+ RedirectToUrl(httpRes, "emby/" + DefaultRedirectPath);
+ return;
+ }
+
+ if (string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase) ||
+ localPath.IndexOf("mediabrowser/web", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ httpRes.StatusCode = 200;
+ httpRes.ContentType = "text/html";
+ var newUrl = urlString.Replace("mediabrowser", "emby", StringComparison.OrdinalIgnoreCase)
+ .Replace("/dashboard/", "/web/", StringComparison.OrdinalIgnoreCase);
+
+ if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase))
+ {
+ Write(httpRes,
+ "<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" +
+ newUrl + "\">" + newUrl + "</a></body></html>");
+ return;
+ }
+ }
+
+ if (localPath.IndexOf("dashboard/", StringComparison.OrdinalIgnoreCase) != -1 &&
+ localPath.IndexOf("web/dashboard", StringComparison.OrdinalIgnoreCase) == -1)
+ {
+ httpRes.StatusCode = 200;
+ httpRes.ContentType = "text/html";
+ var newUrl = urlString.Replace("mediabrowser", "emby", StringComparison.OrdinalIgnoreCase)
+ .Replace("/dashboard/", "/web/", StringComparison.OrdinalIgnoreCase);
+
+ if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase))
+ {
+ Write(httpRes,
+ "<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" +
+ newUrl + "\">" + newUrl + "</a></body></html>");
+ return;
+ }
+ }
+
+ if (string.Equals(localPath, "/web", StringComparison.OrdinalIgnoreCase))
+ {
+ RedirectToUrl(httpRes, DefaultRedirectPath);
+ return;
+ }
+ if (string.Equals(localPath, "/web/", StringComparison.OrdinalIgnoreCase))
+ {
+ RedirectToUrl(httpRes, "../" + DefaultRedirectPath);
+ return;
+ }
+ if (string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase))
+ {
+ RedirectToUrl(httpRes, DefaultRedirectPath);
+ return;
+ }
+ if (string.IsNullOrEmpty(localPath))
+ {
+ RedirectToUrl(httpRes, "/" + DefaultRedirectPath);
+ return;
+ }
+
+ if (string.Equals(localPath, "/emby/pin", StringComparison.OrdinalIgnoreCase))
+ {
+ RedirectToUrl(httpRes, "web/pin.html");
+ return;
+ }
+
+ if (!string.IsNullOrWhiteSpace(GlobalResponse))
+ {
+ httpRes.StatusCode = 503;
+ httpRes.ContentType = "text/html";
+ Write(httpRes, GlobalResponse);
+ return;
+ }
+
+ var handler = HttpHandlerFactory.GetHandler(httpReq, _logger);
+
+ if (handler != null)
+ {
+ await handler.ProcessRequestAsync(httpReq, httpRes, operationName).ConfigureAwait(false);
+ }
+ else
+ {
+ ErrorHandler(new FileNotFoundException(), httpReq);
+ }
+ }
+ catch (Exception ex)
+ {
+ ErrorHandler(ex, httpReq);
+ }
+ finally
+ {
+ httpRes.Close();
+
+ if (enableLog)
+ {
+ var statusCode = httpRes.StatusCode;
+
+ var duration = DateTime.Now - date;
+
+ LoggerUtils.LogResponse(_logger, statusCode, urlToLog, remoteIp, duration);
+ }
+ }
+ }
+
+ private void 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);
+ }
+
+ public static void RedirectToUrl(IResponse httpRes, string url)
+ {
+ httpRes.StatusCode = 302;
+ httpRes.AddHeader("Location", url);
+ }
+
+
+ /// <summary>
+ /// Adds the rest handlers.
+ /// </summary>
+ /// <param name="services">The services.</param>
+ public void Init(IEnumerable<IService> services)
+ {
+ _restServices.AddRange(services);
+
+ ServiceController = CreateServiceController();
+
+ _logger.Info("Calling ServiceStack AppHost.Init");
+
+ base.Init();
+ }
+
+ public override RouteAttribute[] GetRouteAttributes(Type requestType)
+ {
+ var routes = base.GetRouteAttributes(requestType).ToList();
+ var clone = routes.ToList();
+
+ foreach (var route in clone)
+ {
+ routes.Add(new RouteAttribute(NormalizeEmbyRoutePath(route.Path), route.Verbs)
+ {
+ Notes = route.Notes,
+ Priority = route.Priority,
+ Summary = route.Summary
+ });
+
+ routes.Add(new RouteAttribute(NormalizeRoutePath(route.Path), route.Verbs)
+ {
+ Notes = route.Notes,
+ Priority = route.Priority,
+ Summary = route.Summary
+ });
+
+ routes.Add(new RouteAttribute(DoubleNormalizeEmbyRoutePath(route.Path), route.Verbs)
+ {
+ Notes = route.Notes,
+ Priority = route.Priority,
+ Summary = route.Summary
+ });
+ }
+
+ return routes.ToArray();
+ }
+
+ public override object GetTaskResult(Task task, string requestName)
+ {
+ 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;
+ }
+
+ Logger.Warn("Getting task result from " + requestName + " using reflection. For better performance have your api return Task<object>");
+ return type.GetDeclaredProperty("Result").GetValue(task);
+ }
+ catch (TypeAccessException)
+ {
+ return null; //return null for void Task's
+ }
+ }
+
+ public override Func<string, object> GetParseFn(Type propertyType)
+ {
+ return _funcParseFn(propertyType);
+ }
+
+ public override void SerializeToJson(object o, Stream stream)
+ {
+ _jsonSerializer.SerializeToStream(o, stream);
+ }
+
+ public override void SerializeToXml(object o, Stream stream)
+ {
+ _xmlSerializer.SerializeToStream(o, stream);
+ }
+
+ public override object DeserializeXml(Type type, Stream stream)
+ {
+ return _xmlSerializer.DeserializeFromStream(type, stream);
+ }
+
+ public override object DeserializeJson(Type type, Stream stream)
+ {
+ return _jsonSerializer.DeserializeFromStream(stream, type);
+ }
+
+ private string NormalizeEmbyRoutePath(string path)
+ {
+ if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase))
+ {
+ return "/emby" + path;
+ }
+
+ return "emby/" + path;
+ }
+
+ private string DoubleNormalizeEmbyRoutePath(string path)
+ {
+ if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase))
+ {
+ return "/emby/emby" + path;
+ }
+
+ return "emby/emby/" + path;
+ }
+
+ private string NormalizeRoutePath(string path)
+ {
+ if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase))
+ {
+ return "/mediabrowser" + path;
+ }
+
+ return "mediabrowser/" + path;
+ }
+
+ private bool _disposed;
+ private readonly object _disposeLock = new object();
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_disposed) return;
+ base.Dispose();
+
+ lock (_disposeLock)
+ {
+ if (_disposed) return;
+
+ if (disposing)
+ {
+ Stop();
+ }
+
+ //release unmanaged resources here...
+ _disposed = true;
+ }
+ }
+
+ public override void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ public void StartServer(IEnumerable<string> urlPrefixes)
+ {
+ UrlPrefixes = urlPrefixes.ToList();
+ Start(UrlPrefixes.First());
+ }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
index 10d6f74938..995dc7b7b0 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
@@ -2,19 +2,26 @@
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
-using ServiceStack;
-using ServiceStack.Web;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
+using System.IO.Compression;
using System.Net;
+using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
-using CommonIO;
+using System.Xml;
+using Emby.Server.Implementations.HttpServer;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Services;
+using ServiceStack;
+using ServiceStack.Host;
+using IRequest = MediaBrowser.Model.Services.IRequest;
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
+using StreamWriter = Emby.Server.Implementations.HttpServer.StreamWriter;
-namespace MediaBrowser.Server.Implementations.HttpServer
+namespace Emby.Server.Implementations.HttpServer
{
/// <summary>
/// Class HttpResultFactory
@@ -27,17 +34,16 @@ namespace MediaBrowser.Server.Implementations.HttpServer
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
private readonly IJsonSerializer _jsonSerializer;
+ private readonly IMemoryStreamFactory _memoryStreamFactory;
/// <summary>
/// Initializes a new instance of the <see cref="HttpResultFactory" /> class.
/// </summary>
- /// <param name="logManager">The log manager.</param>
- /// <param name="fileSystem">The file system.</param>
- /// <param name="jsonSerializer">The json serializer.</param>
- public HttpResultFactory(ILogManager logManager, IFileSystem fileSystem, IJsonSerializer jsonSerializer)
+ public HttpResultFactory(ILogManager logManager, IFileSystem fileSystem, IJsonSerializer jsonSerializer, IMemoryStreamFactory memoryStreamFactory)
{
_fileSystem = fileSystem;
_jsonSerializer = jsonSerializer;
+ _memoryStreamFactory = memoryStreamFactory;
_logger = logManager.GetLogger("HttpResultFactory");
}
@@ -50,19 +56,15 @@ namespace MediaBrowser.Server.Implementations.HttpServer
/// <returns>System.Object.</returns>
public object GetResult(object content, string contentType, IDictionary<string, string> responseHeaders = null)
{
- return GetHttpResult(content, contentType, responseHeaders);
+ return GetHttpResult(content, contentType, true, responseHeaders);
}
/// <summary>
/// Gets the HTTP result.
/// </summary>
- /// <param name="content">The content.</param>
- /// <param name="contentType">Type of the content.</param>
- /// <param name="responseHeaders">The response headers.</param>
- /// <returns>IHasOptions.</returns>
- private IHasOptions GetHttpResult(object content, string contentType, IDictionary<string, string> responseHeaders = null)
+ private IHasHeaders GetHttpResult(object content, string contentType, bool addCachePrevention, IDictionary<string, string> responseHeaders = null)
{
- IHasOptions result;
+ IHasHeaders result;
var stream = content as Stream;
@@ -89,7 +91,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer
}
else
{
- result = new HttpResult(content, contentType);
+ result = new HttpResult(content, contentType, HttpStatusCode.OK);
}
}
}
@@ -98,7 +100,12 @@ namespace MediaBrowser.Server.Implementations.HttpServer
responseHeaders = new Dictionary<string, string>();
}
- responseHeaders["Expires"] = "-1";
+ string expires;
+ if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out expires))
+ {
+ responseHeaders["Expires"] = "-1";
+ }
+
AddResponseHeaders(result, responseHeaders);
return result;
@@ -127,7 +134,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer
throw new ArgumentNullException("result");
}
- var optimizedResult = requestContext.ToOptimizedResult(result);
+ var optimizedResult = ToOptimizedResult(requestContext, result);
if (responseHeaders == null)
{
@@ -140,16 +147,116 @@ namespace MediaBrowser.Server.Implementations.HttpServer
}
// Apply headers
- var hasOptions = optimizedResult as IHasOptions;
+ var hasHeaders = optimizedResult as IHasHeaders;
- if (hasOptions != null)
+ if (hasHeaders != null)
{
- AddResponseHeaders(hasOptions, responseHeaders);
+ AddResponseHeaders(hasHeaders, responseHeaders);
}
return optimizedResult;
}
-
+
+ public static string GetCompressionType(IRequest request)
+ {
+ var acceptEncoding = request.Headers["Accept-Encoding"];
+
+ if (!string.IsNullOrWhiteSpace(acceptEncoding))
+ {
+ if (acceptEncoding.Contains("deflate"))
+ return "deflate";
+
+ if (acceptEncoding.Contains("gzip"))
+ return "gzip";
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Returns the optimized result for the IRequestContext.
+ /// Does not use or store results in any cache.
+ /// </summary>
+ /// <param name="request"></param>
+ /// <param name="dto"></param>
+ /// <returns></returns>
+ public object ToOptimizedResult<T>(IRequest request, T dto)
+ {
+ var compressionType = GetCompressionType(request);
+ if (compressionType == null)
+ {
+ var contentType = request.ResponseContentType;
+
+ switch (GetRealContentType(contentType))
+ {
+ case "application/xml":
+ case "text/xml":
+ case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml
+ return SerializeToXmlString(dto);
+
+ case "application/json":
+ case "text/json":
+ return _jsonSerializer.SerializeToString(dto);
+ }
+ }
+
+ // Do not use the memoryStreamFactory here, they don't place nice with compression
+ using (var ms = new MemoryStream())
+ {
+ using (var compressionStream = GetCompressionStream(ms, compressionType))
+ {
+ ContentTypes.Instance.SerializeToStream(request, dto, compressionStream);
+ compressionStream.Dispose();
+
+ var compressedBytes = ms.ToArray();
+
+ var httpResult = new StreamWriter(compressedBytes, request.ResponseContentType, _logger);
+
+ //httpResult.Headers["Content-Length"] = compressedBytes.Length.ToString(UsCulture);
+ httpResult.Headers["Content-Encoding"] = compressionType;
+
+ return httpResult;
+ }
+ }
+ }
+
+ private static Stream GetCompressionStream(Stream outputStream, string compressionType)
+ {
+ if (compressionType == "deflate")
+ return new DeflateStream(outputStream, CompressionMode.Compress, true);
+ if (compressionType == "gzip")
+ return new GZipStream(outputStream, CompressionMode.Compress, true);
+
+ throw new NotSupportedException(compressionType);
+ }
+
+ public static string GetRealContentType(string contentType)
+ {
+ return contentType == null
+ ? null
+ : contentType.Split(';')[0].ToLower().Trim();
+ }
+
+ private string SerializeToXmlString(object from)
+ {
+ using (var ms = new MemoryStream())
+ {
+ var xwSettings = new XmlWriterSettings();
+ xwSettings.Encoding = new UTF8Encoding(false);
+ xwSettings.OmitXmlDeclaration = false;
+
+ 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>
/// Gets the optimized result using cache.
/// </summary>
@@ -237,31 +344,15 @@ namespace MediaBrowser.Server.Implementations.HttpServer
result = factoryFn();
// Apply caching headers
- var hasOptions = result as IHasOptions;
+ var hasHeaders = result as IHasHeaders;
- if (hasOptions != null)
+ if (hasHeaders != null)
{
- AddResponseHeaders(hasOptions, responseHeaders);
- return hasOptions;
+ AddResponseHeaders(hasHeaders, responseHeaders);
+ return hasHeaders;
}
- IHasOptions httpResult;
-
- var stream = result as Stream;
-
- if (stream != null)
- {
- httpResult = new StreamWriter(stream, contentType, _logger);
- }
- else
- {
- // Otherwise wrap into an HttpResult
- httpResult = new HttpResult(result, contentType ?? "text/html", HttpStatusCode.NotModified);
- }
-
- AddResponseHeaders(httpResult, responseHeaders);
-
- return httpResult;
+ return GetHttpResult(result, contentType, false, responseHeaders);
}
/// <summary>
@@ -298,7 +389,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer
public Task<object> GetStaticFileResult(IRequest requestContext,
string path,
- FileShare fileShare = FileShare.Read)
+ FileShareMode fileShare = FileShareMode.Read)
{
if (string.IsNullOrEmpty(path))
{
@@ -323,7 +414,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer
throw new ArgumentNullException("path");
}
- if (fileShare != FileShare.Read && fileShare != FileShare.ReadWrite)
+ if (fileShare != FileShareMode.Read && fileShare != FileShareMode.ReadWrite)
{
throw new ArgumentException("FileShare must be either Read or ReadWrite");
}
@@ -352,9 +443,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer
/// <param name="path">The path.</param>
/// <param name="fileShare">The file share.</param>
/// <returns>Stream.</returns>
- private Stream GetFileStream(string path, FileShare fileShare)
+ private Stream GetFileStream(string path, FileShareMode fileShare)
{
- return _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, fileShare);
+ return _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, fileShare);
}
public Task<object> GetStaticResult(IRequest requestContext,
@@ -404,10 +495,10 @@ namespace MediaBrowser.Server.Implementations.HttpServer
}
var compress = ShouldCompressResponse(requestContext, contentType);
- var hasOptions = await GetStaticResult(requestContext, options, compress).ConfigureAwait(false);
- AddResponseHeaders(hasOptions, options.ResponseHeaders);
+ var hasHeaders = await GetStaticResult(requestContext, options, compress).ConfigureAwait(false);
+ AddResponseHeaders(hasHeaders, options.ResponseHeaders);
- return hasOptions;
+ return hasHeaders;
}
/// <summary>
@@ -419,7 +510,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer
private bool ShouldCompressResponse(IRequest requestContext, string contentType)
{
// It will take some work to support compression with byte range requests
- if (!string.IsNullOrEmpty(requestContext.GetHeader("Range")))
+ if (!string.IsNullOrEmpty(requestContext.Headers.Get("Range")))
{
return false;
}
@@ -461,18 +552,18 @@ namespace MediaBrowser.Server.Implementations.HttpServer
/// </summary>
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
- private async Task<IHasOptions> GetStaticResult(IRequest requestContext, StaticResultOptions options, bool compress)
+ private async Task<IHasHeaders> GetStaticResult(IRequest requestContext, StaticResultOptions options, bool compress)
{
var isHeadRequest = options.IsHeadRequest;
var factoryFn = options.ContentFactory;
var contentType = options.ContentType;
var responseHeaders = options.ResponseHeaders;
- var requestedCompressionType = requestContext.GetCompressionType();
+ var requestedCompressionType = GetCompressionType(requestContext);
if (!compress || string.IsNullOrEmpty(requestedCompressionType))
{
- var rangeHeader = requestContext.GetHeader("Range");
+ var rangeHeader = requestContext.Headers.Get("Range");
var stream = await factoryFn().ConfigureAwait(false);
@@ -490,7 +581,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer
{
stream.Dispose();
- return GetHttpResult(new byte[] { }, contentType);
+ return GetHttpResult(new byte[] { }, contentType, true);
}
return new StreamWriter(stream, contentType, _logger)
@@ -510,16 +601,64 @@ namespace MediaBrowser.Server.Implementations.HttpServer
}
}
- var contents = content.Compress(requestedCompressionType);
+ var contents = Compress(content, requestedCompressionType);
responseHeaders["Content-Length"] = contents.Length.ToString(UsCulture);
+ responseHeaders["Content-Encoding"] = requestedCompressionType;
if (isHeadRequest)
{
- return GetHttpResult(new byte[] { }, contentType);
+ return GetHttpResult(new byte[] { }, contentType, true);
+ }
+
+ return GetHttpResult(contents, contentType, true, responseHeaders);
+ }
+
+ private byte[] Compress(string text, string compressionType)
+ {
+ if (compressionType == "deflate")
+ return Deflate(text);
+
+ if (compressionType == "gzip")
+ return GZip(text);
+
+ throw new NotSupportedException(compressionType);
+ }
+
+ private byte[] Deflate(string text)
+ {
+ return Deflate(Encoding.UTF8.GetBytes(text));
+ }
+
+ private byte[] Deflate(byte[] bytes)
+ {
+ // In .NET FX incompat-ville, you can't access compressed bytes without closing DeflateStream
+ // Which means we must use MemoryStream since you have to use ToArray() on a closed Stream
+ using (var ms = new MemoryStream())
+ using (var zipStream = new DeflateStream(ms, CompressionMode.Compress))
+ {
+ zipStream.Write(bytes, 0, bytes.Length);
+ zipStream.Dispose();
+
+ return ms.ToArray();
}
+ }
- return new CompressedResult(contents, requestedCompressionType, contentType);
+ private byte[] GZip(string text)
+ {
+ return GZip(Encoding.UTF8.GetBytes(text));
+ }
+
+ private byte[] GZip(byte[] buffer)
+ {
+ using (var ms = new MemoryStream())
+ using (var zipStream = new GZipStream(ms, CompressionMode.Compress))
+ {
+ zipStream.Write(buffer, 0, buffer.Length);
+ zipStream.Dispose();
+
+ return ms.ToArray();
+ }
}
/// <summary>
@@ -596,9 +735,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer
/// <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)
{
- var isNotModified = true;
+ //var isNotModified = true;
- var ifModifiedSinceHeader = requestContext.GetHeader("If-Modified-Since");
+ var ifModifiedSinceHeader = requestContext.Headers.Get("If-Modified-Since");
if (!string.IsNullOrEmpty(ifModifiedSinceHeader))
{
@@ -606,18 +745,23 @@ namespace MediaBrowser.Server.Implementations.HttpServer
if (DateTime.TryParse(ifModifiedSinceHeader, out ifModifiedSince))
{
- isNotModified = IsNotModified(ifModifiedSince.ToUniversalTime(), cacheDuration, lastDateModified);
+ if (IsNotModified(ifModifiedSince.ToUniversalTime(), cacheDuration, lastDateModified))
+ {
+ return true;
+ }
}
}
- var ifNoneMatchHeader = requestContext.GetHeader("If-None-Match");
+ var ifNoneMatchHeader = requestContext.Headers.Get("If-None-Match");
// Validate If-None-Match
- if (isNotModified && (cacheKey.HasValue || !string.IsNullOrEmpty(ifNoneMatchHeader)))
+ if ((cacheKey.HasValue || !string.IsNullOrEmpty(ifNoneMatchHeader)))
{
Guid ifNoneMatch;
- if (Guid.TryParse(ifNoneMatchHeader ?? string.Empty, out ifNoneMatch))
+ ifNoneMatchHeader = (ifNoneMatchHeader ?? string.Empty).Trim('\"');
+
+ if (Guid.TryParse(ifNoneMatchHeader, out ifNoneMatch))
{
if (cacheKey.HasValue && cacheKey.Value == ifNoneMatch)
{
@@ -673,19 +817,14 @@ namespace MediaBrowser.Server.Implementations.HttpServer
/// <summary>
/// Adds the response headers.
/// </summary>
- /// <param name="hasOptions">The has options.</param>
+ /// <param name="hasHeaders">The has options.</param>
/// <param name="responseHeaders">The response headers.</param>
- private void AddResponseHeaders(IHasOptions hasOptions, IEnumerable<KeyValuePair<string, string>> responseHeaders)
+ private void AddResponseHeaders(IHasHeaders hasHeaders, IEnumerable<KeyValuePair<string, string>> responseHeaders)
{
foreach (var item in responseHeaders)
{
- hasOptions.Options[item.Key] = item.Value;
+ hasHeaders.Headers[item.Key] = item.Value;
}
}
-
- public object GetAsyncStreamWriter(IAsyncStreamSource streamSource)
- {
- return new AsyncStreamWriter(streamSource);
- }
}
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/HttpServer/IHttpListener.cs b/Emby.Server.Implementations/HttpServer/IHttpListener.cs
index dc315601f2..9f96a8e49d 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/IHttpListener.cs
+++ b/Emby.Server.Implementations/HttpServer/IHttpListener.cs
@@ -1,10 +1,10 @@
using MediaBrowser.Controller.Net;
-using ServiceStack.Web;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
-namespace MediaBrowser.Server.Implementations.HttpServer
+namespace Emby.Server.Implementations.HttpServer
{
public interface IHttpListener : IDisposable
{
diff --git a/MediaBrowser.Server.Implementations/HttpServer/LoggerUtils.cs b/Emby.Server.Implementations/HttpServer/LoggerUtils.cs
index bfbb228edf..8fc92a09a7 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/LoggerUtils.cs
+++ b/Emby.Server.Implementations/HttpServer/LoggerUtils.cs
@@ -3,7 +3,7 @@ using System;
using System.Globalization;
using SocketHttpListener.Net;
-namespace MediaBrowser.Server.Implementations.HttpServer
+namespace Emby.Server.Implementations.HttpServer
{
public static class LoggerUtils
{
diff --git a/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs b/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs
index 4b94095f52..e88994bec0 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs
+++ b/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs
@@ -1,16 +1,16 @@
using MediaBrowser.Model.Logging;
-using ServiceStack.Web;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
+using System.Threading;
using System.Threading.Tasks;
-using ServiceStack;
+using MediaBrowser.Model.Services;
-namespace MediaBrowser.Server.Implementations.HttpServer
+namespace Emby.Server.Implementations.HttpServer
{
- public class RangeRequestWriter : IStreamWriter, IAsyncStreamWriter, IHttpResult
+ public class RangeRequestWriter : IAsyncStreamWriter, IHttpResult
{
/// <summary>
/// Gets or sets the source stream.
@@ -40,28 +40,18 @@ namespace MediaBrowser.Server.Implementations.HttpServer
/// </summary>
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
- public Func<IDisposable> ResultScope { get; set; }
public List<Cookie> Cookies { get; private set; }
/// <summary>
/// Additional HTTP Headers
/// </summary>
/// <value>The headers.</value>
- public Dictionary<string, string> Headers
+ public IDictionary<string, string> Headers
{
get { return _options; }
}
/// <summary>
- /// Gets the options.
- /// </summary>
- /// <value>The options.</value>
- public IDictionary<string, string> Options
- {
- get { return Headers; }
- }
-
- /// <summary>
/// Initializes a new instance of the <see cref="StreamWriter" /> class.
/// </summary>
/// <param name="rangeHeader">The range header.</param>
@@ -81,8 +71,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer
this._logger = logger;
ContentType = contentType;
- Options["Content-Type"] = contentType;
- Options["Accept-Ranges"] = "bytes";
+ Headers["Content-Type"] = contentType;
+ Headers["Accept-Ranges"] = "bytes";
StatusCode = HttpStatusCode.PartialContent;
Cookies = new List<Cookie>();
@@ -112,8 +102,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer
RangeLength = 1 + RangeEnd - RangeStart;
// Content-Length is the length of what we're serving, not the original content
- Options["Content-Length"] = RangeLength.ToString(UsCulture);
- Options["Content-Range"] = string.Format("bytes {0}-{1}/{2}", RangeStart, RangeEnd, TotalContentLength);
+ Headers["Content-Length"] = RangeLength.ToString(UsCulture);
+ Headers["Content-Range"] = string.Format("bytes {0}-{1}/{2}", RangeStart, RangeEnd, TotalContentLength);
if (RangeStart > 0)
{
@@ -164,62 +154,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer
}
}
- /// <summary>
- /// Writes to.
- /// </summary>
- /// <param name="responseStream">The response stream.</param>
- public void WriteTo(Stream responseStream)
- {
- try
- {
- // Headers only
- if (IsHeadRequest)
- {
- return;
- }
-
- using (var source = SourceStream)
- {
- // If the requested range is "0-", we can optimize by just doing a stream copy
- if (RangeEnd >= TotalContentLength - 1)
- {
- source.CopyTo(responseStream, BufferSize);
- }
- else
- {
- CopyToInternal(source, responseStream, RangeLength);
- }
- }
- }
- finally
- {
- if (OnComplete != null)
- {
- OnComplete();
- }
- }
- }
-
- private void CopyToInternal(Stream source, Stream destination, long copyLength)
- {
- var array = new byte[BufferSize];
- int count;
- while ((count = source.Read(array, 0, array.Length)) != 0)
- {
- var bytesToCopy = Math.Min(count, copyLength);
-
- destination.Write(array, 0, Convert.ToInt32(bytesToCopy));
-
- copyLength -= bytesToCopy;
-
- if (copyLength <= 0)
- {
- break;
- }
- }
- }
-
- public async Task WriteToAsync(Stream responseStream)
+ public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken)
{
try
{
@@ -276,8 +211,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer
public object Response { get; set; }
- public IContentTypeWriter ResponseFilter { get; set; }
-
public int Status { get; set; }
public HttpStatusCode StatusCode
@@ -287,7 +220,5 @@ namespace MediaBrowser.Server.Implementations.HttpServer
}
public string StatusDescription { get; set; }
-
- public int PaddingLength { get; set; }
}
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/HttpServer/ResponseFilter.cs b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs
index ee05702f4c..6d9d7d921d 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/ResponseFilter.cs
+++ b/Emby.Server.Implementations/HttpServer/ResponseFilter.cs
@@ -1,12 +1,11 @@
using MediaBrowser.Model.Logging;
-using MediaBrowser.Server.Implementations.HttpServer.SocketSharp;
-using ServiceStack.Web;
using System;
using System.Globalization;
-using System.Net;
using System.Text;
+using Emby.Server.Implementations.HttpServer.SocketSharp;
+using MediaBrowser.Model.Services;
-namespace MediaBrowser.Server.Implementations.HttpServer
+namespace Emby.Server.Implementations.HttpServer
{
public class ResponseFilter
{
@@ -28,6 +27,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer
{
// Try to prevent compatibility view
res.AddHeader("X-UA-Compatible", "IE=Edge");
+ res.AddHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization");
+ res.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
+ res.AddHeader("Access-Control-Allow-Origin", "*");
var exception = dto as Exception;
@@ -46,21 +48,21 @@ namespace MediaBrowser.Server.Implementations.HttpServer
var vary = "Accept-Encoding";
- var hasOptions = dto as IHasOptions;
+ var hasHeaders = dto as IHasHeaders;
var sharpResponse = res as WebSocketSharpResponse;
- if (hasOptions != null)
+ if (hasHeaders != null)
{
- if (!hasOptions.Options.ContainsKey("Server"))
+ if (!hasHeaders.Headers.ContainsKey("Server"))
{
- hasOptions.Options["Server"] = "Mono-HTTPAPI/1.1, UPnP/1.0 DLNADOC/1.50";
- //hasOptions.Options["Server"] = "Mono-HTTPAPI/1.1";
+ hasHeaders.Headers["Server"] = "Mono-HTTPAPI/1.1, UPnP/1.0 DLNADOC/1.50";
+ //hasHeaders.Headers["Server"] = "Mono-HTTPAPI/1.1";
}
// Content length has to be explicitly set on on HttpListenerResponse or it won't be happy
string contentLength;
- if (hasOptions.Options.TryGetValue("Content-Length", out contentLength) && !string.IsNullOrEmpty(contentLength))
+ if (hasHeaders.Headers.TryGetValue("Content-Length", out contentLength) && !string.IsNullOrEmpty(contentLength))
{
var length = long.Parse(contentLength, UsCulture);
@@ -68,15 +70,15 @@ namespace MediaBrowser.Server.Implementations.HttpServer
{
res.SetContentLength(length);
- var listenerResponse = res.OriginalResponse as HttpListenerResponse;
+ //var listenerResponse = res.OriginalResponse as HttpListenerResponse;
- if (listenerResponse != null)
- {
- // Disable chunked encoding. Technically this is only needed when using Content-Range, but
- // anytime we know the content length there's no need for it
- listenerResponse.SendChunked = false;
- return;
- }
+ //if (listenerResponse != null)
+ //{
+ // // Disable chunked encoding. Technically this is only needed when using Content-Range, but
+ // // anytime we know the content length there's no need for it
+ // listenerResponse.SendChunked = false;
+ // return;
+ //}
if (sharpResponse != null)
{
@@ -85,13 +87,13 @@ namespace MediaBrowser.Server.Implementations.HttpServer
}
}
- string hasOptionsVary;
- if (hasOptions.Options.TryGetValue("Vary", out hasOptionsVary))
+ string hasHeadersVary;
+ if (hasHeaders.Headers.TryGetValue("Vary", out hasHeadersVary))
{
- vary = hasOptionsVary;
+ vary = hasHeadersVary;
}
- hasOptions.Options["Vary"] = vary;
+ hasHeaders.Headers["Vary"] = vary;
}
//res.KeepAlive = false;
diff --git a/MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
index d8f7d889c3..4d00c9b195 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/Security/AuthService.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
@@ -10,7 +10,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
-namespace MediaBrowser.Server.Implementations.HttpServer.Security
+namespace Emby.Server.Implementations.HttpServer.Security
{
public class AuthService : IAuthService
{
diff --git a/MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
index bc3e7b163b..ec3dfeb609 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
@@ -1,12 +1,12 @@
using MediaBrowser.Controller.Connect;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Security;
-using ServiceStack.Web;
using System;
using System.Collections.Generic;
using System.Linq;
+using MediaBrowser.Model.Services;
-namespace MediaBrowser.Server.Implementations.HttpServer.Security
+namespace Emby.Server.Implementations.HttpServer.Security
{
public class AuthorizationContext : IAuthorizationContext
{
@@ -21,7 +21,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security
public AuthorizationInfo GetAuthorizationInfo(object requestContext)
{
- var req = new ServiceStackServiceRequest((IRequest)requestContext);
+ var req = new ServiceRequest((IRequest)requestContext);
return GetAuthorizationInfo(req);
}
diff --git a/MediaBrowser.Server.Implementations/HttpServer/Security/SessionContext.cs b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
index a498d32fac..33dd4e2d77 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/Security/SessionContext.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
@@ -3,10 +3,10 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Session;
-using ServiceStack.Web;
using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
-namespace MediaBrowser.Server.Implementations.HttpServer.Security
+namespace Emby.Server.Implementations.HttpServer.Security
{
public class SessionContext : ISessionContext
{
@@ -47,7 +47,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security
public Task<SessionInfo> GetSession(object requestContext)
{
- var req = new ServiceStackServiceRequest((IRequest)requestContext);
+ var req = new ServiceRequest((IRequest)requestContext);
return GetSession(req);
}
@@ -60,7 +60,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security
public Task<User> GetUser(object requestContext)
{
- var req = new ServiceStackServiceRequest((IRequest)requestContext);
+ var req = new ServiceRequest((IRequest)requestContext);
return GetUser(req);
}
}
diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/Extensions.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/Extensions.cs
new file mode 100644
index 0000000000..07a338f198
--- /dev/null
+++ b/Emby.Server.Implementations/HttpServer/SocketSharp/Extensions.cs
@@ -0,0 +1,12 @@
+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/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/HttpUtility.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/HttpUtility.cs
index 3ef48d13a0..4fbe0ed946 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/HttpUtility.cs
+++ b/Emby.Server.Implementations/HttpServer/SocketSharp/HttpUtility.cs
@@ -1,32 +1,13 @@
using System;
using System.Collections;
using System.Collections.Generic;
-using System.Collections.Specialized;
using System.Text;
+using MediaBrowser.Model.Services;
-namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
+namespace Emby.Server.Implementations.HttpServer.SocketSharp
{
public static class MyHttpUtility
{
- sealed class HttpQSCollection : NameValueCollection
- {
- public override string ToString()
- {
- int count = Count;
- if (count == 0)
- return "";
- StringBuilder sb = new StringBuilder();
- string[] keys = AllKeys;
- for (int i = 0; i < count; i++)
- {
- sb.AppendFormat("{0}={1}&", keys[i], this[keys[i]]);
- }
- if (sb.Length > 0)
- sb.Length--;
- return sb.ToString();
- }
- }
-
// Must be sorted
static readonly long[] entities = new long[] {
(long)'A' << 56 | (long)'E' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24,
@@ -606,7 +587,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
byte[] buf = bytes.ToArray();
bytes = null;
- return e.GetString(buf);
+ return e.GetString(buf, 0, buf.Length);
}
@@ -857,28 +838,28 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
return output.ToString();
}
- public static NameValueCollection ParseQueryString(string query)
+ public static QueryParamCollection ParseQueryString(string query)
{
return ParseQueryString(query, Encoding.UTF8);
}
- public static NameValueCollection ParseQueryString(string query, Encoding encoding)
+ 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 NameValueCollection();
+ return new QueryParamCollection();
if (query[0] == '?')
query = query.Substring(1);
- NameValueCollection result = new HttpQSCollection();
+ QueryParamCollection result = new QueryParamCollection();
ParseQueryString(query, encoding, result);
return result;
}
- internal static void ParseQueryString(string query, Encoding encoding, NameValueCollection result)
+ internal static void ParseQueryString(string query, Encoding encoding, QueryParamCollection result)
{
if (query.Length == 0)
return;
diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/RequestMono.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/RequestMono.cs
index d20dd7ec08..ec14c32c8f 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/RequestMono.cs
+++ b/Emby.Server.Implementations/HttpServer/SocketSharp/RequestMono.cs
@@ -1,14 +1,14 @@
using System;
+using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.IO;
+using System.Net;
using System.Text;
using System.Threading.Tasks;
-using System.Web;
-using ServiceStack;
-using ServiceStack.Web;
+using MediaBrowser.Model.Services;
-namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
+namespace Emby.Server.Implementations.HttpServer.SocketSharp
{
public partial class WebSocketSharpRequest : IHttpRequest
{
@@ -69,7 +69,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
input.Position = e.Start;
input.Read(copy, 0, (int)e.Length);
- form.Add(e.Name, (e.Encoding ?? ContentEncoding).GetString(copy));
+ form.Add(e.Name, (e.Encoding ?? ContentEncoding).GetString(copy, 0, copy.Length));
}
else
{
@@ -77,20 +77,20 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
// We use a substream, as in 2.x we will support large uploads streamed to disk,
//
HttpPostedFile sub = new HttpPostedFile(e.Filename, e.ContentType, input, e.Start, e.Length);
- files.AddFile(e.Name, sub);
+ files[e.Name] = sub;
}
}
}
}
- public NameValueCollection Form
+ public QueryParamCollection Form
{
get
{
if (form == null)
{
form = new WebROCollection();
- files = new HttpFileCollection();
+ files = new Dictionary<string, HttpPostedFile>();
if (IsContentType("multipart/form-data", true))
{
@@ -128,7 +128,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
{
get
{
- return string.IsNullOrEmpty(request.Headers[HttpHeaders.Accept]) ? null : request.Headers[HttpHeaders.Accept];
+ return string.IsNullOrEmpty(request.Headers["Accept"]) ? null : request.Headers["Accept"];
}
}
@@ -136,7 +136,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
{
get
{
- return string.IsNullOrEmpty(request.Headers[HttpHeaders.Authorization]) ? null : request.Headers[HttpHeaders.Authorization];
+ return string.IsNullOrEmpty(request.Headers["Authorization"]) ? null : request.Headers["Authorization"];
}
}
@@ -152,17 +152,18 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
string msg = String.Format("A potentially dangerous Request.{0} value was " +
"detected from the client ({1}={2}).", name, key, v);
- throw new HttpRequestValidationException(msg);
+ throw new Exception(msg);
}
- static void ValidateNameValueCollection(string name, NameValueCollection coll)
+ static void ValidateNameValueCollection(string name, QueryParamCollection coll)
{
if (coll == null)
return;
- foreach (string key in coll.Keys)
+ foreach (var pair in coll)
{
- string val = coll[key];
+ var key = pair.Name;
+ var val = pair.Value;
if (val != null && val.Length > 0 && IsInvalidString(val))
ThrowValidationException(name, key, val);
}
@@ -224,7 +225,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
if (starts_with)
return StrUtils.StartsWith(ContentType, ct, true);
- return String.Compare(ContentType, ct, true, Helpers.InvariantCulture) == 0;
+ return string.Equals(ContentType, ct, StringComparison.OrdinalIgnoreCase);
}
async Task LoadWwwForm()
@@ -277,9 +278,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
void AddRawKeyValue(StringBuilder key, StringBuilder value)
{
- string decodedKey = HttpUtility.UrlDecode(key.ToString(), ContentEncoding);
+ string decodedKey = WebUtility.UrlDecode(key.ToString());
form.Add(decodedKey,
- HttpUtility.UrlDecode(value.ToString(), ContentEncoding));
+ WebUtility.UrlDecode(value.ToString()));
key.Length = 0;
value.Length = 0;
@@ -287,68 +288,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
WebROCollection form;
- HttpFileCollection files;
+ Dictionary<string, HttpPostedFile> files;
- public sealed class HttpFileCollection : NameObjectCollectionBase
- {
- internal HttpFileCollection()
- {
- }
-
- internal void AddFile(string name, HttpPostedFile file)
- {
- BaseAdd(name, file);
- }
-
- public void CopyTo(Array dest, int index)
- {
- /* XXX this is kind of gross and inefficient
- * since it makes a copy of the superclass's
- * list */
- object[] values = BaseGetAllValues();
- values.CopyTo(dest, index);
- }
-
- public string GetKey(int index)
- {
- return BaseGetKey(index);
- }
-
- public HttpPostedFile Get(int index)
- {
- return (HttpPostedFile)BaseGet(index);
- }
-
- public HttpPostedFile Get(string key)
- {
- return (HttpPostedFile)BaseGet(key);
- }
-
- public HttpPostedFile this[string key]
- {
- get
- {
- return Get(key);
- }
- }
-
- public HttpPostedFile this[int index]
- {
- get
- {
- return Get(index);
- }
- }
-
- public string[] AllKeys
- {
- get
- {
- return BaseGetAllKeys();
- }
- }
- }
- class WebROCollection : NameValueCollection
+ class WebROCollection : QueryParamCollection
{
bool got_id;
int id;
@@ -369,28 +311,29 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
}
public void Protect()
{
- IsReadOnly = true;
+ //IsReadOnly = true;
}
public void Unprotect()
{
- IsReadOnly = false;
+ //IsReadOnly = false;
}
public override string ToString()
{
StringBuilder result = new StringBuilder();
- foreach (string key in AllKeys)
+ foreach (var pair in this)
{
if (result.Length > 0)
result.Append('&');
+ var key = pair.Name;
if (key != null && key.Length > 0)
{
result.Append(key);
result.Append('=');
}
- result.Append(Get(key));
+ result.Append(pair.Value);
}
return result.ToString();
@@ -588,29 +531,15 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
internal sealed class StrUtils
{
- StrUtils() { }
-
- public static bool StartsWith(string str1, string str2)
- {
- return StartsWith(str1, str2, false);
- }
-
public static bool StartsWith(string str1, string str2, bool ignore_case)
{
- int l2 = str2.Length;
- if (l2 == 0)
- return true;
-
- int l1 = str1.Length;
- if (l2 > l1)
+ if (string.IsNullOrWhiteSpace(str1))
+ {
return false;
+ }
- return 0 == String.Compare(str1, 0, str2, 0, l2, ignore_case, Helpers.InvariantCulture);
- }
-
- public static bool EndsWith(string str1, string str2)
- {
- return EndsWith(str1, str2, false);
+ var comparison = ignore_case ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
+ return str1.IndexOf(str2, comparison) == 0;
}
public static bool EndsWith(string str1, string str2, bool ignore_case)
@@ -623,7 +552,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
if (l2 > l1)
return false;
- return 0 == String.Compare(str1, l1 - l2, str2, 0, l2, ignore_case, Helpers.InvariantCulture);
+ var comparison = ignore_case ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
+ return str1.IndexOf(str2, comparison) == str1.Length - str2.Length - 1;
}
}
@@ -741,7 +671,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
for (int i = temp.Length - 1; i >= 0; i--)
source[i] = (byte)temp[i];
- return encoding.GetString(source);
+ return encoding.GetString(source, 0, source.Length);
}
bool ReadBoundary()
diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/SharpWebSocket.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/SharpWebSocket.cs
index d363c4de6c..9823a2ff50 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/SharpWebSocket.cs
+++ b/Emby.Server.Implementations/HttpServer/SocketSharp/SharpWebSocket.cs
@@ -6,7 +6,7 @@ using System.Threading;
using System.Threading.Tasks;
using WebSocketState = MediaBrowser.Model.Net.WebSocketState;
-namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
+namespace Emby.Server.Implementations.HttpServer.SocketSharp
{
public class SharpWebSocket : IWebSocket
{
@@ -25,12 +25,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
- /// <summary>
- /// Initializes a new instance of the <see cref="NativeWebSocket" /> class.
- /// </summary>
- /// <param name="socket">The socket.</param>
- /// <param name="logger">The logger.</param>
- /// <exception cref="System.ArgumentNullException">socket</exception>
public SharpWebSocket(SocketHttpListener.WebSocket socket, ILogger logger)
{
if (socket == null)
@@ -108,11 +102,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
/// <returns>Task.</returns>
public Task SendAsync(byte[] bytes, bool endOfMessage, CancellationToken cancellationToken)
{
- var completionSource = new TaskCompletionSource<bool>();
-
- WebSocket.SendAsync(bytes, res => completionSource.TrySetResult(true));
-
- return completionSource.Task;
+ return WebSocket.SendAsync(bytes);
}
/// <summary>
@@ -124,11 +114,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
/// <returns>Task.</returns>
public Task SendAsync(string text, bool endOfMessage, CancellationToken cancellationToken)
{
- var completionSource = new TaskCompletionSource<bool>();
-
- WebSocket.SendAsync(text, res => completionSource.TrySetResult(true));
-
- return completionSource.Task;
+ return WebSocket.SendAsync(text);
}
/// <summary>
diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs
index b090c97c6e..4606d0e316 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs
+++ b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs
@@ -1,35 +1,50 @@
-using System.Collections.Specialized;
-using MediaBrowser.Controller.Net;
+using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Logging;
-using MediaBrowser.Server.Implementations.Logging;
-using ServiceStack;
-using ServiceStack.Web;
using SocketHttpListener.Net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-
-namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
+using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Cryptography;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Services;
+using MediaBrowser.Model.Text;
+using SocketHttpListener.Primitives;
+
+namespace Emby.Server.Implementations.HttpServer.SocketSharp
{
public class WebSocketSharpListener : IHttpListener
{
private HttpListener _listener;
private readonly ILogger _logger;
- private readonly string _certificatePath;
- private readonly IMemoryStreamProvider _memoryStreamProvider;
-
- public WebSocketSharpListener(ILogger logger, string certificatePath, IMemoryStreamProvider memoryStreamProvider)
+ private readonly ICertificate _certificate;
+ private readonly IMemoryStreamFactory _memoryStreamProvider;
+ private readonly ITextEncoding _textEncoding;
+ private readonly INetworkManager _networkManager;
+ private readonly ISocketFactory _socketFactory;
+ private readonly ICryptoProvider _cryptoProvider;
+ private readonly IStreamFactory _streamFactory;
+ private readonly Func<HttpListenerContext, IHttpRequest> _httpRequestFactory;
+ private readonly bool _enableDualMode;
+
+ public WebSocketSharpListener(ILogger logger, ICertificate certificate, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, INetworkManager networkManager, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, bool enableDualMode, Func<HttpListenerContext, IHttpRequest> httpRequestFactory)
{
_logger = logger;
- _certificatePath = certificatePath;
+ _certificate = certificate;
_memoryStreamProvider = memoryStreamProvider;
+ _textEncoding = textEncoding;
+ _networkManager = networkManager;
+ _socketFactory = socketFactory;
+ _cryptoProvider = cryptoProvider;
+ _streamFactory = streamFactory;
+ _enableDualMode = enableDualMode;
+ _httpRequestFactory = httpRequestFactory;
}
public Action<Exception, IRequest> ErrorHandler { get; set; }
-
public Func<IHttpRequest, Uri, Task> RequestHandler { get; set; }
public Action<WebSocketConnectingEventArgs> WebSocketConnecting { get; set; }
@@ -39,7 +54,14 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
public void Start(IEnumerable<string> urlPrefixes)
{
if (_listener == null)
- _listener = new HttpListener(new PatternsLogger(_logger), _certificatePath);
+ _listener = new HttpListener(_logger, _cryptoProvider, _streamFactory, _socketFactory, _networkManager, _textEncoding, _memoryStreamProvider);
+
+ _listener.EnableDualMode = _enableDualMode;
+
+ if (_certificate != null)
+ {
+ _listener.LoadCert(_certificate);
+ }
foreach (var prefix in urlPrefixes)
{
@@ -54,44 +76,36 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
private void ProcessContext(HttpListenerContext context)
{
- Task.Factory.StartNew(() => InitTask(context));
+ //Task.Factory.StartNew(() => InitTask(context), TaskCreationOptions.DenyChildAttach | TaskCreationOptions.PreferFairness);
+ Task.Run(() => InitTask(context));
}
- private void InitTask(HttpListenerContext context)
+ private Task InitTask(HttpListenerContext context)
{
+ IHttpRequest httpReq = null;
+ var request = context.Request;
+
try
{
- var task = this.ProcessRequestAsync(context);
- task.ContinueWith(x => HandleError(x.Exception, context), TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.AttachedToParent);
+ if (request.IsWebSocketRequest)
+ {
+ LoggerUtils.LogRequest(_logger, request);
+
+ ProcessWebSocketRequest(context);
+ return Task.FromResult(true);
+ }
- //if (task.Status == TaskStatus.Created)
- //{
- // task.RunSynchronously();
- //}
+ httpReq = GetRequest(context);
}
catch (Exception ex)
{
- HandleError(ex, context);
- }
- }
-
- private Task ProcessRequestAsync(HttpListenerContext context)
- {
- var request = context.Request;
-
- if (request.IsWebSocketRequest)
- {
- LoggerUtils.LogRequest(_logger, request);
+ _logger.ErrorException("Error processing request", ex);
- ProcessWebSocketRequest(context);
+ httpReq = httpReq ?? GetRequest(context);
+ ErrorHandler(ex, httpReq);
return Task.FromResult(true);
}
- if (string.IsNullOrEmpty(context.Request.RawUrl))
- return ((object)null).AsTaskResult();
-
- var httpReq = GetRequest(context);
-
return RequestHandler(httpReq, request.Url);
}
@@ -101,12 +115,11 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
{
var endpoint = ctx.Request.RemoteEndPoint.ToString();
var url = ctx.Request.RawUrl;
- var queryString = new NameValueCollection(ctx.Request.QueryString ?? new NameValueCollection());
var connectingArgs = new WebSocketConnectingEventArgs
{
Url = url,
- QueryString = queryString,
+ QueryString = ctx.Request.QueryString,
Endpoint = endpoint
};
@@ -126,7 +139,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
WebSocketConnected(new WebSocketConnectEventArgs
{
Url = url,
- QueryString = queryString,
+ QueryString = ctx.Request.QueryString,
WebSocket = new SharpWebSocket(webSocketContext.WebSocket, _logger),
Endpoint = endpoint
});
@@ -149,22 +162,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
private IHttpRequest GetRequest(HttpListenerContext httpContext)
{
- var operationName = httpContext.Request.GetOperationName();
-
- var req = new WebSocketSharpRequest(httpContext, operationName, RequestAttributes.None, _logger, _memoryStreamProvider);
- req.RequestAttributes = req.GetAttributes();
-
- return req;
- }
-
- private void HandleError(Exception ex, HttpListenerContext context)
- {
- var httpReq = GetRequest(context);
-
- if (ErrorHandler != null)
- {
- ErrorHandler(ex, httpReq);
- }
+ return _httpRequestFactory(httpContext);
}
public void Stop()
@@ -205,4 +203,5 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
}
}
}
+
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs
index b5c8d01075..b3fcde7456 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs
+++ b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs
@@ -1,33 +1,33 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Net;
using System.Text;
-using Funq;
-using MediaBrowser.Common.IO;
+using Emby.Server.Implementations.HttpServer;
+using Emby.Server.Implementations.HttpServer.SocketSharp;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
-using ServiceStack;
-using ServiceStack.Host;
-using ServiceStack.Web;
+using MediaBrowser.Model.Services;
using SocketHttpListener.Net;
+using IHttpFile = MediaBrowser.Model.Services.IHttpFile;
+using IHttpRequest = MediaBrowser.Model.Services.IHttpRequest;
+using IHttpResponse = MediaBrowser.Model.Services.IHttpResponse;
+using IResponse = MediaBrowser.Model.Services.IResponse;
-namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
+namespace Emby.Server.Implementations.HttpServer.SocketSharp
{
public partial class WebSocketSharpRequest : IHttpRequest
{
- public Container Container { get; set; }
private readonly HttpListenerRequest request;
private readonly IHttpResponse response;
- private readonly IMemoryStreamProvider _memoryStreamProvider;
+ private readonly IMemoryStreamFactory _memoryStreamProvider;
- public WebSocketSharpRequest(HttpListenerContext httpContext, string operationName, RequestAttributes requestAttributes, ILogger logger, IMemoryStreamProvider memoryStreamProvider)
+ public WebSocketSharpRequest(HttpListenerContext httpContext, string operationName, ILogger logger, IMemoryStreamFactory memoryStreamProvider)
{
this.OperationName = operationName;
- this.RequestAttributes = requestAttributes;
_memoryStreamProvider = memoryStreamProvider;
this.request = httpContext.Request;
this.response = new WebSocketSharpResponse(logger, httpContext.Response, this);
-
- this.RequestPreferences = new RequestPreferences(this);
}
public HttpListenerRequest HttpRequest
@@ -50,40 +50,10 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
get { return response; }
}
- public RequestAttributes RequestAttributes { get; set; }
-
- public IRequestPreferences RequestPreferences { get; private set; }
-
- public T TryResolve<T>()
- {
- if (typeof(T) == typeof(IHttpRequest))
- throw new Exception("You don't need to use IHttpRequest.TryResolve<IHttpRequest> to resolve itself");
-
- if (typeof(T) == typeof(IHttpResponse))
- throw new Exception("Resolve IHttpResponse with 'Response' property instead of IHttpRequest.TryResolve<IHttpResponse>");
-
- return Container == null
- ? HostContext.TryResolve<T>()
- : Container.TryResolve<T>();
- }
-
public string OperationName { get; set; }
public object Dto { get; set; }
- public string GetRawBody()
- {
- if (bufferedStream != null)
- {
- return bufferedStream.ToArray().FromUtf8Bytes();
- }
-
- using (var reader = new StreamReader(InputStream))
- {
- return reader.ReadToEnd();
- }
- }
-
public string RawUrl
{
get { return request.RawUrl; }
@@ -103,7 +73,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
{
get
{
- return String.IsNullOrEmpty(request.Headers[HttpHeaders.XForwardedFor]) ? null : request.Headers[HttpHeaders.XForwardedFor];
+ return String.IsNullOrEmpty(request.Headers["X-Forwarded-For"]) ? null : request.Headers["X-Forwarded-For"];
}
}
@@ -111,7 +81,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
{
get
{
- return string.IsNullOrEmpty(request.Headers[HttpHeaders.XForwardedPort]) ? (int?)null : int.Parse(request.Headers[HttpHeaders.XForwardedPort]);
+ return string.IsNullOrEmpty(request.Headers["X-Forwarded-Port"]) ? (int?)null : int.Parse(request.Headers["X-Forwarded-Port"]);
}
}
@@ -119,7 +89,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
{
get
{
- return string.IsNullOrEmpty(request.Headers[HttpHeaders.XForwardedProtocol]) ? null : request.Headers[HttpHeaders.XForwardedProtocol];
+ return string.IsNullOrEmpty(request.Headers["X-Forwarded-Proto"]) ? null : request.Headers["X-Forwarded-Proto"];
}
}
@@ -127,7 +97,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
{
get
{
- return String.IsNullOrEmpty(request.Headers[HttpHeaders.XRealIp]) ? null : request.Headers[HttpHeaders.XRealIp];
+ return String.IsNullOrEmpty(request.Headers["X-Real-IP"]) ? null : request.Headers["X-Real-IP"];
}
}
@@ -139,7 +109,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
return remoteIp ??
(remoteIp = (CheckBadChars(XForwardedFor)) ??
(NormalizeIp(CheckBadChars(XRealIp)) ??
- (request.RemoteEndPoint != null ? NormalizeIp(request.RemoteEndPoint.Address.ToString()) : null)));
+ (request.RemoteEndPoint != null ? NormalizeIp(request.RemoteEndPoint.IpAddress.ToString()) : null)));
}
}
@@ -258,7 +228,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
get
{
return responseContentType
- ?? (responseContentType = this.GetResponseContentType());
+ ?? (responseContentType = GetResponseContentType(this));
}
set
{
@@ -267,8 +237,120 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
}
}
+ public const string FormUrlEncoded = "application/x-www-form-urlencoded";
+ public const string MultiPartFormData = "multipart/form-data";
+ private static string GetResponseContentType(IRequest httpReq)
+ {
+ var specifiedContentType = GetQueryStringContentType(httpReq);
+ if (!string.IsNullOrEmpty(specifiedContentType)) return specifiedContentType;
+
+ var serverDefaultContentType = "application/json";
+
+ var acceptContentTypes = httpReq.AcceptTypes;
+ var defaultContentType = httpReq.ContentType;
+ 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)
+ {
+ if (hasDefaultContentType)
+ return defaultContentType;
+ if (serverDefaultContentType != null)
+ return serverDefaultContentType;
+ }
+ }
+
+ if (acceptContentTypes == null && httpReq.ContentType == Soap11)
+ {
+ return Soap11;
+ }
+
+ //We could also send a '406 Not Acceptable', but this is allowed also
+ return serverDefaultContentType;
+ }
+
+ public const string Soap11 = "text/xml; charset=utf-8";
+
+ public static bool HasAnyOfContentTypes(IRequest request, params string[] contentTypes)
+ {
+ if (contentTypes == null || request.ContentType == null) return false;
+ foreach (var contentType in contentTypes)
+ {
+ if (IsContentType(request, contentType)) return true;
+ }
+ return false;
+ }
+
+ public static bool IsContentType(IRequest request, string contentType)
+ {
+ return request.ContentType.StartsWith(contentType, StringComparison.OrdinalIgnoreCase);
+ }
+
+ public const string Xml = "application/xml";
+ private static string GetQueryStringContentType(IRequest httpReq)
+ {
+ var format = httpReq.QueryString["format"];
+ if (format == null)
+ {
+ const int formatMaxLength = 4;
+ var pi = httpReq.PathInfo;
+ if (pi == null || pi.Length <= formatMaxLength) return null;
+ if (pi[0] == '/') pi = pi.Substring(1);
+ format = LeftPart(pi, '/');
+ if (format.Length > formatMaxLength) return null;
+ }
+
+ format = LeftPart(format, '.').ToLower();
+ if (format.Contains("json")) return "application/json";
+ if (format.Contains("xml")) return Xml;
+
+ return null;
+ }
+
+ public static string LeftPart(string strVal, char needle)
+ {
+ if (strVal == null) return null;
+ var pos = strVal.IndexOf(needle);
+ return pos == -1
+ ? strVal
+ : strVal.Substring(0, pos);
+ }
+
public bool HasExplicitResponseContentType { get; private set; }
+ public static string HandlerFactoryPath;
+
private string pathInfo;
public string PathInfo
{
@@ -276,13 +358,13 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
{
if (this.pathInfo == null)
{
- var mode = HostContext.Config.HandlerFactoryPath;
+ var mode = HandlerFactoryPath;
var pos = request.RawUrl.IndexOf("?");
if (pos != -1)
{
var path = request.RawUrl.Substring(0, pos);
- this.pathInfo = HttpRequestExtensions.GetPathInfo(
+ this.pathInfo = GetPathInfo(
path,
mode,
mode ?? "");
@@ -292,13 +374,62 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
this.pathInfo = request.RawUrl;
}
- this.pathInfo = this.pathInfo.UrlDecode();
+ this.pathInfo = WebUtility.UrlDecode(pathInfo);
this.pathInfo = NormalizePathInfo(pathInfo, mode);
}
return this.pathInfo;
}
}
+ private static string GetPathInfo(string fullPath, string mode, string appPath)
+ {
+ var pathInfo = ResolvePathInfoFromMappedPath(fullPath, mode);
+ if (!string.IsNullOrEmpty(pathInfo)) return pathInfo;
+
+ //Wildcard mode relies on this to work out the handlerPath
+ pathInfo = ResolvePathInfoFromMappedPath(fullPath, appPath);
+ if (!string.IsNullOrEmpty(pathInfo)) return pathInfo;
+
+ return fullPath;
+ }
+
+
+
+ private static string ResolvePathInfoFromMappedPath(string fullPath, string mappedPathRoot)
+ {
+ if (mappedPathRoot == null) return null;
+
+ var sbPathInfo = new StringBuilder();
+ var fullPathParts = fullPath.Split('/');
+ var mappedPathRootParts = mappedPathRoot.Split('/');
+ var fullPathIndexOffset = mappedPathRootParts.Length - 1;
+ var pathRootFound = false;
+
+ for (var fullPathIndex = 0; fullPathIndex < fullPathParts.Length; fullPathIndex++)
+ {
+ if (pathRootFound)
+ {
+ sbPathInfo.Append("/" + fullPathParts[fullPathIndex]);
+ }
+ else if (fullPathIndex - fullPathIndexOffset >= 0)
+ {
+ pathRootFound = true;
+ for (var mappedPathRootIndex = 0; mappedPathRootIndex < mappedPathRootParts.Length; mappedPathRootIndex++)
+ {
+ if (!string.Equals(fullPathParts[fullPathIndex - fullPathIndexOffset + mappedPathRootIndex], mappedPathRootParts[mappedPathRootIndex], StringComparison.OrdinalIgnoreCase))
+ {
+ pathRootFound = false;
+ break;
+ }
+ }
+ }
+ }
+ if (!pathRootFound) return null;
+
+ var path = sbPathInfo.ToString();
+ return path.Length > 1 ? path.TrimEnd('/') : "/";
+ }
+
private Dictionary<string, System.Net.Cookie> cookies;
public IDictionary<string, System.Net.Cookie> Cookies
{
@@ -307,9 +438,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
if (cookies == null)
{
cookies = new Dictionary<string, System.Net.Cookie>();
- for (var i = 0; i < this.request.Cookies.Count; i++)
+ foreach (var cookie in this.request.Cookies)
{
- var httpCookie = this.request.Cookies[i];
+ var httpCookie = (Cookie) cookie;
cookies[httpCookie.Name] = new System.Net.Cookie(httpCookie.Name, httpCookie.Value, httpCookie.Path, httpCookie.Domain);
}
}
@@ -323,22 +454,21 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
get { return request.UserAgent; }
}
- private NameValueCollectionWrapper headers;
- public INameValueCollection Headers
+ public QueryParamCollection Headers
{
- get { return headers ?? (headers = new NameValueCollectionWrapper(request.Headers)); }
+ get { return request.Headers; }
}
- private NameValueCollectionWrapper queryString;
- public INameValueCollection QueryString
+ private QueryParamCollection queryString;
+ public QueryParamCollection QueryString
{
- get { return queryString ?? (queryString = new NameValueCollectionWrapper(MyHttpUtility.ParseQueryString(request.Url.Query))); }
+ get { return queryString ?? (queryString = MyHttpUtility.ParseQueryString(request.Url.Query)); }
}
- private NameValueCollectionWrapper formData;
- public INameValueCollection FormData
+ private QueryParamCollection formData;
+ public QueryParamCollection FormData
{
- get { return formData ?? (formData = new NameValueCollectionWrapper(this.Form)); }
+ get { return formData ?? (formData = this.Form); }
}
public bool IsLocal
@@ -352,8 +482,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
get
{
return httpMethod
- ?? (httpMethod = Param(HttpHeaders.XHttpMethodOverride)
- ?? request.HttpMethod);
+ ?? (httpMethod = request.HttpMethod);
}
}
@@ -400,21 +529,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
}
}
- public bool UseBufferedStream
- {
- get { return bufferedStream != null; }
- set
- {
- bufferedStream = value
- ? bufferedStream ?? _memoryStreamProvider.CreateNew(request.InputStream.ReadFully())
- : null;
- }
- }
-
- private MemoryStream bufferedStream;
public Stream InputStream
{
- get { return bufferedStream ?? request.InputStream; }
+ get { return request.InputStream; }
}
public long ContentLength
@@ -433,10 +550,10 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
return httpFiles = new IHttpFile[0];
httpFiles = new IHttpFile[files.Count];
- for (var i = 0; i < files.Count; i++)
+ var i = 0;
+ foreach (var pair in files)
{
- var reqFile = files[i];
-
+ var reqFile = pair.Value;
httpFiles[i] = new HttpFile
{
ContentType = reqFile.ContentType,
@@ -444,25 +561,25 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
FileName = reqFile.FileName,
InputStream = reqFile.InputStream,
};
+ i++;
}
}
return httpFiles;
}
}
- static Stream GetSubStream(Stream stream, IMemoryStreamProvider streamProvider)
+ static Stream GetSubStream(Stream stream, IMemoryStreamFactory streamProvider)
{
if (stream is MemoryStream)
{
var other = (MemoryStream)stream;
- try
- {
- return new MemoryStream(other.GetBuffer(), 0, (int)other.Length, false, true);
- }
- catch (UnauthorizedAccessException)
+
+ byte[] buffer;
+ if (streamProvider.TryGetBuffer(other, out buffer))
{
- return new MemoryStream(other.ToArray(), 0, (int)other.Length, false, true);
+ return streamProvider.CreateNew(buffer);
}
+ return streamProvider.CreateNew(other.ToArray());
}
return stream;
@@ -471,7 +588,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
public static string GetHandlerPathIfAny(string listenerUrl)
{
if (listenerUrl == null) return null;
- var pos = listenerUrl.IndexOf("://", StringComparison.InvariantCultureIgnoreCase);
+ var pos = listenerUrl.IndexOf("://", StringComparison.OrdinalIgnoreCase);
if (pos == -1) return null;
var startHostUrl = listenerUrl.Substring(pos + "://".Length);
var endPos = startHostUrl.IndexOf('/');
@@ -483,7 +600,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
public static string NormalizePathInfo(string pathInfo, string handlerPath)
{
if (handlerPath != null && pathInfo.TrimStart('/').StartsWith(
- handlerPath, StringComparison.InvariantCultureIgnoreCase))
+ handlerPath, StringComparison.OrdinalIgnoreCase))
{
return pathInfo.TrimStart('/').Substring(handlerPath.Length);
}
@@ -491,4 +608,13 @@ namespace MediaBrowser.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/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs
new file mode 100644
index 0000000000..36f7954116
--- /dev/null
+++ b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs
@@ -0,0 +1,193 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Text;
+using MediaBrowser.Model.Logging;
+using SocketHttpListener.Net;
+using HttpListenerResponse = SocketHttpListener.Net.HttpListenerResponse;
+using IHttpResponse = MediaBrowser.Model.Services.IHttpResponse;
+using IRequest = MediaBrowser.Model.Services.IRequest;
+
+namespace Emby.Server.Implementations.HttpServer.SocketSharp
+{
+ public class WebSocketSharpResponse : IHttpResponse
+ {
+ private readonly ILogger _logger;
+ private readonly HttpListenerResponse _response;
+
+ public WebSocketSharpResponse(ILogger logger, HttpListenerResponse response, IRequest request)
+ {
+ _logger = logger;
+ this._response = response;
+ Items = new Dictionary<string, object>();
+ Request = request;
+ }
+
+ public IRequest Request { get; private set; }
+ public bool UseBufferedStream { get; set; }
+ public Dictionary<string, object> Items { get; private set; }
+ public object OriginalResponse
+ {
+ get { return _response; }
+ }
+
+ public int StatusCode
+ {
+ get { return this._response.StatusCode; }
+ set { this._response.StatusCode = value; }
+ }
+
+ public string StatusDescription
+ {
+ get { return this._response.StatusDescription; }
+ set { this._response.StatusDescription = value; }
+ }
+
+ public string ContentType
+ {
+ get { return _response.ContentType; }
+ set { _response.ContentType = value; }
+ }
+
+ //public ICookies Cookies { get; set; }
+
+ public void AddHeader(string name, string value)
+ {
+ if (string.Equals(name, "Content-Type", StringComparison.OrdinalIgnoreCase))
+ {
+ ContentType = value;
+ return;
+ }
+
+ _response.AddHeader(name, value);
+ }
+
+ public string GetHeader(string name)
+ {
+ return _response.Headers[name];
+ }
+
+ public void Redirect(string url)
+ {
+ _response.Redirect(url);
+ }
+
+ public Stream OutputStream
+ {
+ get { return _response.OutputStream; }
+ }
+
+ public void Close()
+ {
+ if (!this.IsClosed)
+ {
+ this.IsClosed = true;
+
+ try
+ {
+ CloseOutputStream(this._response);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error closing HttpListener output stream", ex);
+ }
+ }
+ }
+
+ public void CloseOutputStream(HttpListenerResponse response)
+ {
+ try
+ {
+ var outputStream = response.OutputStream;
+
+ // This is needed with compression
+ if (outputStream is ResponseStream)
+ {
+ //if (!string.IsNullOrWhiteSpace(GetHeader("Content-Encoding")))
+ {
+ outputStream.Flush();
+ }
+
+ outputStream.Dispose();
+ }
+ response.Close();
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error in HttpListenerResponseWrapper: " + ex.Message, ex);
+ }
+ }
+
+ public bool IsClosed
+ {
+ get;
+ private set;
+ }
+
+ public void SetContentLength(long contentLength)
+ {
+ //you can happily set the Content-Length header in Asp.Net
+ //but HttpListener will complain if you do - you have to set ContentLength64 on the response.
+ //workaround: HttpListener throws "The parameter is incorrect" exceptions when we try to set the Content-Length header
+ _response.ContentLength64 = contentLength;
+ }
+
+ public void SetCookie(Cookie cookie)
+ {
+ var cookieStr = AsHeaderValue(cookie);
+ _response.Headers.Add("Set-Cookie", cookieStr);
+ }
+
+ public static string AsHeaderValue(Cookie cookie)
+ {
+ var defaultExpires = DateTime.MinValue;
+
+ var path = cookie.Expires == defaultExpires
+ ? "/"
+ : cookie.Path ?? "/";
+
+ var sb = new StringBuilder();
+
+ sb.Append($"{cookie.Name}={cookie.Value};path={path}");
+
+ if (cookie.Expires != defaultExpires)
+ {
+ sb.Append($";expires={cookie.Expires:R}");
+ }
+
+ if (!string.IsNullOrEmpty(cookie.Domain))
+ {
+ sb.Append($";domain={cookie.Domain}");
+ }
+ //else if (restrictAllCookiesToDomain != null)
+ //{
+ // sb.Append($";domain={restrictAllCookiesToDomain}");
+ //}
+
+ if (cookie.Secure)
+ {
+ sb.Append(";Secure");
+ }
+ if (cookie.HttpOnly)
+ {
+ sb.Append(";HttpOnly");
+ }
+
+ return sb.ToString();
+ }
+
+
+ public bool SendChunked
+ {
+ get { return _response.SendChunked; }
+ set { _response.SendChunked = value; }
+ }
+
+ public bool KeepAlive { get; set; }
+
+ public void ClearCookies()
+ {
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/HttpServer/StreamWriter.cs b/Emby.Server.Implementations/HttpServer/StreamWriter.cs
index 5f122fb96f..33378949c3 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/StreamWriter.cs
+++ b/Emby.Server.Implementations/HttpServer/StreamWriter.cs
@@ -1,19 +1,19 @@
using MediaBrowser.Model.Logging;
-using ServiceStack.Web;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
+using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.IO;
-using ServiceStack;
+using MediaBrowser.Model.Services;
-namespace MediaBrowser.Server.Implementations.HttpServer
+namespace Emby.Server.Implementations.HttpServer
{
/// <summary>
/// Class StreamWriter
/// </summary>
- public class StreamWriter : IStreamWriter, IAsyncStreamWriter, IHasOptions
+ public class StreamWriter : IAsyncStreamWriter, IHasHeaders
{
private ILogger Logger { get; set; }
@@ -25,6 +25,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer
/// <value>The source stream.</value>
private Stream SourceStream { get; set; }
+ private byte[] SourceBytes { get; set; }
+
/// <summary>
/// The _options
/// </summary>
@@ -33,14 +35,13 @@ namespace MediaBrowser.Server.Implementations.HttpServer
/// Gets the options.
/// </summary>
/// <value>The options.</value>
- public IDictionary<string, string> Options
+ public IDictionary<string, string> Headers
{
get { return _options; }
}
public Action OnComplete { get; set; }
public Action OnError { get; set; }
- private readonly byte[] _bytes;
/// <summary>
/// Initializes a new instance of the <see cref="StreamWriter" /> class.
@@ -58,11 +59,11 @@ namespace MediaBrowser.Server.Implementations.HttpServer
SourceStream = source;
Logger = logger;
- Options["Content-Type"] = contentType;
+ Headers["Content-Type"] = contentType;
if (source.CanSeek)
{
- Options["Content-Length"] = source.Length.ToString(UsCulture);
+ Headers["Content-Length"] = source.Length.ToString(UsCulture);
}
}
@@ -73,76 +74,35 @@ namespace MediaBrowser.Server.Implementations.HttpServer
/// <param name="contentType">Type of the content.</param>
/// <param name="logger">The logger.</param>
public StreamWriter(byte[] source, string contentType, ILogger logger)
- : this(new MemoryStream(source), contentType, logger)
{
if (string.IsNullOrEmpty(contentType))
{
throw new ArgumentNullException("contentType");
}
- _bytes = source;
+ SourceBytes = source;
Logger = logger;
- Options["Content-Type"] = contentType;
+ Headers["Content-Type"] = contentType;
- Options["Content-Length"] = source.Length.ToString(UsCulture);
+ Headers["Content-Length"] = source.Length.ToString(UsCulture);
}
- private const int BufferSize = 81920;
-
- /// <summary>
- /// Writes to.
- /// </summary>
- /// <param name="responseStream">The response stream.</param>
- public void WriteTo(Stream responseStream)
+ public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken)
{
try
{
- if (_bytes != null)
- {
- responseStream.Write(_bytes, 0, _bytes.Length);
- }
- else
- {
- using (var src = SourceStream)
- {
- src.CopyTo(responseStream, BufferSize);
- }
- }
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error streaming data", ex);
+ var bytes = SourceBytes;
- if (OnError != null)
- {
- OnError();
- }
-
- throw;
- }
- finally
- {
- if (OnComplete != null)
- {
- OnComplete();
- }
- }
- }
-
- public async Task WriteToAsync(Stream responseStream)
- {
- try
- {
- if (_bytes != null)
+ if (bytes != null)
{
- await responseStream.WriteAsync(_bytes, 0, _bytes.Length);
+ await responseStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
}
else
{
using (var src = SourceStream)
{
- await src.CopyToAsync(responseStream, BufferSize).ConfigureAwait(false);
+ await src.CopyToAsync(responseStream).ConfigureAwait(false);
}
}
}
diff --git a/MediaBrowser.Server.Implementations/HttpServer/SwaggerService.cs b/Emby.Server.Implementations/HttpServer/SwaggerService.cs
index d91f316d6d..d41946645b 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/SwaggerService.cs
+++ b/Emby.Server.Implementations/HttpServer/SwaggerService.cs
@@ -1,17 +1,21 @@
using MediaBrowser.Controller;
using MediaBrowser.Controller.Net;
-using ServiceStack.Web;
using System.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Services;
-namespace MediaBrowser.Server.Implementations.HttpServer
+namespace Emby.Server.Implementations.HttpServer
{
- public class SwaggerService : IHasResultFactory, IRestfulService
+ public class SwaggerService : IService, IRequiresRequest
{
private readonly IServerApplicationPaths _appPaths;
+ private readonly IFileSystem _fileSystem;
- public SwaggerService(IServerApplicationPaths appPaths)
+ public SwaggerService(IServerApplicationPaths appPaths, IFileSystem fileSystem, IHttpResultFactory resultFactory)
{
_appPaths = appPaths;
+ _fileSystem = fileSystem;
+ _resultFactory = resultFactory;
}
/// <summary>
@@ -23,16 +27,16 @@ namespace MediaBrowser.Server.Implementations.HttpServer
{
var swaggerDirectory = Path.Combine(_appPaths.ApplicationResourcesPath, "swagger-ui");
- var requestedFile = Path.Combine(swaggerDirectory, request.ResourceName.Replace('/', Path.DirectorySeparatorChar));
+ var requestedFile = Path.Combine(swaggerDirectory, request.ResourceName.Replace('/', _fileSystem.DirectorySeparatorChar));
- return ResultFactory.GetStaticFileResult(Request, requestedFile).Result;
+ return _resultFactory.GetStaticFileResult(Request, requestedFile).Result;
}
/// <summary>
/// Gets or sets the result factory.
/// </summary>
/// <value>The result factory.</value>
- public IHttpResultFactory ResultFactory { get; set; }
+ private readonly IHttpResultFactory _resultFactory;
/// <summary>
/// Gets or sets the request context.
diff --git a/MediaBrowser.Server.Implementations/IO/FileRefresher.cs b/Emby.Server.Implementations/IO/FileRefresher.cs
index c2c776c2bb..39033249fd 100644
--- a/MediaBrowser.Server.Implementations/IO/FileRefresher.cs
+++ b/Emby.Server.Implementations/IO/FileRefresher.cs
@@ -4,17 +4,20 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Common.Events;
-using MediaBrowser.Common.ScheduledTasks;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Logging;
-using MediaBrowser.Server.Implementations.ScheduledTasks;
-using MoreLinq;
+using MediaBrowser.Model.System;
+using MediaBrowser.Model.Tasks;
+using MediaBrowser.Model.Threading;
-namespace MediaBrowser.Server.Implementations.IO
+namespace Emby.Server.Implementations.IO
{
public class FileRefresher : IDisposable
{
@@ -24,13 +27,15 @@ namespace MediaBrowser.Server.Implementations.IO
private IServerConfigurationManager ConfigurationManager { get; set; }
private readonly IFileSystem _fileSystem;
private readonly List<string> _affectedPaths = new List<string>();
- private Timer _timer;
+ private ITimer _timer;
+ private readonly ITimerFactory _timerFactory;
private readonly object _timerLock = new object();
public string Path { get; private set; }
public event EventHandler<EventArgs> Completed;
+ private readonly IEnvironmentInfo _environmentInfo;
- public FileRefresher(string path, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ITaskManager taskManager, ILogger logger)
+ public FileRefresher(string path, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ITaskManager taskManager, ILogger logger, ITimerFactory timerFactory, IEnvironmentInfo environmentInfo)
{
logger.Debug("New file refresher created for {0}", path);
Path = path;
@@ -40,6 +45,8 @@ namespace MediaBrowser.Server.Implementations.IO
LibraryManager = libraryManager;
TaskManager = taskManager;
Logger = logger;
+ _timerFactory = timerFactory;
+ _environmentInfo = environmentInfo;
AddPath(path);
}
@@ -86,7 +93,7 @@ namespace MediaBrowser.Server.Implementations.IO
if (_timer == null)
{
- _timer = new Timer(OnTimerCallback, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1));
+ _timer = _timerFactory.Create(OnTimerCallback, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1));
}
else
{
@@ -161,7 +168,7 @@ namespace MediaBrowser.Server.Implementations.IO
// If the root folder changed, run the library task so the user can see it
if (itemsToRefresh.Any(i => i is AggregateFolder))
{
- TaskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>();
+ LibraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None);
return;
}
@@ -222,7 +229,7 @@ namespace MediaBrowser.Server.Implementations.IO
private bool IsFileLocked(string path)
{
- if (Environment.OSVersion.Platform != PlatformID.Win32NT)
+ if (_environmentInfo.OperatingSystem != OperatingSystem.Windows)
{
// Causing lockups on linux
return false;
@@ -236,7 +243,7 @@ namespace MediaBrowser.Server.Implementations.IO
|| data.IsDirectory
// Opening a writable stream will fail with readonly files
- || data.Attributes.HasFlag(FileAttributes.ReadOnly))
+ || data.IsReadOnly)
{
return false;
}
@@ -255,22 +262,22 @@ namespace MediaBrowser.Server.Implementations.IO
// But if the server only has readonly access, this is going to cause this entire algorithm to fail
// So we'll take a best guess about our access level
var requestedFileAccess = ConfigurationManager.Configuration.SaveLocalMeta
- ? FileAccess.ReadWrite
- : FileAccess.Read;
+ ? FileAccessMode.ReadWrite
+ : FileAccessMode.Read;
try
{
- using (_fileSystem.GetFileStream(path, FileMode.Open, requestedFileAccess, FileShare.ReadWrite))
+ using (_fileSystem.GetFileStream(path, FileOpenMode.Open, requestedFileAccess, FileShareMode.ReadWrite))
{
//file is not locked
return false;
}
}
- catch (DirectoryNotFoundException)
- {
- // File may have been deleted
- return false;
- }
+ //catch (DirectoryNotFoundException)
+ //{
+ // // File may have been deleted
+ // return false;
+ //}
catch (FileNotFoundException)
{
// File may have been deleted
diff --git a/MediaBrowser.Server.Startup.Common/MbLinkShortcutHandler.cs b/Emby.Server.Implementations/IO/MbLinkShortcutHandler.cs
index 14588b427a..0b1391ae02 100644
--- a/MediaBrowser.Server.Startup.Common/MbLinkShortcutHandler.cs
+++ b/Emby.Server.Implementations/IO/MbLinkShortcutHandler.cs
@@ -1,8 +1,10 @@
using System;
using System.IO;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
-namespace MediaBrowser.Server.Startup.Common
+namespace Emby.Server.Implementations.IO
{
public class MbLinkShortcutHandler : IShortcutHandler
{
@@ -47,7 +49,7 @@ namespace MediaBrowser.Server.Startup.Common
throw new ArgumentNullException("targetPath");
}
- File.WriteAllText(shortcutPath, targetPath);
+ _fileSystem.WriteAllText(shortcutPath, targetPath);
}
}
}
diff --git a/MediaBrowser.Controller/IO/ThrottledStream.cs b/Emby.Server.Implementations/IO/ThrottledStream.cs
index 1df00b45a2..81760b6397 100644
--- a/MediaBrowser.Controller/IO/ThrottledStream.cs
+++ b/Emby.Server.Implementations/IO/ThrottledStream.cs
@@ -3,7 +3,7 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
-namespace MediaBrowser.Controller.IO
+namespace Emby.Server.Implementations.IO
{
/// <summary>
/// Class for streaming data with throttling support.
@@ -326,9 +326,10 @@ namespace MediaBrowser.Controller.IO
try
{
// The time to sleep is more then a millisecond, so sleep.
- Thread.Sleep(toSleep);
+ var task = Task.Delay(toSleep);
+ Task.WaitAll(task);
}
- catch (ThreadAbortException)
+ catch
{
// Eatup ThreadAbortException.
}
diff --git a/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
index 22d7ba3bec..7a36691dff 100644
--- a/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs
+++ b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
@@ -12,11 +12,14 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Net;
-namespace MediaBrowser.Server.Implementations.Photos
+namespace Emby.Server.Implementations.Images
{
public abstract class BaseDynamicImageProvider<T> : IHasItemChangeMonitor, IForcedProvider, ICustomMetadataProvider<T>, IHasOrder
where T : IHasMetadata
@@ -144,7 +147,14 @@ namespace MediaBrowser.Server.Implementations.Photos
return ItemUpdateType.None;
}
- await ProviderManager.SaveImage(item, outputPath, "image/png", imageType, null, false, cancellationToken).ConfigureAwait(false);
+ var mimeType = MimeTypes.GetMimeType(outputPath);
+
+ if (string.Equals(mimeType, "application/octet-stream", StringComparison.OrdinalIgnoreCase))
+ {
+ mimeType = "image/png";
+ }
+
+ await ProviderManager.SaveImage(item, outputPath, mimeType, imageType, null, false, cancellationToken).ConfigureAwait(false);
return ItemUpdateType.ImageUpdate;
}
@@ -351,7 +361,7 @@ namespace MediaBrowser.Server.Implementations.Photos
var ext = Path.GetExtension(image);
var outputPath = Path.ChangeExtension(outputPathWithoutExtension, ext);
- File.Copy(image, outputPath);
+ FileSystem.CopyFile(image, outputPath, true);
return outputPath;
}
diff --git a/Emby.Server.Implementations/Intros/DefaultIntroProvider.cs b/Emby.Server.Implementations/Intros/DefaultIntroProvider.cs
new file mode 100644
index 0000000000..180f6aba7f
--- /dev/null
+++ b/Emby.Server.Implementations/Intros/DefaultIntroProvider.cs
@@ -0,0 +1,384 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Security;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Entities;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.Globalization;
+
+namespace Emby.Server.Implementations.Intros
+{
+ public class DefaultIntroProvider : IIntroProvider
+ {
+ private readonly ISecurityManager _security;
+ private readonly ILocalizationManager _localization;
+ private readonly IConfigurationManager _serverConfig;
+ private readonly ILibraryManager _libraryManager;
+ private readonly IFileSystem _fileSystem;
+ private readonly IMediaSourceManager _mediaSourceManager;
+
+ public DefaultIntroProvider(ISecurityManager security, ILocalizationManager localization, IConfigurationManager serverConfig, ILibraryManager libraryManager, IFileSystem fileSystem, IMediaSourceManager mediaSourceManager)
+ {
+ _security = security;
+ _localization = localization;
+ _serverConfig = serverConfig;
+ _libraryManager = libraryManager;
+ _fileSystem = fileSystem;
+ _mediaSourceManager = mediaSourceManager;
+ }
+
+ public async Task<IEnumerable<IntroInfo>> GetIntros(BaseItem item, User user)
+ {
+ var config = GetOptions();
+
+ if (item is Movie)
+ {
+ if (!config.EnableIntrosForMovies)
+ {
+ return new List<IntroInfo>();
+ }
+ }
+ else if (item is Episode)
+ {
+ if (!config.EnableIntrosForEpisodes)
+ {
+ return new List<IntroInfo>();
+ }
+ }
+ else
+ {
+ return new List<IntroInfo>();
+ }
+
+ var ratingLevel = string.IsNullOrWhiteSpace(item.OfficialRating)
+ ? null
+ : _localization.GetRatingLevel(item.OfficialRating);
+
+ var candidates = new List<ItemWithTrailer>();
+
+ var trailerTypes = new List<TrailerType>();
+ var sourceTypes = new List<SourceType>();
+
+ if (config.EnableIntrosFromMoviesInLibrary)
+ {
+ trailerTypes.Add(TrailerType.LocalTrailer);
+ sourceTypes.Add(SourceType.Library);
+ }
+
+ if (IsSupporter)
+ {
+ if (config.EnableIntrosFromUpcomingTrailers)
+ {
+ trailerTypes.Add(TrailerType.ComingSoonToTheaters);
+ sourceTypes.Clear();
+ }
+ if (config.EnableIntrosFromUpcomingDvdMovies)
+ {
+ trailerTypes.Add(TrailerType.ComingSoonToDvd);
+ sourceTypes.Clear();
+ }
+ if (config.EnableIntrosFromUpcomingStreamingMovies)
+ {
+ trailerTypes.Add(TrailerType.ComingSoonToStreaming);
+ sourceTypes.Clear();
+ }
+ if (config.EnableIntrosFromSimilarMovies)
+ {
+ trailerTypes.Add(TrailerType.Archive);
+ sourceTypes.Clear();
+ }
+ }
+
+ if (trailerTypes.Count > 0)
+ {
+ var trailerResult = _libraryManager.GetItemList(new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { typeof(Trailer).Name },
+ TrailerTypes = trailerTypes.ToArray(),
+ SimilarTo = item,
+ IsPlayed = config.EnableIntrosForWatchedContent ? (bool?)null : false,
+ MaxParentalRating = config.EnableIntrosParentalControl ? ratingLevel : null,
+ BlockUnratedItems = config.EnableIntrosParentalControl ? new[] { UnratedItem.Trailer } : new UnratedItem[] { },
+
+ // Account for duplicates by imdb id, since the database doesn't support this yet
+ Limit = config.TrailerLimit * 2,
+ SourceTypes = sourceTypes.ToArray()
+
+ }).Where(i => string.IsNullOrWhiteSpace(i.GetProviderId(MetadataProviders.Imdb)) || !string.Equals(i.GetProviderId(MetadataProviders.Imdb), item.GetProviderId(MetadataProviders.Imdb), StringComparison.OrdinalIgnoreCase)).Take(config.TrailerLimit);
+
+ candidates.AddRange(trailerResult.Select(i => new ItemWithTrailer
+ {
+ Item = i,
+ Type = i.SourceType == SourceType.Channel ? ItemWithTrailerType.ChannelTrailer : ItemWithTrailerType.ItemWithTrailer,
+ LibraryManager = _libraryManager
+ }));
+ }
+
+ return GetResult(item, candidates, config);
+ }
+
+ private IEnumerable<IntroInfo> GetResult(BaseItem item, IEnumerable<ItemWithTrailer> candidates, CinemaModeConfiguration config)
+ {
+ var customIntros = !string.IsNullOrWhiteSpace(config.CustomIntroPath) ?
+ GetCustomIntros(config) :
+ new List<IntroInfo>();
+
+ var mediaInfoIntros = !string.IsNullOrWhiteSpace(config.MediaInfoIntroPath) ?
+ GetMediaInfoIntros(config, item) :
+ new List<IntroInfo>();
+
+ // Avoid implicitly captured closure
+ return candidates.Select(i => i.IntroInfo)
+ .Concat(customIntros.Take(1))
+ .Concat(mediaInfoIntros);
+ }
+
+ private CinemaModeConfiguration GetOptions()
+ {
+ return _serverConfig.GetConfiguration<CinemaModeConfiguration>("cinemamode");
+ }
+
+ private List<IntroInfo> GetCustomIntros(CinemaModeConfiguration options)
+ {
+ try
+ {
+ return GetCustomIntroFiles(options, true, false)
+ .OrderBy(i => Guid.NewGuid())
+ .Select(i => new IntroInfo
+ {
+ Path = i
+
+ }).ToList();
+ }
+ catch (IOException)
+ {
+ return new List<IntroInfo>();
+ }
+ }
+
+ private IEnumerable<IntroInfo> GetMediaInfoIntros(CinemaModeConfiguration options, BaseItem item)
+ {
+ try
+ {
+ var hasMediaSources = item as IHasMediaSources;
+
+ if (hasMediaSources == null)
+ {
+ return new List<IntroInfo>();
+ }
+
+ var mediaSource = _mediaSourceManager.GetStaticMediaSources(hasMediaSources, false)
+ .FirstOrDefault();
+
+ if (mediaSource == null)
+ {
+ return new List<IntroInfo>();
+ }
+
+ var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
+ var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
+
+ var allIntros = GetCustomIntroFiles(options, false, true)
+ .OrderBy(i => Guid.NewGuid())
+ .Select(i => new IntroInfo
+ {
+ Path = i
+
+ }).ToList();
+
+ var returnResult = new List<IntroInfo>();
+
+ if (videoStream != null)
+ {
+ returnResult.AddRange(GetMediaInfoIntrosByVideoStream(allIntros, videoStream).Take(1));
+ }
+
+ if (audioStream != null)
+ {
+ returnResult.AddRange(GetMediaInfoIntrosByAudioStream(allIntros, audioStream).Take(1));
+ }
+
+ returnResult.AddRange(GetMediaInfoIntrosByTags(allIntros, item.Tags).Take(1));
+
+ return returnResult.DistinctBy(i => i.Path, StringComparer.OrdinalIgnoreCase);
+ }
+ catch (IOException)
+ {
+ return new List<IntroInfo>();
+ }
+ }
+
+ private IEnumerable<IntroInfo> GetMediaInfoIntrosByVideoStream(List<IntroInfo> allIntros, MediaStream stream)
+ {
+ var codec = stream.Codec;
+
+ if (string.IsNullOrWhiteSpace(codec))
+ {
+ return new List<IntroInfo>();
+ }
+
+ return allIntros
+ .Where(i => IsMatch(i.Path, codec))
+ .OrderBy(i => Guid.NewGuid());
+ }
+
+ private IEnumerable<IntroInfo> GetMediaInfoIntrosByAudioStream(List<IntroInfo> allIntros, MediaStream stream)
+ {
+ var codec = stream.Codec;
+
+ if (string.IsNullOrWhiteSpace(codec))
+ {
+ return new List<IntroInfo>();
+ }
+
+ return allIntros
+ .Where(i => IsAudioMatch(i.Path, stream))
+ .OrderBy(i => Guid.NewGuid());
+ }
+
+ private IEnumerable<IntroInfo> GetMediaInfoIntrosByTags(List<IntroInfo> allIntros, List<string> tags)
+ {
+ return allIntros
+ .Where(i => tags.Any(t => IsMatch(i.Path, t)))
+ .OrderBy(i => Guid.NewGuid());
+ }
+
+ private bool IsMatch(string file, string attribute)
+ {
+ var filename = Path.GetFileNameWithoutExtension(file) ?? string.Empty;
+ filename = Normalize(filename);
+
+ if (string.IsNullOrWhiteSpace(filename))
+ {
+ return false;
+ }
+
+ attribute = Normalize(attribute);
+ if (string.IsNullOrWhiteSpace(attribute))
+ {
+ return false;
+ }
+
+ return string.Equals(filename, attribute, StringComparison.OrdinalIgnoreCase);
+ }
+
+ private string Normalize(string value)
+ {
+ return value;
+ }
+
+ private bool IsAudioMatch(string path, MediaStream stream)
+ {
+ if (!string.IsNullOrWhiteSpace(stream.Codec))
+ {
+ if (IsMatch(path, stream.Codec))
+ {
+ return true;
+ }
+ }
+ if (!string.IsNullOrWhiteSpace(stream.Profile))
+ {
+ if (IsMatch(path, stream.Profile))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private IEnumerable<string> GetCustomIntroFiles(CinemaModeConfiguration options, bool enableCustomIntros, bool enableMediaInfoIntros)
+ {
+ var list = new List<string>();
+
+ if (enableCustomIntros && !string.IsNullOrWhiteSpace(options.CustomIntroPath))
+ {
+ list.AddRange(_fileSystem.GetFilePaths(options.CustomIntroPath, true)
+ .Where(_libraryManager.IsVideoFile));
+ }
+
+ if (enableMediaInfoIntros && !string.IsNullOrWhiteSpace(options.MediaInfoIntroPath))
+ {
+ list.AddRange(_fileSystem.GetFilePaths(options.MediaInfoIntroPath, true)
+ .Where(_libraryManager.IsVideoFile));
+ }
+
+ return list.Distinct(StringComparer.OrdinalIgnoreCase);
+ }
+
+ public IEnumerable<string> GetAllIntroFiles()
+ {
+ return GetCustomIntroFiles(GetOptions(), true, true);
+ }
+
+ private bool IsSupporter
+ {
+ get { return _security.IsMBSupporter; }
+ }
+
+ public string Name
+ {
+ get { return "Default"; }
+ }
+
+ internal class ItemWithTrailer
+ {
+ internal BaseItem Item;
+ internal ItemWithTrailerType Type;
+ internal ILibraryManager LibraryManager;
+
+ public IntroInfo IntroInfo
+ {
+ get
+ {
+ var id = Item.Id;
+
+ if (Type == ItemWithTrailerType.ItemWithTrailer)
+ {
+ var hasTrailers = Item as IHasTrailers;
+
+ if (hasTrailers != null)
+ {
+ id = hasTrailers.LocalTrailerIds.FirstOrDefault();
+ }
+ }
+ return new IntroInfo
+ {
+ ItemId = id
+ };
+ }
+ }
+ }
+
+ internal enum ItemWithTrailerType
+ {
+ ChannelTrailer,
+ ItemWithTrailer
+ }
+ }
+
+ public class CinemaModeConfigurationFactory : IConfigurationFactory
+ {
+ public IEnumerable<ConfigurationStore> GetConfigurations()
+ {
+ return new[]
+ {
+ new ConfigurationStore
+ {
+ ConfigurationType = typeof(CinemaModeConfiguration),
+ Key = "cinemamode"
+ }
+ };
+ }
+ }
+
+}
diff --git a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
index b550d1dda2..2e69cd2efa 100644
--- a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
+++ b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
@@ -6,9 +6,11 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
-namespace MediaBrowser.Server.Implementations.Library
+namespace Emby.Server.Implementations.Library
{
/// <summary>
/// Provides the core resolver ignore rules
@@ -53,7 +55,7 @@ namespace MediaBrowser.Server.Implementations.Library
public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent)
{
var filename = fileInfo.Name;
- var isHidden = (fileInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
+ var isHidden = fileInfo.IsHidden;
var path = fileInfo.FullName;
// Handle mac .DS_Store
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 64abcc0440..5bf53fcb43 100644
--- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -1,7 +1,5 @@
-using Interfaces.IO;
-using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
-using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@@ -20,9 +18,6 @@ using MediaBrowser.Naming.Audio;
using MediaBrowser.Naming.Common;
using MediaBrowser.Naming.TV;
using MediaBrowser.Naming.Video;
-using MediaBrowser.Server.Implementations.Library.Validators;
-using MediaBrowser.Server.Implementations.Logging;
-using MediaBrowser.Server.Implementations.ScheduledTasks;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -32,20 +27,23 @@ using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using Emby.Server.Implementations.Library.Resolvers;
+using Emby.Server.Implementations.Library.Validators;
+using Emby.Server.Implementations.ScheduledTasks;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Library;
using MediaBrowser.Model.Net;
-using MediaBrowser.Server.Implementations.Library.Resolvers;
-using MoreLinq;
using SortOrder = MediaBrowser.Model.Entities.SortOrder;
using VideoResolver = MediaBrowser.Naming.Video.VideoResolver;
using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Server.Implementations.Library
+namespace Emby.Server.Implementations.Library
{
/// <summary>
/// Class LibraryManager
@@ -341,28 +339,23 @@ namespace MediaBrowser.Server.Implementations.Library
{
throw new ArgumentNullException("item");
}
- RegisterItem(item.Id, item);
- }
-
- private void RegisterItem(Guid id, BaseItem item)
- {
if (item is IItemByName)
{
- if (!(item is MusicArtist))
+ if (!(item is MusicArtist) && !(item is Studio))
{
return;
}
}
- if (item.IsFolder)
+ else if (item.IsFolder)
{
- if (!(item is ICollectionFolder) && !(item is UserView) && !(item is Channel) && !(item is AggregateFolder))
- {
- if (item.SourceType != SourceType.Library)
- {
- return;
- }
- }
+ //if (!(item is ICollectionFolder) && !(item is UserView) && !(item is Channel) && !(item is AggregateFolder))
+ //{
+ // if (item.SourceType != SourceType.Library)
+ // {
+ // return;
+ // }
+ //}
}
else
{
@@ -372,7 +365,7 @@ namespace MediaBrowser.Server.Implementations.Library
}
}
- LibraryItemsCache.AddOrUpdate(id, item, delegate { return item; });
+ LibraryItemsCache.AddOrUpdate(item.Id, item, delegate { return item; });
}
public async Task DeleteItem(BaseItem item, DeleteOptions options)
@@ -404,7 +397,7 @@ namespace MediaBrowser.Server.Implementations.Library
{
_fileSystem.DeleteDirectory(metadataPath, true);
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
}
@@ -825,6 +818,30 @@ namespace MediaBrowser.Server.Implementations.Library
return _userRootFolder;
}
+ public Guid? FindIdByPath(string path, bool? isFolder)
+ {
+ // 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
+
+ var query = new InternalItemsQuery
+ {
+ Path = path,
+ IsFolder = isFolder,
+ SortBy = new[] { ItemSortBy.DateCreated },
+ SortOrder = SortOrder.Descending,
+ Limit = 1
+ };
+
+ var id = GetItemIds(query);
+
+ if (id.Count == 0)
+ {
+ return null;
+ }
+
+ return id[0];
+ }
+
public BaseItem FindByPath(string path, bool? isFolder)
{
// If this returns multiple items it could be tricky figuring out which one is correct.
@@ -1064,6 +1081,12 @@ namespace MediaBrowser.Server.Implementations.Library
try
{
await PerformLibraryValidation(progress, cancellationToken).ConfigureAwait(false);
+
+ if (!ConfigurationManager.Configuration.EnableSeriesPresentationUniqueKey)
+ {
+ ConfigurationManager.Configuration.EnableSeriesPresentationUniqueKey = true;
+ ConfigurationManager.SaveConfiguration();
+ }
}
finally
{
@@ -1076,6 +1099,11 @@ namespace MediaBrowser.Server.Implementations.Library
{
_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);
@@ -1085,16 +1113,21 @@ namespace MediaBrowser.Server.Implementations.Library
progress.Report(1);
- var userRoot = GetUserRootFolder();
+ await GetUserRootFolder().RefreshMetadata(cancellationToken).ConfigureAwait(false);
- await userRoot.RefreshMetadata(cancellationToken).ConfigureAwait(false);
-
- await userRoot.ValidateChildren(new Progress<double>(), cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: false).ConfigureAwait(false);
+ await GetUserRootFolder().ValidateChildren(new Progress<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);
+
var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(pct => progress.Report(2 + pct * .73));
+ innerProgress.RegisterAction(pct => progress.Report(3 + pct * .72));
// Now validate the entire media library
await RootFolder.ValidateChildren(innerProgress, cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: true).ConfigureAwait(false);
@@ -1190,7 +1223,8 @@ namespace MediaBrowser.Server.Implementations.Library
{
Name = Path.GetFileName(dir),
- Locations = Directory.EnumerateFiles(dir, "*.mblink", SearchOption.TopDirectoryOnly)
+ Locations = _fileSystem.GetFilePaths(dir, false)
+ .Where(i => string.Equals(ShortcutFileExtension, Path.GetExtension(i), StringComparison.OrdinalIgnoreCase))
.Select(_fileSystem.ResolveShortcut)
.OrderBy(i => i)
.ToList(),
@@ -1262,7 +1296,6 @@ namespace MediaBrowser.Server.Implementations.Library
if (parent != null)
{
SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent });
- query.ParentId = null;
}
}
@@ -1274,10 +1307,27 @@ namespace MediaBrowser.Server.Implementations.Library
return ItemRepository.GetItemList(query);
}
- public IEnumerable<BaseItem> GetItemList(InternalItemsQuery query, IEnumerable<string> parentIds)
+ public int GetCount(InternalItemsQuery query)
{
- var parents = parentIds.Select(i => GetItemById(new Guid(i))).Where(i => i != null).ToList();
+ if (query.Recursive && query.ParentId.HasValue)
+ {
+ var parent = GetItemById(query.ParentId.Value);
+ if (parent != null)
+ {
+ SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent });
+ }
+ }
+
+ if (query.User != null)
+ {
+ AddUserToQuery(query, query.User);
+ }
+ return ItemRepository.GetCount(query);
+ }
+
+ public IEnumerable<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents)
+ {
SetTopParentIdsOrAncestors(query, parents);
if (query.AncestorIds.Length == 0 && query.TopParentIds.Length == 0)
@@ -1407,8 +1457,14 @@ namespace MediaBrowser.Server.Implementations.Library
}))
{
// Optimize by querying against top level views
- query.TopParentIds = parents.SelectMany(i => GetTopParentsForQuery(i, query.User)).Select(i => i.Id.ToString("N")).ToArray();
+ query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).Select(i => i.ToString("N")).ToArray();
query.AncestorIds = new string[] { };
+
+ // Prevent searching in all libraries due to empty filter
+ if (query.TopParentIds.Length == 0)
+ {
+ query.TopParentIds = new[] { Guid.NewGuid().ToString("N") };
+ }
}
}
@@ -1431,7 +1487,6 @@ namespace MediaBrowser.Server.Implementations.Library
if (parent != null)
{
SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent });
- query.ParentId = null;
}
}
@@ -1466,13 +1521,27 @@ namespace MediaBrowser.Server.Implementations.Library
}))
{
// Optimize by querying against top level views
- query.TopParentIds = parents.SelectMany(i => GetTopParentsForQuery(i, query.User)).Select(i => i.Id.ToString("N")).ToArray();
+ query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).Select(i => i.ToString("N")).ToArray();
+
+ // Prevent searching in all libraries due to empty filter
+ if (query.TopParentIds.Length == 0)
+ {
+ query.TopParentIds = new[] { Guid.NewGuid().ToString("N") };
+ }
}
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();
+
+ // Prevent searching in all libraries due to empty filter
+ if (query.AncestorIds.Length == 0)
+ {
+ query.AncestorIds = new[] { Guid.NewGuid().ToString("N") };
+ }
}
+
+ query.ParentId = null;
}
private void AddUserToQuery(InternalItemsQuery query, User user)
@@ -1481,8 +1550,9 @@ namespace MediaBrowser.Server.Implementations.Library
!query.ParentId.HasValue &&
query.ChannelIds.Length == 0 &&
query.TopParentIds.Length == 0 &&
- string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey)
- && query.ItemIds.Length == 0)
+ string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey) &&
+ string.IsNullOrWhiteSpace(query.SeriesPresentationUniqueKey) &&
+ query.ItemIds.Length == 0)
{
var userViews = _userviewManager().GetUserViews(new UserViewQuery
{
@@ -1491,11 +1561,11 @@ namespace MediaBrowser.Server.Implementations.Library
}, CancellationToken.None).Result.ToList();
- query.TopParentIds = userViews.SelectMany(i => GetTopParentsForQuery(i, user)).Select(i => i.Id.ToString("N")).ToArray();
+ query.TopParentIds = userViews.SelectMany(i => GetTopParentIdsForQuery(i, user)).Select(i => i.ToString("N")).ToArray();
}
}
- private IEnumerable<BaseItem> GetTopParentsForQuery(BaseItem item, User user)
+ private IEnumerable<Guid> GetTopParentIdsForQuery(BaseItem item, User user)
{
var view = item as UserView;
@@ -1503,7 +1573,7 @@ namespace MediaBrowser.Server.Implementations.Library
{
if (string.Equals(view.ViewType, CollectionType.LiveTv))
{
- return new[] { view };
+ return new[] { view.Id };
}
if (string.Equals(view.ViewType, CollectionType.Channels))
{
@@ -1513,7 +1583,7 @@ namespace MediaBrowser.Server.Implementations.Library
}, CancellationToken.None).Result;
- return channelResult.Items;
+ return channelResult.Items.Select(i => i.Id);
}
// Translate view into folders
@@ -1522,45 +1592,45 @@ namespace MediaBrowser.Server.Implementations.Library
var displayParent = GetItemById(view.DisplayParentId);
if (displayParent != null)
{
- return GetTopParentsForQuery(displayParent, user);
+ return GetTopParentIdsForQuery(displayParent, user);
}
- return new BaseItem[] { };
+ return new Guid[] { };
}
if (view.ParentId != Guid.Empty)
{
var displayParent = GetItemById(view.ParentId);
if (displayParent != null)
{
- return GetTopParentsForQuery(displayParent, user);
+ return GetTopParentIdsForQuery(displayParent, user);
}
- return new BaseItem[] { };
+ return new Guid[] { };
}
// Handle grouping
- if (user != null && !string.IsNullOrWhiteSpace(view.ViewType) && UserView.IsEligibleForGrouping(view.ViewType))
+ if (user != null && !string.IsNullOrWhiteSpace(view.ViewType) && UserView.IsEligibleForGrouping(view.ViewType) && user.Configuration.GroupedFolders.Length > 0)
{
return user.RootFolder
.GetChildren(user, true)
.OfType<CollectionFolder>()
.Where(i => string.IsNullOrWhiteSpace(i.CollectionType) || string.Equals(i.CollectionType, view.ViewType, StringComparison.OrdinalIgnoreCase))
.Where(i => user.IsFolderGrouped(i.Id))
- .SelectMany(i => GetTopParentsForQuery(i, user));
+ .SelectMany(i => GetTopParentIdsForQuery(i, user));
}
- return new BaseItem[] { };
+ return new Guid[] { };
}
var collectionFolder = item as CollectionFolder;
if (collectionFolder != null)
{
- return collectionFolder.GetPhysicalParents();
+ return collectionFolder.PhysicalFolderIds;
}
var topParent = item.GetTopParent();
if (topParent != null)
{
- return new[] { topParent };
+ return new[] { topParent.Id };
}
- return new BaseItem[] { };
+ return new Guid[] { };
}
/// <summary>
@@ -2266,7 +2336,7 @@ namespace MediaBrowser.Server.Implementations.Library
public bool IsVideoFile(string path, LibraryOptions libraryOptions)
{
- var resolver = new VideoResolver(GetNamingOptions(libraryOptions), new PatternsLogger());
+ var resolver = new VideoResolver(GetNamingOptions(libraryOptions), new NullLogger());
return resolver.IsVideoFile(path);
}
@@ -2294,7 +2364,7 @@ namespace MediaBrowser.Server.Implementations.Library
public bool FillMissingEpisodeNumbersFromPath(Episode episode)
{
var resolver = new EpisodeResolver(GetNamingOptions(),
- new PatternsLogger());
+ new NullLogger());
var isFolder = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd ||
episode.VideoType == VideoType.HdDvd;
@@ -2303,11 +2373,11 @@ namespace MediaBrowser.Server.Implementations.Library
var episodeInfo = locationType == LocationType.FileSystem || locationType == LocationType.Offline ?
resolver.Resolve(episode.Path, isFolder) :
- new Naming.TV.EpisodeInfo();
+ new MediaBrowser.Naming.TV.EpisodeInfo();
if (episodeInfo == null)
{
- episodeInfo = new Naming.TV.EpisodeInfo();
+ episodeInfo = new MediaBrowser.Naming.TV.EpisodeInfo();
}
var changed = false;
@@ -2440,7 +2510,7 @@ namespace MediaBrowser.Server.Implementations.Library
public ItemLookupInfo ParseName(string name)
{
- var resolver = new VideoResolver(GetNamingOptions(), new PatternsLogger());
+ var resolver = new VideoResolver(GetNamingOptions(), new NullLogger());
var result = resolver.CleanDateTime(name);
var cleanName = resolver.CleanString(result.Name);
@@ -2459,14 +2529,9 @@ namespace MediaBrowser.Server.Implementations.Library
.SelectMany(i => _fileSystem.GetFiles(i.FullName, false))
.ToList();
- var videoListResolver = new VideoListResolver(GetNamingOptions(), new PatternsLogger());
-
- var videos = videoListResolver.Resolve(fileSystemChildren.Select(i => new FileMetadata
- {
- Id = i.FullName,
- IsFolder = (i.Attributes & FileAttributes.Directory) == FileAttributes.Directory
+ var videoListResolver = new VideoListResolver(GetNamingOptions(), new NullLogger());
- }).ToList());
+ var videos = videoListResolver.Resolve(fileSystemChildren);
var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files.First().Path, StringComparison.OrdinalIgnoreCase));
@@ -2501,21 +2566,18 @@ namespace MediaBrowser.Server.Implementations.Library
}).OrderBy(i => i.Path).ToList();
}
+ private static readonly string[] ExtrasSubfolderNames = new[] { "extras", "specials", "shorts", "scenes", "featurettes", "behind the scenes", "deleted scenes" };
+
public IEnumerable<Video> FindExtras(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
{
var files = fileSystemChildren.Where(i => i.IsDirectory)
- .Where(i => string.Equals(i.Name, "extras", StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, "specials", StringComparison.OrdinalIgnoreCase))
+ .Where(i => ExtrasSubfolderNames.Contains(i.Name ?? string.Empty, StringComparer.OrdinalIgnoreCase))
.SelectMany(i => _fileSystem.GetFiles(i.FullName, false))
.ToList();
- var videoListResolver = new VideoListResolver(GetNamingOptions(), new PatternsLogger());
+ var videoListResolver = new VideoListResolver(GetNamingOptions(), new NullLogger());
- var videos = videoListResolver.Resolve(fileSystemChildren.Select(i => new FileMetadata
- {
- Id = i.FullName,
- IsFolder = (i.Attributes & FileAttributes.Directory) == FileAttributes.Directory
-
- }).ToList());
+ var videos = videoListResolver.Resolve(fileSystemChildren);
var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files.First().Path, StringComparison.OrdinalIgnoreCase));
@@ -2636,7 +2698,7 @@ namespace MediaBrowser.Server.Implementations.Library
private void SetExtraTypeFromFilename(Video item)
{
- var resolver = new ExtraResolver(GetNamingOptions(), new PatternsLogger(), new RegexProvider());
+ var resolver = new ExtraResolver(GetNamingOptions(), new NullLogger(), new RegexProvider());
var result = resolver.GetExtraInfo(item.Path);
@@ -2798,10 +2860,7 @@ namespace MediaBrowser.Server.Implementations.Library
{
var path = Path.Combine(virtualFolderPath, collectionType + ".collection");
- using (File.Create(path))
- {
-
- }
+ _fileSystem.WriteAllBytes(path, new byte[] { });
}
CollectionFolder.SaveLibraryOptions(virtualFolderPath, options);
@@ -2838,21 +2897,20 @@ namespace MediaBrowser.Server.Implementations.Library
private bool ValidateNetworkPath(string path)
{
- if (Environment.OSVersion.Platform == PlatformID.Win32NT)
- {
- // We can't validate protocol-based paths, so just allow them
- if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) == -1)
- {
- return Directory.Exists(path);
- }
- }
+ //if (Environment.OSVersion.Platform == PlatformID.Win32NT)
+ //{
+ // // We can't validate protocol-based paths, so just allow them
+ // if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) == -1)
+ // {
+ // return _fileSystem.DirectoryExists(path);
+ // }
+ //}
// Without native support for unc, we cannot validate this when running under mono
return true;
}
private const string ShortcutFileExtension = ".mblink";
- private const string ShortcutFileSearch = "*" + ShortcutFileExtension;
public void AddMediaPath(string virtualFolderName, MediaPathInfo pathInfo)
{
AddMediaPathInternal(virtualFolderName, pathInfo, true);
@@ -2874,12 +2932,12 @@ namespace MediaBrowser.Server.Implementations.Library
if (!_fileSystem.DirectoryExists(path))
{
- throw new DirectoryNotFoundException("The path does not exist.");
+ throw new FileNotFoundException("The path does not exist.");
}
if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !ValidateNetworkPath(pathInfo.NetworkPath))
{
- throw new DirectoryNotFoundException("The network path does not exist.");
+ throw new FileNotFoundException("The network path does not exist.");
}
var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
@@ -2922,7 +2980,7 @@ namespace MediaBrowser.Server.Implementations.Library
if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !ValidateNetworkPath(pathInfo.NetworkPath))
{
- throw new DirectoryNotFoundException("The network path does not exist.");
+ throw new FileNotFoundException("The network path does not exist.");
}
var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
@@ -2984,7 +3042,7 @@ namespace MediaBrowser.Server.Implementations.Library
if (!_fileSystem.DirectoryExists(path))
{
- throw new DirectoryNotFoundException("The media folder does not exist");
+ throw new FileNotFoundException("The media folder does not exist");
}
_libraryMonitorFactory().Stop();
@@ -3055,10 +3113,12 @@ namespace MediaBrowser.Server.Implementations.Library
if (!_fileSystem.DirectoryExists(virtualFolderPath))
{
- throw new DirectoryNotFoundException(string.Format("The media collection {0} does not exist", virtualFolderName));
+ throw new FileNotFoundException(string.Format("The media collection {0} does not exist", virtualFolderName));
}
- var shortcut = Directory.EnumerateFiles(virtualFolderPath, ShortcutFileSearch, SearchOption.AllDirectories).FirstOrDefault(f => _fileSystem.ResolveShortcut(f).Equals(mediaPath, StringComparison.OrdinalIgnoreCase));
+ 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));
if (!string.IsNullOrEmpty(shortcut))
{
diff --git a/MediaBrowser.Server.Implementations/Library/LocalTrailerPostScanTask.cs b/Emby.Server.Implementations/Library/LocalTrailerPostScanTask.cs
index 78107b82d1..7424ed5e5e 100644
--- a/MediaBrowser.Server.Implementations/Library/LocalTrailerPostScanTask.cs
+++ b/Emby.Server.Implementations/Library/LocalTrailerPostScanTask.cs
@@ -9,7 +9,7 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
-namespace MediaBrowser.Server.Implementations.Library
+namespace Emby.Server.Implementations.Library
{
public class LocalTrailerPostScanTask : ILibraryPostScanTask
{
diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index e7bfe56f2e..93c406ebca 100644
--- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -14,10 +14,13 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Threading;
-namespace MediaBrowser.Server.Implementations.Library
+namespace Emby.Server.Implementations.Library
{
public class MediaSourceManager : IMediaSourceManager, IDisposable
{
@@ -30,8 +33,9 @@ namespace MediaBrowser.Server.Implementations.Library
private IMediaSourceProvider[] _providers;
private readonly ILogger _logger;
private readonly IUserDataManager _userDataManager;
+ private readonly ITimerFactory _timerFactory;
- public MediaSourceManager(IItemRepository itemRepo, IUserManager userManager, ILibraryManager libraryManager, ILogger logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem, IUserDataManager userDataManager)
+ public MediaSourceManager(IItemRepository itemRepo, IUserManager userManager, ILibraryManager libraryManager, ILogger logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem, IUserDataManager userDataManager, ITimerFactory timerFactory)
{
_itemRepo = itemRepo;
_userManager = userManager;
@@ -40,6 +44,7 @@ namespace MediaBrowser.Server.Implementations.Library
_jsonSerializer = jsonSerializer;
_fileSystem = fileSystem;
_userDataManager = userDataManager;
+ _timerFactory = timerFactory;
}
public void AddParts(IEnumerable<IMediaSourceProvider> providers)
@@ -549,14 +554,14 @@ namespace MediaBrowser.Server.Implementations.Library
return new Tuple<IMediaSourceProvider, string>(provider, keyId);
}
- private Timer _closeTimer;
+ private ITimer _closeTimer;
private readonly TimeSpan _openStreamMaxAge = TimeSpan.FromSeconds(180);
private void StartCloseTimer()
{
StopCloseTimer();
- _closeTimer = new Timer(CloseTimerCallback, null, _openStreamMaxAge, _openStreamMaxAge);
+ _closeTimer = _timerFactory.Create(CloseTimerCallback, null, _openStreamMaxAge, _openStreamMaxAge);
}
private void StopCloseTimer()
diff --git a/MediaBrowser.Server.Implementations/Library/MusicManager.cs b/Emby.Server.Implementations/Library/MusicManager.cs
index 3ff4348981..7669dd0bf1 100644
--- a/MediaBrowser.Server.Implementations/Library/MusicManager.cs
+++ b/Emby.Server.Implementations/Library/MusicManager.cs
@@ -6,7 +6,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
-namespace MediaBrowser.Server.Implementations.Library
+namespace Emby.Server.Implementations.Library
{
public class MusicManager : IMusicManager
{
diff --git a/MediaBrowser.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs
index 6c0e3237e8..28ed2f53ca 100644
--- a/MediaBrowser.Server.Implementations/Library/PathExtensions.cs
+++ b/Emby.Server.Implementations/Library/PathExtensions.cs
@@ -1,7 +1,7 @@
using System;
using System.Text.RegularExpressions;
-namespace MediaBrowser.Server.Implementations.Library
+namespace Emby.Server.Implementations.Library
{
public static class PathExtensions
{
diff --git a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs b/Emby.Server.Implementations/Library/ResolverHelper.cs
index 9f949db92a..1d3cacc1dc 100644
--- a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs
+++ b/Emby.Server.Implementations/Library/ResolverHelper.cs
@@ -5,9 +5,11 @@ using System;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
-namespace MediaBrowser.Server.Implementations.Library
+namespace Emby.Server.Implementations.Library
{
/// <summary>
/// Class ResolverHelper
@@ -110,7 +112,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// <summary>
/// The MB name regex
/// </summary>
- private static readonly Regex MbNameRegex = new Regex(@"(\[.*?\])", RegexOptions.Compiled);
+ private static readonly Regex MbNameRegex = new Regex(@"(\[.*?\])");
internal static string StripBrackets(string inputString)
{
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
index 039a17100a..2e3d81474a 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
@@ -2,13 +2,14 @@
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
using System;
+using MediaBrowser.Controller.Entities;
-namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
+namespace Emby.Server.Implementations.Library.Resolvers.Audio
{
/// <summary>
/// Class AudioResolver
/// </summary>
- public class AudioResolver : ItemResolver<Controller.Entities.Audio.Audio>
+ public class AudioResolver : ItemResolver<MediaBrowser.Controller.Entities.Audio.Audio>
{
private readonly ILibraryManager _libraryManager;
@@ -31,7 +32,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
/// </summary>
/// <param name="args">The args.</param>
/// <returns>Entities.Audio.Audio.</returns>
- protected override Controller.Entities.Audio.Audio Resolve(ItemResolveArgs args)
+ protected override MediaBrowser.Controller.Entities.Audio.Audio Resolve(ItemResolveArgs args)
{
// Return audio if the path is a file and has a matching extension
@@ -57,7 +58,12 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase) ||
isMixed)
{
- return new Controller.Entities.Audio.Audio();
+ return new MediaBrowser.Controller.Entities.Audio.Audio();
+ }
+
+ if (string.Equals(collectionType, CollectionType.Books, StringComparison.OrdinalIgnoreCase))
+ {
+ return new AudioBook();
}
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
index 7f35fc3ea5..871b2d46d6 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
@@ -5,15 +5,16 @@ using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Naming.Audio;
-using MediaBrowser.Server.Implementations.Logging;
using System;
using System.Collections.Generic;
using System.IO;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Configuration;
-namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
+namespace Emby.Server.Implementations.Library.Resolvers.Audio
{
/// <summary>
/// Class MusicAlbumResolver
@@ -112,7 +113,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
foreach (var fileSystemInfo in list)
{
- if ((fileSystemInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
+ if (fileSystemInfo.IsDirectory)
{
if (allowSubfolders)
{
@@ -162,7 +163,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
{
var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions(libraryOptions);
- var parser = new AlbumParser(namingOptions, new PatternsLogger());
+ var parser = new AlbumParser(namingOptions, new NullLogger());
var result = parser.ParseMultiPart(path);
return result.IsMultiPart;
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
index 686105ddb1..2971405b94 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
@@ -6,10 +6,12 @@ using MediaBrowser.Model.Logging;
using System;
using System.IO;
using System.Linq;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.IO;
-namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
+namespace Emby.Server.Implementations.Library.Resolvers.Audio
{
/// <summary>
/// Class MusicArtistResolver
@@ -85,7 +87,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
var albumResolver = new MusicAlbumResolver(_logger, _fileSystem, _libraryManager);
// If we contain an album assume we are an artist folder
- return args.FileSystemChildren.Where(i => (i.Attributes & FileAttributes.Directory) == FileAttributes.Directory).Any(i => albumResolver.IsMusicAlbum(i.FullName, directoryService, args.GetLibraryOptions())) ? new MusicArtist() : null;
+ return args.FileSystemChildren.Where(i => i.IsDirectory).Any(i => albumResolver.IsMusicAlbum(i.FullName, directoryService, args.GetLibraryOptions())) ? new MusicArtist() : null;
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
index d0042a9907..384ed8dacd 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
@@ -2,17 +2,19 @@
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Entities;
using MediaBrowser.Naming.Video;
-using MediaBrowser.Server.Implementations.Logging;
using System;
using System.IO;
+using System.Linq;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Logging;
-namespace MediaBrowser.Server.Implementations.Library.Resolvers
+namespace Emby.Server.Implementations.Library.Resolvers
{
/// <summary>
/// Resolves a Path into a Video or Video subclass
/// </summary>
/// <typeparam name="T"></typeparam>
- public abstract class BaseVideoResolver<T> : Controller.Resolvers.ItemResolver<T>
+ public abstract class BaseVideoResolver<T> : MediaBrowser.Controller.Resolvers.ItemResolver<T>
where T : Video, new()
{
protected readonly ILibraryManager LibraryManager;
@@ -45,7 +47,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions();
// If the path is a file check for a matching extensions
- var parser = new Naming.Video.VideoResolver(namingOptions, new PatternsLogger());
+ var parser = new MediaBrowser.Naming.Video.VideoResolver(namingOptions, new NullLogger());
if (args.IsDirectory)
{
@@ -57,9 +59,9 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
{
var filename = child.Name;
- if ((child.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
+ if (child.IsDirectory)
{
- if (IsDvdDirectory(filename))
+ if (IsDvdDirectory(child.FullName, filename, args.DirectoryService))
{
videoInfo = parser.ResolveDirectory(args.Path);
@@ -76,7 +78,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
};
break;
}
- if (IsBluRayDirectory(filename))
+ if (IsBluRayDirectory(child.FullName, filename, args.DirectoryService))
{
videoInfo = parser.ResolveDirectory(args.Path);
@@ -258,7 +260,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
{
var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions();
- var resolver = new Format3DParser(namingOptions, new PatternsLogger());
+ var resolver = new Format3DParser(namingOptions, new NullLogger());
var result = resolver.Parse(video.Path);
Set3DFormat(video, result.Is3D, result.Format3D);
@@ -267,11 +269,14 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
/// <summary>
/// Determines whether [is DVD directory] [the specified directory name].
/// </summary>
- /// <param name="directoryName">Name of the directory.</param>
- /// <returns><c>true</c> if [is DVD directory] [the specified directory name]; otherwise, <c>false</c>.</returns>
- protected bool IsDvdDirectory(string directoryName)
+ protected bool IsDvdDirectory(string fullPath, string directoryName, IDirectoryService directoryService)
{
- return string.Equals(directoryName, "video_ts", StringComparison.OrdinalIgnoreCase);
+ if (!string.Equals(directoryName, "video_ts", StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ return directoryService.GetFiles(fullPath).Any(i => string.Equals(i.Extension, ".vob", StringComparison.OrdinalIgnoreCase));
}
/// <summary>
@@ -287,11 +292,23 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
/// <summary>
/// Determines whether [is blu ray directory] [the specified directory name].
/// </summary>
- /// <param name="directoryName">Name of the directory.</param>
- /// <returns><c>true</c> if [is blu ray directory] [the specified directory name]; otherwise, <c>false</c>.</returns>
- protected bool IsBluRayDirectory(string directoryName)
+ protected bool IsBluRayDirectory(string fullPath, string directoryName, IDirectoryService directoryService)
{
- return string.Equals(directoryName, "bdmv", StringComparison.OrdinalIgnoreCase);
+ if (!string.Equals(directoryName, "bdmv", StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ return true;
+ //var blurayExtensions = new[]
+ //{
+ // ".mts",
+ // ".m2ts",
+ // ".bdmv",
+ // ".mpls"
+ //};
+
+ //return directoryService.GetFiles(fullPath).Any(i => blurayExtensions.Contains(i.Extension ?? string.Empty, StringComparer.OrdinalIgnoreCase));
}
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
new file mode 100644
index 0000000000..4852c3c6ae
--- /dev/null
+++ b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
@@ -0,0 +1,77 @@
+using System;
+using System.IO;
+using System.Linq;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Entities;
+
+namespace Emby.Server.Implementations.Library.Resolvers.Books
+{
+ /// <summary>
+ ///
+ /// </summary>
+ public class BookResolver : MediaBrowser.Controller.Resolvers.ItemResolver<Book>
+ {
+ private readonly string[] _validExtensions = {".pdf", ".epub", ".mobi", ".cbr", ".cbz"};
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="args"></param>
+ /// <returns></returns>
+ protected override Book Resolve(ItemResolveArgs args)
+ {
+ var collectionType = args.GetCollectionType();
+
+ // Only process items that are in a collection folder containing books
+ if (!string.Equals(collectionType, CollectionType.Books, StringComparison.OrdinalIgnoreCase))
+ return null;
+
+ if (args.IsDirectory)
+ {
+ return GetBook(args);
+ }
+
+ var extension = Path.GetExtension(args.Path);
+
+ if (extension != null && _validExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
+ {
+ // It's a book
+ return new Book
+ {
+ Path = args.Path,
+ IsInMixedFolder = true
+ };
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="args"></param>
+ /// <returns></returns>
+ private Book GetBook(ItemResolveArgs args)
+ {
+ var bookFiles = args.FileSystemChildren.Where(f =>
+ {
+ var fileExtension = Path.GetExtension(f.FullName) ??
+ string.Empty;
+
+ return _validExtensions.Contains(fileExtension,
+ StringComparer
+ .OrdinalIgnoreCase);
+ }).ToList();
+
+ // Don't return a Book if there is more (or less) than one document in the directory
+ if (bookFiles.Count != 1)
+ return null;
+
+ return new Book
+ {
+ Path = bookFiles[0].FullName
+ };
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs b/Emby.Server.Implementations/Library/Resolvers/FolderResolver.cs
index ff07c5282f..5e73baa5ce 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/FolderResolver.cs
@@ -2,7 +2,7 @@
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers;
-namespace MediaBrowser.Server.Implementations.Library.Resolvers
+namespace Emby.Server.Implementations.Library.Resolvers
{
/// <summary>
/// Class FolderResolver
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/ItemResolver.cs b/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs
index a03eda263a..b4a37be5f2 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/ItemResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs
@@ -2,7 +2,7 @@
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers;
-namespace MediaBrowser.Server.Implementations.Library.Resolvers
+namespace Emby.Server.Implementations.Library.Resolvers
{
/// <summary>
/// Class ItemResolver
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs
index e3447afc99..df441c5eda 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs
@@ -5,7 +5,7 @@ using MediaBrowser.Model.Entities;
using System;
using System.IO;
-namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
+namespace Emby.Server.Implementations.Library.Resolvers.Movies
{
/// <summary>
/// Class BoxSetResolver
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index 5f41995647..55a63b4e5d 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -1,5 +1,4 @@
-using Interfaces.IO;
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
@@ -8,14 +7,16 @@ using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Naming.Video;
-using MediaBrowser.Server.Implementations.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
-namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
+namespace Emby.Server.Implementations.Library.Resolvers.Movies
{
/// <summary>
/// Class MovieResolver
@@ -116,7 +117,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
// Loop through each child file/folder and see if we find a video
foreach (var child in fileSystemEntries)
{
- if ((child.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
+ if (child.IsDirectory)
{
leftOver.Add(child);
}
@@ -132,13 +133,8 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions();
- var resolver = new VideoListResolver(namingOptions, new PatternsLogger());
- var resolverResult = resolver.Resolve(files.Select(i => new FileMetadata
- {
- Id = i.FullName,
- IsFolder = i.IsDirectory
-
- }).ToList(), suppportMultiEditions).ToList();
+ var resolver = new VideoListResolver(namingOptions, new NullLogger());
+ var resolverResult = resolver.Resolve(files, suppportMultiEditions).ToList();
var result = new MultiItemResolverResult
{
@@ -368,9 +364,9 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
{
var filename = child.Name;
- if ((child.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
+ if (child.IsDirectory)
{
- if (IsDvdDirectory(filename))
+ if (IsDvdDirectory(child.FullName, filename, directoryService))
{
var movie = new T
{
@@ -380,7 +376,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
Set3DFormat(movie);
return movie;
}
- if (IsBluRayDirectory(filename))
+ if (IsBluRayDirectory(child.FullName, filename, directoryService))
{
var movie = new T
{
@@ -449,23 +445,22 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
.ToList();
var subfolders = subFileEntries
- .Where(e => (e.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
- .Select(d => d.Name)
+ .Where(e => e.IsDirectory)
.ToList();
- if (subfolders.Any(IsDvdDirectory))
+ if (subfolders.Any(s => IsDvdDirectory(s.FullName, s.Name, directoryService)))
{
videoTypes.Add(VideoType.Dvd);
return true;
}
- if (subfolders.Any(IsBluRayDirectory))
+ if (subfolders.Any(s => IsBluRayDirectory(s.FullName, s.Name, directoryService)))
{
videoTypes.Add(VideoType.BluRay);
return true;
}
var subFiles = subFileEntries
- .Where(e => (e.Attributes & FileAttributes.Directory) != FileAttributes.Directory)
+ .Where(e => !e.IsDirectory)
.Select(d => d.Name);
if (subFiles.Any(IsDvdFile))
@@ -490,7 +485,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
}
var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions();
- var resolver = new StackResolver(namingOptions, new PatternsLogger());
+ var resolver = new StackResolver(namingOptions, new NullLogger());
var result = resolver.ResolveDirectories(folderPaths);
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs
index e7f2397800..3d7ede879f 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs
@@ -7,7 +7,7 @@ using System;
using System.IO;
using System.Linq;
-namespace MediaBrowser.Server.Implementations.Library.Resolvers
+namespace Emby.Server.Implementations.Library.Resolvers
{
public class PhotoAlbumResolver : FolderResolver<PhotoAlbum>
{
@@ -41,7 +41,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
private bool HasPhotos(ItemResolveArgs args)
{
- return args.FileSystemChildren.Any(i => ((i.Attributes & FileAttributes.Directory) != FileAttributes.Directory) && PhotoResolver.IsImageFile(i.FullName, _imageProcessor));
+ return args.FileSystemChildren.Any(i => (!i.IsDirectory) && PhotoResolver.IsImageFile(i.FullName, _imageProcessor));
}
public override ResolverPriority Priority
diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
new file mode 100644
index 0000000000..df39e57add
--- /dev/null
+++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
@@ -0,0 +1,103 @@
+using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Entities;
+using System;
+using System.IO;
+using System.Linq;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.Configuration;
+
+namespace Emby.Server.Implementations.Library.Resolvers
+{
+ public class PhotoResolver : ItemResolver<Photo>
+ {
+ private readonly IImageProcessor _imageProcessor;
+ private readonly ILibraryManager _libraryManager;
+
+ public PhotoResolver(IImageProcessor imageProcessor, ILibraryManager libraryManager)
+ {
+ _imageProcessor = imageProcessor;
+ _libraryManager = libraryManager;
+ }
+
+ /// <summary>
+ /// Resolves the specified args.
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns>Trailer.</returns>
+ protected override Photo Resolve(ItemResolveArgs args)
+ {
+ if (!args.IsDirectory)
+ {
+ // Must be an image file within a photo collection
+ var collectionType = args.GetCollectionType();
+
+
+ if (string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase) ||
+ (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) && args.GetLibraryOptions().EnablePhotos))
+ {
+ if (IsImageFile(args.Path, _imageProcessor))
+ {
+ var filename = Path.GetFileNameWithoutExtension(args.Path);
+
+ // Make sure the image doesn't belong to a video file
+ if (args.DirectoryService.GetFiles(Path.GetDirectoryName(args.Path)).Any(i => IsOwnedByMedia(args.GetLibraryOptions(), i, filename)))
+ {
+ return null;
+ }
+
+ return new Photo
+ {
+ Path = args.Path
+ };
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private bool IsOwnedByMedia(LibraryOptions libraryOptions, FileSystemMetadata file, string imageFilename)
+ {
+ if (_libraryManager.IsVideoFile(file.FullName, libraryOptions) && imageFilename.StartsWith(Path.GetFileNameWithoutExtension(file.Name), StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ private static readonly string[] IgnoreFiles =
+ {
+ "folder",
+ "thumb",
+ "landscape",
+ "fanart",
+ "backdrop",
+ "poster",
+ "cover"
+ };
+
+ internal static bool IsImageFile(string path, IImageProcessor imageProcessor)
+ {
+ var filename = Path.GetFileNameWithoutExtension(path) ?? string.Empty;
+
+ if (IgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+
+ if (IgnoreFiles.Any(i => filename.IndexOf(i, StringComparison.OrdinalIgnoreCase) != -1))
+ {
+ return false;
+ }
+
+ return imageProcessor.SupportedInputFormats.Contains((Path.GetExtension(path) ?? string.Empty).TrimStart('.'), StringComparer.OrdinalIgnoreCase);
+ }
+
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/PlaylistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
index a95739f227..8c59cf20fd 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Controller.Playlists;
using System;
using System.IO;
-namespace MediaBrowser.Server.Implementations.Library.Resolvers
+namespace Emby.Server.Implementations.Library.Resolvers
{
public class PlaylistResolver : FolderResolver<Playlist>
{
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs b/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
index 7bb66ed89d..1bec1073d7 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
@@ -5,9 +5,11 @@ using MediaBrowser.Controller.Resolvers;
using System;
using System.IO;
using System.Linq;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
-namespace MediaBrowser.Server.Implementations.Library.Resolvers
+namespace Emby.Server.Implementations.Library.Resolvers
{
class SpecialFolderResolver : FolderResolver<Folder>
{
@@ -67,7 +69,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
try
{
- return (i.Attributes & FileAttributes.Directory) != FileAttributes.Directory &&
+ return !i.IsDirectory &&
string.Equals(".collection", i.Extension, StringComparison.OrdinalIgnoreCase);
}
catch (IOException)
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
index 6edc4a009e..2a4cc49b76 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
@@ -4,7 +4,7 @@ using MediaBrowser.Controller.Library;
using System.Linq;
using MediaBrowser.Model.Entities;
-namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
+namespace Emby.Server.Implementations.Library.Resolvers.TV
{
/// <summary>
/// Class EpisodeResolver
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
index fc49297482..c065feda10 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
@@ -4,7 +4,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Naming.Common;
using MediaBrowser.Naming.TV;
-namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
+namespace Emby.Server.Implementations.Library.Resolvers.TV
{
/// <summary>
/// Class SeasonResolver
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
index 3217cd67b9..e5cad9f91b 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
@@ -6,16 +6,17 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Naming.Common;
using MediaBrowser.Naming.TV;
-using MediaBrowser.Server.Implementations.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Configuration;
-namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
+namespace Emby.Server.Implementations.Library.Resolvers.TV
{
/// <summary>
/// Class SeriesResolver
@@ -128,8 +129,6 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
{
foreach (var child in fileSystemChildren)
{
- var attributes = child.Attributes;
-
//if ((attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
//{
// //logger.Debug("Igoring series file or folder marked hidden: {0}", child.FullName);
@@ -143,7 +142,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
// continue;
//}
- if ((attributes & FileAttributes.Directory) == FileAttributes.Directory)
+ if (child.IsDirectory)
{
if (IsSeasonFolder(child.FullName, isTvContentType, libraryManager))
{
@@ -171,7 +170,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
.ToList();
}
- var episodeResolver = new Naming.TV.EpisodeResolver(namingOptions, new PatternsLogger());
+ var episodeResolver = new MediaBrowser.Naming.TV.EpisodeResolver(namingOptions, new NullLogger());
var episodeInfo = episodeResolver.Resolve(fullName, false, false);
if (episodeInfo != null && episodeInfo.EpisodeNumber.HasValue)
{
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/VideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs
index c7f21cef16..b5e1bf5f74 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/VideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs
@@ -2,7 +2,7 @@
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers;
-namespace MediaBrowser.Server.Implementations.Library.Resolvers
+namespace Emby.Server.Implementations.Library.Resolvers
{
/// <summary>
/// Resolves a Path into a Video
diff --git a/MediaBrowser.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs
index 40cca7baba..afdf65c063 100644
--- a/MediaBrowser.Server.Implementations/Library/SearchEngine.cs
+++ b/Emby.Server.Implementations/Library/SearchEngine.cs
@@ -1,7 +1,5 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying;
@@ -10,12 +8,11 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using MediaBrowser.Controller.Extensions;
-namespace MediaBrowser.Server.Implementations.Library
+namespace Emby.Server.Implementations.Library
{
/// <summary>
- /// Class LuceneSearchEngine
- /// http://www.codeproject.com/Articles/320219/Lucene-Net-ultra-fast-search-for-MVC-or-WebForms
/// </summary>
public class SearchEngine : ISearchEngine
{
diff --git a/MediaBrowser.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs
index 9ee65a57c6..5a14edf135 100644
--- a/MediaBrowser.Server.Implementations/Library/UserDataManager.cs
+++ b/Emby.Server.Implementations/Library/UserDataManager.cs
@@ -12,8 +12,9 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Querying;
-namespace MediaBrowser.Server.Implementations.Library
+namespace Emby.Server.Implementations.Library
{
/// <summary>
/// Class UserDataManager
@@ -186,16 +187,16 @@ namespace MediaBrowser.Server.Implementations.Library
var userData = GetUserData(user.Id, item);
var dto = GetUserItemDataDto(userData);
- await item.FillUserDataDtoValues(dto, userData, null, user).ConfigureAwait(false);
+ await item.FillUserDataDtoValues(dto, userData, null, user, new List<ItemFields>()).ConfigureAwait(false);
return dto;
}
- public async Task<UserItemDataDto> GetUserDataDto(IHasUserData item, BaseItemDto itemDto, User user)
+ public async Task<UserItemDataDto> GetUserDataDto(IHasUserData item, BaseItemDto itemDto, User user, List<ItemFields> fields)
{
var userData = GetUserData(user.Id, item);
var dto = GetUserItemDataDto(userData);
- await item.FillUserDataDtoValues(dto, userData, itemDto, user).ConfigureAwait(false);
+ await item.FillUserDataDtoValues(dto, userData, itemDto, user, fields).ConfigureAwait(false);
return dto;
}
@@ -274,7 +275,7 @@ namespace MediaBrowser.Server.Implementations.Library
positionTicks = 0;
data.Played = false;
}
- if (item is Audio)
+ if (!item.SupportsPositionTicksResume)
{
positionTicks = 0;
}
diff --git a/MediaBrowser.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs
index 6456d7f81b..2a5706b3bf 100644
--- a/MediaBrowser.Server.Implementations/Library/UserManager.cs
+++ b/Emby.Server.Implementations/Library/UserManager.cs
@@ -23,13 +23,13 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
-using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.Cryptography;
+using MediaBrowser.Model.IO;
-namespace MediaBrowser.Server.Implementations.Library
+namespace Emby.Server.Implementations.Library
{
/// <summary>
/// Class UserManager
@@ -70,8 +70,10 @@ namespace MediaBrowser.Server.Implementations.Library
private readonly Func<IConnectManager> _connectFactory;
private readonly IServerApplicationHost _appHost;
private readonly IFileSystem _fileSystem;
+ private readonly ICryptoProvider _cryptographyProvider;
+ private readonly string _defaultUserName;
- 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)
+ 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, string defaultUserName)
{
_logger = logger;
UserRepository = userRepository;
@@ -83,6 +85,8 @@ namespace MediaBrowser.Server.Implementations.Library
_appHost = appHost;
_jsonSerializer = jsonSerializer;
_fileSystem = fileSystem;
+ _cryptographyProvider = cryptographyProvider;
+ _defaultUserName = defaultUserName;
ConfigurationManager = configurationManager;
Users = new List<User>();
@@ -186,7 +190,14 @@ namespace MediaBrowser.Server.Implementations.Library
public bool IsValidUsername(string username)
{
// Usernames can contain letters (a-z), numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)
- return username.All(IsValidUsernameCharacter);
+ foreach (var currentChar in username)
+ {
+ if (!IsValidUsernameCharacter(currentChar))
+ {
+ return false;
+ }
+ }
+ return true;
}
private bool IsValidUsernameCharacter(char i)
@@ -271,8 +282,8 @@ namespace MediaBrowser.Server.Implementations.Library
{
user.Policy.InvalidLoginAttemptCount = newValue;
- var maxCount = user.Policy.IsAdministrator ?
- 3 :
+ var maxCount = user.Policy.IsAdministrator ?
+ 3 :
5;
var fireLockout = false;
@@ -321,13 +332,9 @@ namespace MediaBrowser.Server.Implementations.Library
/// </summary>
/// <param name="str">The STR.</param>
/// <returns>System.String.</returns>
- private static string GetSha1String(string str)
+ private string GetSha1String(string str)
{
- using (var provider = SHA1.Create())
- {
- var hash = provider.ComputeHash(Encoding.UTF8.GetBytes(str));
- return BitConverter.ToString(hash).Replace("-", string.Empty);
- }
+ return BitConverter.ToString(_cryptographyProvider.ComputeSHA1(Encoding.UTF8.GetBytes(str))).Replace("-", string.Empty);
}
/// <summary>
@@ -341,7 +348,7 @@ namespace MediaBrowser.Server.Implementations.Library
// There always has to be at least one user.
if (users.Count == 0)
{
- var name = MakeValidUsername(Environment.UserName);
+ var name = MakeValidUsername(_defaultUserName);
var user = InstantiateNewUser(name);
@@ -739,9 +746,12 @@ namespace MediaBrowser.Server.Implementations.Library
text.AppendLine(string.Empty);
text.AppendLine(pin);
text.AppendLine(string.Empty);
- text.AppendLine("The pin code will expire at " + expiration.ToLocalTime().ToShortDateString() + " " + expiration.ToLocalTime().ToShortTimeString());
- _fileSystem.WriteAllText(path, text.ToString(), Encoding.UTF8);
+ var localExpirationTime = expiration.ToLocalTime();
+ // Tuesday, 22 August 2006 06:30 AM
+ text.AppendLine("The pin code will expire at " + localExpirationTime.ToString("f1", CultureInfo.CurrentCulture));
+
+ _fileSystem.WriteAllText(path, text.ToString(), Encoding.UTF8);
var result = new PasswordPinCreationResult
{
@@ -873,11 +883,11 @@ namespace MediaBrowser.Server.Implementations.Library
return (UserPolicy)_xmlSerializer.DeserializeFromFile(typeof(UserPolicy), path);
}
}
- catch (DirectoryNotFoundException)
+ catch (FileNotFoundException)
{
return GetDefaultPolicy(user);
}
- catch (FileNotFoundException)
+ catch (IOException)
{
return GetDefaultPolicy(user);
}
@@ -915,7 +925,7 @@ namespace MediaBrowser.Server.Implementations.Library
var path = GetPolifyFilePath(user);
- _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
+ _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
lock (_policySyncLock)
{
@@ -968,11 +978,11 @@ namespace MediaBrowser.Server.Implementations.Library
return (UserConfiguration)_xmlSerializer.DeserializeFromFile(typeof(UserConfiguration), path);
}
}
- catch (DirectoryNotFoundException)
+ catch (FileNotFoundException)
{
return new UserConfiguration();
}
- catch (FileNotFoundException)
+ catch (IOException)
{
return new UserConfiguration();
}
@@ -1002,7 +1012,7 @@ namespace MediaBrowser.Server.Implementations.Library
config = _jsonSerializer.DeserializeFromString<UserConfiguration>(json);
}
- _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
+ _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
lock (_configSyncLock)
{
diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs
index 2cbee7c97f..f7cc8bb73f 100644
--- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
+++ b/Emby.Server.Implementations/Library/UserViewManager.cs
@@ -3,7 +3,6 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Library;
@@ -14,8 +13,9 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Model.Globalization;
-namespace MediaBrowser.Server.Implementations.Library
+namespace Emby.Server.Implementations.Library
{
public class UserViewManager : IUserViewManager
{
@@ -245,20 +245,26 @@ namespace MediaBrowser.Server.Implementations.Library
var includeItemTypes = request.IncludeItemTypes;
var limit = request.Limit ?? 10;
- var parentIds = string.IsNullOrEmpty(parentId)
- ? new string[] { }
- : new[] { parentId };
+ var parents = new List<BaseItem>();
- if (parentIds.Length == 0)
+ if (!string.IsNullOrWhiteSpace(parentId))
{
- parentIds = user.RootFolder.GetChildren(user, true)
- .OfType<Folder>()
- .Select(i => i.Id.ToString("N"))
- .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i))
- .ToArray();
+ var parent = _libraryManager.GetItemById(parentId) as Folder;
+ if (parent != null)
+ {
+ parents.Add(parent);
+ }
+ }
+
+ if (parents.Count == 0)
+ {
+ parents = user.RootFolder.GetChildren(user, true)
+ .Where(i => i is Folder)
+ .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N")))
+ .ToList();
}
- if (parentIds.Length == 0)
+ if (parents.Count == 0)
{
return new List<BaseItem>();
}
@@ -283,10 +289,10 @@ namespace MediaBrowser.Server.Implementations.Library
ExcludeItemTypes = excludeItemTypes,
ExcludeLocationTypes = new[] { LocationType.Virtual },
Limit = limit * 5,
- SourceTypes = parentIds.Length == 0 ? new[] { SourceType.Library } : new SourceType[] { },
+ SourceTypes = parents.Count == 0 ? new[] { SourceType.Library } : new SourceType[] { },
IsPlayed = request.IsPlayed
- }, parentIds);
+ }, parents);
}
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs
index 91b035a350..4d718dbee8 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs
+++ b/Emby.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs
@@ -5,7 +5,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Persistence;
-namespace MediaBrowser.Server.Implementations.Library.Validators
+namespace Emby.Server.Implementations.Library.Validators
{
/// <summary>
/// Class ArtistsPostScanTask
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/ArtistsValidator.cs b/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs
index 3dcdbeae9d..643c5970e1 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/ArtistsValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs
@@ -9,7 +9,7 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Persistence;
-namespace MediaBrowser.Server.Implementations.Library.Validators
+namespace Emby.Server.Implementations.Library.Validators
{
/// <summary>
/// Class ArtistsValidator
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/GameGenresPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/GameGenresPostScanTask.cs
index f3891180e2..ee6c4461c2 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/GameGenresPostScanTask.cs
+++ b/Emby.Server.Implementations/Library/Validators/GameGenresPostScanTask.cs
@@ -5,7 +5,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Persistence;
-namespace MediaBrowser.Server.Implementations.Library.Validators
+namespace Emby.Server.Implementations.Library.Validators
{
/// <summary>
/// Class GameGenresPostScanTask
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs b/Emby.Server.Implementations/Library/Validators/GameGenresValidator.cs
index b06c0b3b9b..b1820bb917 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/GameGenresValidator.cs
@@ -7,7 +7,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Persistence;
-namespace MediaBrowser.Server.Implementations.Library.Validators
+namespace Emby.Server.Implementations.Library.Validators
{
class GameGenresValidator
{
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/GenresPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/GenresPostScanTask.cs
index ed2429769c..be46decfb1 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/GenresPostScanTask.cs
+++ b/Emby.Server.Implementations/Library/Validators/GenresPostScanTask.cs
@@ -5,7 +5,7 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Logging;
-namespace MediaBrowser.Server.Implementations.Library.Validators
+namespace Emby.Server.Implementations.Library.Validators
{
public class GenresPostScanTask : ILibraryPostScanTask
{
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs b/Emby.Server.Implementations/Library/Validators/GenresValidator.cs
index f35bb51363..d8956f78a1 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/GenresValidator.cs
@@ -8,7 +8,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Persistence;
-namespace MediaBrowser.Server.Implementations.Library.Validators
+namespace Emby.Server.Implementations.Library.Validators
{
class GenresValidator
{
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs
index 777532ff87..cd40215486 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs
+++ b/Emby.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs
@@ -5,7 +5,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Persistence;
-namespace MediaBrowser.Server.Implementations.Library.Validators
+namespace Emby.Server.Implementations.Library.Validators
{
/// <summary>
/// Class MusicGenresPostScanTask
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs b/Emby.Server.Implementations/Library/Validators/MusicGenresValidator.cs
index 2be99f106e..983c881b75 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/MusicGenresValidator.cs
@@ -8,7 +8,7 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Persistence;
-namespace MediaBrowser.Server.Implementations.Library.Validators
+namespace Emby.Server.Implementations.Library.Validators
{
class MusicGenresValidator
{
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
index 93b9c0da1e..7ebfd71c09 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
@@ -11,9 +11,11 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
-namespace MediaBrowser.Server.Implementations.Library.Validators
+namespace Emby.Server.Implementations.Library.Validators
{
/// <summary>
/// Class PeopleValidator
@@ -45,36 +47,6 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
_fileSystem = fileSystem;
}
- private bool DownloadMetadata(PersonInfo i, PeopleMetadataOptions options)
- {
- if (i.IsType(PersonType.Actor))
- {
- return options.DownloadActorMetadata;
- }
- if (i.IsType(PersonType.Director))
- {
- return options.DownloadDirectorMetadata;
- }
- if (i.IsType(PersonType.Composer))
- {
- return options.DownloadComposerMetadata;
- }
- if (i.IsType(PersonType.Writer))
- {
- return options.DownloadWriterMetadata;
- }
- if (i.IsType(PersonType.Producer))
- {
- return options.DownloadProducerMetadata;
- }
- if (i.IsType(PersonType.GuestStar))
- {
- return options.DownloadGuestStarMetadata;
- }
-
- return options.DownloadOtherPeopleMetadata;
- }
-
/// <summary>
/// Validates the people.
/// </summary>
@@ -87,28 +59,13 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
innerProgress.RegisterAction(pct => progress.Report(pct * .15));
- var peopleOptions = _config.Configuration.PeopleMetadataOptions;
-
var people = _libraryManager.GetPeople(new InternalPeopleQuery());
var dict = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
foreach (var person in people)
{
- var isMetadataEnabled = DownloadMetadata(person, peopleOptions);
-
- bool currentValue;
- if (dict.TryGetValue(person.Name, out currentValue))
- {
- if (!currentValue && isMetadataEnabled)
- {
- dict[person.Name] = true;
- }
- }
- else
- {
- dict[person.Name] = isMetadataEnabled;
- }
+ dict[person.Name] = true;
}
var numComplete = 0;
@@ -125,22 +82,10 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
{
var item = _libraryManager.GetPerson(person.Key);
- var hasMetdata = !string.IsNullOrWhiteSpace(item.Overview);
- var performFullRefresh = !hasMetdata && (DateTime.UtcNow - item.DateLastRefreshed).TotalDays >= 30;
-
- var defaultMetadataRefreshMode = performFullRefresh
- ? MetadataRefreshMode.FullRefresh
- : MetadataRefreshMode.Default;
-
- var imageRefreshMode = performFullRefresh
- ? ImageRefreshMode.FullRefresh
- : ImageRefreshMode.Default;
-
var options = new MetadataRefreshOptions(_fileSystem)
{
- MetadataRefreshMode = person.Value ? defaultMetadataRefreshMode : MetadataRefreshMode.ValidationOnly,
- ImageRefreshMode = person.Value ? imageRefreshMode : ImageRefreshMode.ValidationOnly,
- ForceSave = performFullRefresh
+ ImageRefreshMode = ImageRefreshMode.ValidationOnly,
+ MetadataRefreshMode = MetadataRefreshMode.ValidationOnly
};
await item.RefreshMetadata(options, cancellationToken).ConfigureAwait(false);
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/StudiosPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/StudiosPostScanTask.cs
index 77c6d51465..d23efb6d3b 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/StudiosPostScanTask.cs
+++ b/Emby.Server.Implementations/Library/Validators/StudiosPostScanTask.cs
@@ -5,7 +5,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Persistence;
-namespace MediaBrowser.Server.Implementations.Library.Validators
+namespace Emby.Server.Implementations.Library.Validators
{
/// <summary>
/// Class MusicGenresPostScanTask
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs b/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
index a19b8158a0..6faab7bb9d 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Persistence;
-namespace MediaBrowser.Server.Implementations.Library.Validators
+namespace Emby.Server.Implementations.Library.Validators
{
class StudiosValidator
{
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/YearsPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/YearsPostScanTask.cs
index 164b142234..ae43c77f0a 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/YearsPostScanTask.cs
+++ b/Emby.Server.Implementations/Library/Validators/YearsPostScanTask.cs
@@ -4,7 +4,7 @@ using System;
using System.Threading;
using System.Threading.Tasks;
-namespace MediaBrowser.Server.Implementations.Library.Validators
+namespace Emby.Server.Implementations.Library.Validators
{
public class YearsPostScanTask : ILibraryPostScanTask
{
diff --git a/MediaBrowser.Server.Implementations/LiveTv/ChannelImageProvider.cs b/Emby.Server.Implementations/LiveTv/ChannelImageProvider.cs
index 23560b1aa1..95cefd9990 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/ChannelImageProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/ChannelImageProvider.cs
@@ -11,7 +11,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-namespace MediaBrowser.Server.Implementations.LiveTv
+namespace Emby.Server.Implementations.LiveTv
{
public class ChannelImageProvider : IDynamicImageProvider, IHasItemChangeMonitor
{
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
index 0f8c15e719..6d527c1cfb 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
@@ -2,13 +2,14 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Logging;
-namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
+namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
public class DirectRecorder : IRecorder
{
@@ -30,18 +31,17 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
public async Task Record(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
{
- var httpRequestOptions = new HttpRequestOptions()
+ var httpRequestOptions = new HttpRequestOptions
{
- Url = mediaSource.Path
+ Url = mediaSource.Path,
+ BufferContent = false
};
- httpRequestOptions.BufferContent = false;
-
using (var response = await _httpClient.SendAsync(httpRequestOptions, "GET").ConfigureAwait(false))
{
_logger.Info("Opened recording stream from tuner provider");
- using (var output = _fileSystem.GetFileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.Read))
+ using (var output = _fileSystem.GetFileStream(targetFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
{
onStarted();
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index 214bb87169..84a255c7a9 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -15,7 +15,6 @@ using MediaBrowser.Model.Events;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Server.Implementations.FileOrganization;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -26,17 +25,22 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Diagnostics;
using MediaBrowser.Model.FileOrganization;
-using Microsoft.Win32;
+using MediaBrowser.Model.System;
+using MediaBrowser.Model.Threading;
+using MediaBrowser.Model.Extensions;
-namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
+namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
public class EmbyTV : ILiveTvService, ISupportsDirectStreamProvider, ISupportsNewTimerIds, IDisposable
{
@@ -57,6 +61,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
private readonly IProviderManager _providerManager;
private readonly IFileOrganizationService _organizationService;
private readonly IMediaEncoder _mediaEncoder;
+ private readonly IProcessFactory _processFactory;
+ private readonly ISystemEvents _systemEvents;
public static EmbyTV Current;
@@ -66,7 +72,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
private readonly ConcurrentDictionary<string, ActiveRecordingInfo> _activeRecordings =
new ConcurrentDictionary<string, ActiveRecordingInfo>(StringComparer.OrdinalIgnoreCase);
- public EmbyTV(IServerApplicationHost appHost, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IServerConfigurationManager config, ILiveTvManager liveTvManager, IFileSystem fileSystem, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager, IFileOrganizationService organizationService, IMediaEncoder mediaEncoder)
+ public EmbyTV(IServerApplicationHost appHost, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IServerConfigurationManager config, ILiveTvManager liveTvManager, IFileSystem fileSystem, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager, IFileOrganizationService organizationService, IMediaEncoder mediaEncoder, ITimerFactory timerFactory, IProcessFactory processFactory, ISystemEvents systemEvents)
{
Current = this;
@@ -80,11 +86,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
_providerManager = providerManager;
_organizationService = organizationService;
_mediaEncoder = mediaEncoder;
+ _processFactory = processFactory;
+ _systemEvents = systemEvents;
_liveTvManager = (LiveTvManager)liveTvManager;
_jsonSerializer = jsonSerializer;
_seriesTimerProvider = new SeriesTimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers"));
- _timerProvider = new TimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "timers"), _logger);
+ _timerProvider = new TimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "timers"), _logger, timerFactory);
_timerProvider.TimerFired += _timerProvider_TimerFired;
_config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
@@ -102,10 +110,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
{
_timerProvider.RestartTimers();
- SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
+ _systemEvents.Resume += _systemEvents_Resume;
CreateRecordingFolders();
}
+ private void _systemEvents_Resume(object sender, EventArgs e)
+ {
+ _timerProvider.RestartTimers();
+ }
+
private void OnRecordingFoldersChanged()
{
CreateRecordingFolders();
@@ -229,16 +242,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
}
}
- void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
- {
- _logger.Info("Power mode changed to {0}", e.Mode);
-
- if (e.Mode == PowerModes.Resume)
- {
- _timerProvider.RestartTimers();
- }
- }
-
public string Name
{
get { return "Emby"; }
@@ -325,15 +328,35 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
}
await UpdateTimersForSeriesTimer(epgData, timer, true).ConfigureAwait(false);
}
+ }
+ public async Task RefreshTimers(CancellationToken cancellationToken, IProgress<double> progress)
+ {
var timers = await GetTimersAsync(cancellationToken).ConfigureAwait(false);
- foreach (var timer in timers.ToList())
+ foreach (var timer in timers)
{
if (DateTime.UtcNow > timer.EndDate && !_activeRecordings.ContainsKey(timer.Id))
{
OnTimerOutOfDate(timer);
+ continue;
+ }
+
+ if (string.IsNullOrWhiteSpace(timer.ProgramId) || string.IsNullOrWhiteSpace(timer.ChannelId))
+ {
+ continue;
}
+
+ var epg = GetEpgDataForChannel(timer.ChannelId);
+ var program = epg.FirstOrDefault(i => string.Equals(i.Id, timer.ProgramId, StringComparison.OrdinalIgnoreCase));
+ if (program == null)
+ {
+ OnTimerOutOfDate(timer);
+ continue;
+ }
+
+ RecordingHelper.CopyProgramInfoToTimerInfo(program, timer);
+ _timerProvider.Update(timer);
}
}
@@ -642,6 +665,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
existingTimer.SeasonNumber = updatedTimer.SeasonNumber;
existingTimer.ShortOverview = updatedTimer.ShortOverview;
existingTimer.StartDate = updatedTimer.StartDate;
+ existingTimer.ShowId = updatedTimer.ShowId;
}
public Task<ImageStream> GetChannelImageAsync(string channelId, CancellationToken cancellationToken)
@@ -939,21 +963,20 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
await _liveStreamsSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
- var result = _liveStreams.FirstOrDefault(i => string.Equals(i.OriginalStreamId, streamId, StringComparison.OrdinalIgnoreCase));
-
- if (result != null && result.EnableStreamSharing)
+ try
{
- var openedMediaSource = CloneMediaSource(result.OpenedMediaSource, result.EnableStreamSharing);
- result.SharedStreamIds.Add(openedMediaSource.Id);
- _liveStreamsSemaphore.Release();
+ var result = _liveStreams.FirstOrDefault(i => string.Equals(i.OriginalStreamId, streamId, StringComparison.OrdinalIgnoreCase));
- _logger.Info("Live stream {0} consumer count is now {1}", streamId, result.ConsumerCount);
+ if (result != null && result.EnableStreamSharing)
+ {
+ var openedMediaSource = CloneMediaSource(result.OpenedMediaSource, result.EnableStreamSharing);
+ result.SharedStreamIds.Add(openedMediaSource.Id);
- return new Tuple<LiveStream, MediaSourceInfo, ITunerHost>(result, openedMediaSource, result.TunerHost);
- }
+ _logger.Info("Live stream {0} consumer count is now {1}", streamId, result.ConsumerCount);
+
+ return new Tuple<LiveStream, MediaSourceInfo, ITunerHost>(result, openedMediaSource, result.TunerHost);
+ }
- try
- {
foreach (var hostInstance in _liveTvManager.TunerHosts)
{
try
@@ -986,11 +1009,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
_liveStreamsSemaphore.Release();
}
- throw new ApplicationException("Tuner not found.");
+ throw new Exception("Tuner not found.");
}
public async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken)
{
+ if (string.IsNullOrWhiteSpace(channelId))
+ {
+ throw new ArgumentNullException("channelId");
+ }
+
foreach (var hostInstance in _liveTvManager.TunerHosts)
{
try
@@ -1029,7 +1057,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
IsInfiniteStream = true,
RequiresOpening = false,
RequiresClosing = false,
- Protocol = Model.MediaInfo.MediaProtocol.Http,
+ Protocol = MediaBrowser.Model.MediaInfo.MediaProtocol.Http,
BufferMs = 0
};
@@ -1269,6 +1297,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
try
{
+ var recorder = await GetRecorder().ConfigureAwait(false);
+
var allMediaSources = await GetChannelStreamMediaSources(timer.ChannelId, CancellationToken.None).ConfigureAwait(false);
var liveStreamInfo = await GetChannelStreamInternal(timer.ChannelId, allMediaSources[0].Id, CancellationToken.None)
@@ -1280,8 +1310,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
// HDHR doesn't seem to release the tuner right away after first probing with ffmpeg
//await Task.Delay(3000, cancellationToken).ConfigureAwait(false);
- var recorder = await GetRecorder().ConfigureAwait(false);
-
recordPath = recorder.GetOutputPath(mediaStreamInfo, recordPath);
recordPath = EnsureFileUnique(recordPath, timer.Id);
@@ -1303,7 +1331,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
timer.Status = RecordingStatus.InProgress;
_timerProvider.AddOrUpdate(timer, false);
- SaveNfo(timer, recordPath, seriesPath);
+ SaveRecordingMetadata(timer, recordPath, seriesPath);
EnforceKeepUpTo(timer);
};
@@ -1351,7 +1379,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
timer.StartDate = DateTime.UtcNow.AddSeconds(retryIntervalSeconds);
_timerProvider.AddOrUpdate(timer);
}
- else if (File.Exists(recordPath))
+ else if (_fileSystem.FileExists(recordPath))
{
timer.RecordingPath = recordPath;
timer.Status = RecordingStatus.Completed;
@@ -1407,7 +1435,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
.Where(i => i.Status == RecordingStatus.Completed && !string.IsNullOrWhiteSpace(i.RecordingPath))
.Where(i => string.Equals(i.SeriesTimerId, seriesTimerId, StringComparison.OrdinalIgnoreCase))
.OrderByDescending(i => i.EndDate)
- .Where(i => File.Exists(i.RecordingPath))
+ .Where(i => _fileSystem.FileExists(i.RecordingPath))
.Skip(seriesTimer.KeepUpTo - 1)
.ToList();
@@ -1455,13 +1483,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
{
try
{
- File.Delete(timer.RecordingPath);
+ _fileSystem.DeleteFile(timer.RecordingPath);
}
- catch (DirectoryNotFoundException)
- {
-
- }
- catch (FileNotFoundException)
+ catch (IOException)
{
}
@@ -1517,7 +1541,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
if (regInfo.IsValid)
{
- return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, config, _httpClient);
+ return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, config, _httpClient, _processFactory);
}
}
@@ -1526,42 +1550,251 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
private async void OnSuccessfulRecording(TimerInfo timer, string path)
{
- if (timer.IsProgramSeries && GetConfiguration().EnableAutoOrganize)
+ //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);
+ }
+
+ private void PostProcessRecording(TimerInfo timer, string path)
+ {
+ var options = GetConfiguration();
+ if (string.IsNullOrWhiteSpace(options.RecordingPostProcessor))
{
- try
+ return;
+ }
+
+ try
+ {
+ var process = _processFactory.Create(new ProcessOptions
{
- // 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);
+ Arguments = GetPostProcessArguments(path, options.RecordingPostProcessorArguments),
+ CreateNoWindow = true,
+ EnableRaisingEvents = true,
+ ErrorDialog = false,
+ FileName = options.RecordingPostProcessor,
+ IsHidden = true,
+ UseShellExecute = false
+ });
+
+ _logger.Info("Running recording post processor {0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
+
+ process.Exited += Process_Exited;
+ process.Start();
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error running recording post processor", ex);
+ }
+ }
+
+ private string GetPostProcessArguments(string path, string arguments)
+ {
+ return arguments.Replace("{path}", path, StringComparison.OrdinalIgnoreCase);
+ }
+
+ private void Process_Exited(object sender, EventArgs e)
+ {
+ var process = (IProcess)sender;
+ try
+ {
+ _logger.Info("Recording post-processing script completed with exit code {0}", process.ExitCode);
+ }
+ catch
+ {
+
+ }
+
+ process.Dispose();
+ }
+
+ private async Task SaveRecordingImage(string recordingPath, LiveTvProgram program, ItemImageInfo image)
+ {
+ if (!image.IsLocalFile)
+ {
+ image = await _libraryManager.ConvertImageToLocal(program, image, 0).ConfigureAwait(false);
+ }
- var organize = new EpisodeFileOrganizer(_organizationService, _config, _fileSystem, _logger, _libraryManager, _libraryMonitor, _providerManager);
+ string imageSaveFilenameWithoutExtension = null;
- var result = await organize.OrganizeEpisodeFile(path, _config.GetAutoOrganizeOptions(), false, CancellationToken.None).ConfigureAwait(false);
+ switch (image.Type)
+ {
+ case ImageType.Primary:
- if (result.Status == FileSortingStatus.Success)
+ if (program.IsSeries)
{
- return;
+ imageSaveFilenameWithoutExtension = Path.GetFileNameWithoutExtension(recordingPath) + "-thumb";
}
+ else
+ {
+ imageSaveFilenameWithoutExtension = "poster";
+ }
+
+ break;
+ case ImageType.Logo:
+ imageSaveFilenameWithoutExtension = "logo";
+ break;
+ case ImageType.Thumb:
+ imageSaveFilenameWithoutExtension = "landscape";
+ break;
+ case ImageType.Backdrop:
+ imageSaveFilenameWithoutExtension = "fanart";
+ break;
+ default:
+ break;
+ }
+
+ if (string.IsNullOrWhiteSpace(imageSaveFilenameWithoutExtension))
+ {
+ return;
+ }
+
+ var imageSavePath = Path.Combine(Path.GetDirectoryName(recordingPath), imageSaveFilenameWithoutExtension);
+
+ // preserve original image extension
+ imageSavePath = Path.ChangeExtension(imageSavePath, Path.GetExtension(image.Path));
+
+ _fileSystem.CopyFile(image.Path, imageSavePath, true);
+ }
+
+ private async Task SaveRecordingImages(string recordingPath, LiveTvProgram program)
+ {
+ var image = program.GetImageInfo(ImageType.Primary, 0);
+
+ if (image != null && program.IsMovie)
+ {
+ try
+ {
+ await SaveRecordingImage(recordingPath, program, image).ConfigureAwait(false);
}
catch (Exception ex)
{
- _logger.ErrorException("Error processing new recording", ex);
+ _logger.ErrorException("Error saving recording image", ex);
+ }
+ }
+
+ if (!program.IsSeries)
+ {
+ image = program.GetImageInfo(ImageType.Backdrop, 0);
+ if (image != null)
+ {
+ try
+ {
+ await SaveRecordingImage(recordingPath, program, image).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error saving recording image", ex);
+ }
+ }
+
+ image = program.GetImageInfo(ImageType.Thumb, 0);
+ if (image != null)
+ {
+ try
+ {
+ await SaveRecordingImage(recordingPath, program, image).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error saving recording image", ex);
+ }
+ }
+
+ image = program.GetImageInfo(ImageType.Logo, 0);
+ if (image != null)
+ {
+ try
+ {
+ await SaveRecordingImage(recordingPath, program, image).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error saving recording image", ex);
+ }
}
}
}
- private void SaveNfo(TimerInfo timer, string recordingPath, string seriesPath)
+ private async void SaveRecordingMetadata(TimerInfo timer, string recordingPath, string seriesPath)
{
try
{
+ var program = string.IsNullOrWhiteSpace(timer.ProgramId) ? null : _libraryManager.GetItemList(new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
+ Limit = 1,
+ ExternalId = timer.ProgramId
+
+ }).FirstOrDefault() as LiveTvProgram;
+
+ // dummy this up
+ if (program == null)
+ {
+ program = new LiveTvProgram
+ {
+ Name = timer.Name,
+ HomePageUrl = timer.HomePageUrl,
+ ShortOverview = timer.ShortOverview,
+ Overview = timer.Overview,
+ Genres = timer.Genres,
+ CommunityRating = timer.CommunityRating,
+ OfficialRating = timer.OfficialRating,
+ ProductionYear = timer.ProductionYear,
+ PremiereDate = timer.OriginalAirDate,
+ IndexNumber = timer.EpisodeNumber,
+ ParentIndexNumber = timer.SeasonNumber
+ };
+ }
+
+ if (timer.IsSports)
+ {
+ AddGenre(program.Genres, "Sports");
+ }
+ if (timer.IsKids)
+ {
+ AddGenre(program.Genres, "Kids");
+ AddGenre(program.Genres, "Children");
+ }
+ if (timer.IsNews)
+ {
+ AddGenre(program.Genres, "News");
+ }
+
if (timer.IsProgramSeries)
{
- SaveSeriesNfo(timer, recordingPath, seriesPath);
+ SaveSeriesNfo(timer, seriesPath);
+ SaveVideoNfo(timer, recordingPath, program, false);
}
else if (!timer.IsMovie || timer.IsSports || timer.IsNews)
{
- SaveVideoNfo(timer, recordingPath);
+ SaveVideoNfo(timer, recordingPath, program, true);
}
+ else
+ {
+ SaveVideoNfo(timer, recordingPath, program, false);
+ }
+
+ await SaveRecordingImages(recordingPath, program).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -1569,16 +1802,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
}
}
- private void SaveSeriesNfo(TimerInfo timer, string recordingPath, string seriesPath)
+ private void SaveSeriesNfo(TimerInfo timer, string seriesPath)
{
var nfoPath = Path.Combine(seriesPath, "tvshow.nfo");
- if (File.Exists(nfoPath))
+ if (_fileSystem.FileExists(nfoPath))
{
return;
}
- using (var stream = _fileSystem.GetFileStream(nfoPath, FileMode.Create, FileAccess.Write, FileShare.Read))
+ using (var stream = _fileSystem.GetFileStream(nfoPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
{
var settings = new XmlWriterSettings
{
@@ -1597,6 +1830,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
writer.WriteElementString("title", timer.Name);
}
+ if (!string.IsNullOrEmpty(timer.OfficialRating))
+ {
+ writer.WriteElementString("mpaa", timer.OfficialRating);
+ }
+
+ foreach (var genre in timer.Genres)
+ {
+ writer.WriteElementString("genre", genre);
+ }
+
writer.WriteEndElement();
writer.WriteEndDocument();
}
@@ -1604,16 +1847,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
}
public const string DateAddedFormat = "yyyy-MM-dd HH:mm:ss";
- private void SaveVideoNfo(TimerInfo timer, string recordingPath)
+ private void SaveVideoNfo(TimerInfo timer, string recordingPath, BaseItem item, bool lockData)
{
var nfoPath = Path.ChangeExtension(recordingPath, ".nfo");
- if (File.Exists(nfoPath))
+ if (_fileSystem.FileExists(nfoPath))
{
return;
}
- using (var stream = _fileSystem.GetFileStream(nfoPath, FileMode.Create, FileAccess.Write, FileShare.Read))
+ using (var stream = _fileSystem.GetFileStream(nfoPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
{
var settings = new XmlWriterSettings
{
@@ -1622,66 +1865,199 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
CloseOutput = false
};
+ var options = _config.GetNfoConfiguration();
+
using (XmlWriter writer = XmlWriter.Create(stream, settings))
{
writer.WriteStartDocument(true);
- writer.WriteStartElement("movie");
- if (!string.IsNullOrWhiteSpace(timer.Name))
+ if (timer.IsProgramSeries)
{
- writer.WriteElementString("title", timer.Name);
+ writer.WriteStartElement("episodedetails");
+
+ if (!string.IsNullOrWhiteSpace(timer.EpisodeTitle))
+ {
+ writer.WriteElementString("title", timer.EpisodeTitle);
+ }
+
+ if (item.PremiereDate.HasValue)
+ {
+ var formatString = options.ReleaseDateFormat;
+
+ writer.WriteElementString("aired", item.PremiereDate.Value.ToLocalTime().ToString(formatString));
+ }
+
+ if (item.IndexNumber.HasValue)
+ {
+ writer.WriteElementString("episode", item.IndexNumber.Value.ToString(CultureInfo.InvariantCulture));
+ }
+
+ if (item.ParentIndexNumber.HasValue)
+ {
+ writer.WriteElementString("season", item.ParentIndexNumber.Value.ToString(CultureInfo.InvariantCulture));
+ }
+ }
+ else
+ {
+ writer.WriteStartElement("movie");
+
+ if (!string.IsNullOrWhiteSpace(item.Name))
+ {
+ writer.WriteElementString("title", item.Name);
+ }
+
+ if (!string.IsNullOrWhiteSpace(item.OriginalTitle))
+ {
+ writer.WriteElementString("originaltitle", item.OriginalTitle);
+ }
+
+ if (item.PremiereDate.HasValue)
+ {
+ var formatString = options.ReleaseDateFormat;
+
+ writer.WriteElementString("premiered", item.PremiereDate.Value.ToLocalTime().ToString(formatString));
+ writer.WriteElementString("releasedate", item.PremiereDate.Value.ToLocalTime().ToString(formatString));
+ }
}
writer.WriteElementString("dateadded", DateTime.UtcNow.ToLocalTime().ToString(DateAddedFormat));
- if (timer.ProductionYear.HasValue)
+ if (item.ProductionYear.HasValue)
{
- writer.WriteElementString("year", timer.ProductionYear.Value.ToString(CultureInfo.InvariantCulture));
+ writer.WriteElementString("year", item.ProductionYear.Value.ToString(CultureInfo.InvariantCulture));
}
- if (!string.IsNullOrEmpty(timer.OfficialRating))
+
+ if (!string.IsNullOrEmpty(item.OfficialRating))
{
- writer.WriteElementString("mpaa", timer.OfficialRating);
+ writer.WriteElementString("mpaa", item.OfficialRating);
+ }
+
+ if (!string.IsNullOrEmpty(item.OfficialRatingDescription))
+ {
+ writer.WriteElementString("mpaadescription", item.OfficialRatingDescription);
}
- var overview = (timer.Overview ?? string.Empty)
+ var overview = (item.Overview ?? string.Empty)
.StripHtml()
.Replace("&quot;", "'");
writer.WriteElementString("plot", overview);
- writer.WriteElementString("lockdata", true.ToString().ToLower());
- if (timer.CommunityRating.HasValue)
+ if (lockData)
{
- writer.WriteElementString("rating", timer.CommunityRating.Value.ToString(CultureInfo.InvariantCulture));
+ writer.WriteElementString("lockdata", true.ToString().ToLower());
}
- if (timer.IsSports)
+ if (item.CommunityRating.HasValue)
{
- AddGenre(timer.Genres, "Sports");
+ writer.WriteElementString("rating", item.CommunityRating.Value.ToString(CultureInfo.InvariantCulture));
}
- if (timer.IsKids)
+
+ foreach (var genre in item.Genres)
{
- AddGenre(timer.Genres, "Kids");
- AddGenre(timer.Genres, "Children");
+ writer.WriteElementString("genre", genre);
}
- if (timer.IsNews)
+
+ if (!string.IsNullOrWhiteSpace(item.ShortOverview))
{
- AddGenre(timer.Genres, "News");
+ writer.WriteElementString("outline", item.ShortOverview);
}
- foreach (var genre in timer.Genres)
+ if (!string.IsNullOrWhiteSpace(item.HomePageUrl))
{
- writer.WriteElementString("genre", genre);
+ writer.WriteElementString("website", item.HomePageUrl);
}
- if (!string.IsNullOrWhiteSpace(timer.ShortOverview))
+ var people = item.Id == Guid.Empty ? new List<PersonInfo>() : _libraryManager.GetPeople(item);
+
+ var directors = people
+ .Where(i => IsPersonType(i, PersonType.Director))
+ .Select(i => i.Name)
+ .ToList();
+
+ foreach (var person in directors)
{
- writer.WriteElementString("outline", timer.ShortOverview);
+ writer.WriteElementString("director", person);
}
- if (!string.IsNullOrWhiteSpace(timer.HomePageUrl))
+ var writers = people
+ .Where(i => IsPersonType(i, PersonType.Writer))
+ .Select(i => i.Name)
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .ToList();
+
+ foreach (var person in writers)
+ {
+ writer.WriteElementString("writer", person);
+ }
+
+ foreach (var person in writers)
+ {
+ writer.WriteElementString("credits", person);
+ }
+
+ var rt = item.GetProviderId(MetadataProviders.RottenTomatoes);
+
+ if (!string.IsNullOrEmpty(rt))
{
- writer.WriteElementString("website", timer.HomePageUrl);
+ writer.WriteElementString("rottentomatoesid", rt);
+ }
+
+ var tmdbCollection = item.GetProviderId(MetadataProviders.TmdbCollection);
+
+ if (!string.IsNullOrEmpty(tmdbCollection))
+ {
+ writer.WriteElementString("collectionnumber", tmdbCollection);
+ }
+
+ var imdb = item.GetProviderId(MetadataProviders.Imdb);
+ if (!string.IsNullOrEmpty(imdb))
+ {
+ if (item is Series)
+ {
+ writer.WriteElementString("imdb_id", imdb);
+ }
+ else
+ {
+ writer.WriteElementString("imdbid", imdb);
+ }
+ }
+
+ var tvdb = item.GetProviderId(MetadataProviders.Tvdb);
+ if (!string.IsNullOrEmpty(tvdb))
+ {
+ writer.WriteElementString("tvdbid", tvdb);
+ }
+
+ var tmdb = item.GetProviderId(MetadataProviders.Tmdb);
+ if (!string.IsNullOrEmpty(tmdb))
+ {
+ writer.WriteElementString("tmdbid", tmdb);
+ }
+
+ if (item.CriticRating.HasValue)
+ {
+ writer.WriteElementString("criticrating", item.CriticRating.Value.ToString(CultureInfo.InvariantCulture));
+ }
+
+ if (!string.IsNullOrEmpty(item.CriticRatingSummary))
+ {
+ writer.WriteElementString("criticratingsummary", item.CriticRatingSummary);
+ }
+
+ if (!string.IsNullOrWhiteSpace(item.Tagline))
+ {
+ writer.WriteElementString("tagline", item.Tagline);
+ }
+
+ foreach (var studio in item.Studios)
+ {
+ writer.WriteElementString("studio", studio);
+ }
+
+ if (item.VoteCount.HasValue)
+ {
+ writer.WriteElementString("votes", item.VoteCount.Value.ToString(CultureInfo.InvariantCulture));
}
writer.WriteEndElement();
@@ -1690,6 +2066,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
}
}
+ private static bool IsPersonType(PersonInfo person, string type)
+ {
+ return string.Equals(person.Type, type, StringComparison.OrdinalIgnoreCase) || string.Equals(person.Role, type, StringComparison.OrdinalIgnoreCase);
+ }
+
private void AddGenre(List<string> genres, string genre)
{
if (!genres.Contains(genre, StringComparer.OrdinalIgnoreCase))
@@ -1745,6 +2126,39 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
return seriesTimer.SkipEpisodesInLibrary && IsProgramAlreadyInLibrary(timer);
}
+ private void HandleDuplicateShowIds(List<TimerInfo> timers)
+ {
+ foreach (var timer in timers.Skip(1))
+ {
+ // TODO: Get smarter, prefer HD, etc
+
+ timer.Status = RecordingStatus.Cancelled;
+ _timerProvider.Update(timer);
+ }
+ }
+
+ private void SearchForDuplicateShowIds(List<TimerInfo> timers)
+ {
+ var groups = timers.ToLookup(i => i.ShowId ?? string.Empty).ToList();
+
+ foreach (var group in groups)
+ {
+ if (string.IsNullOrWhiteSpace(group.Key))
+ {
+ continue;
+ }
+
+ var groupTimers = group.ToList();
+
+ if (groupTimers.Count < 2)
+ {
+ continue;
+ }
+
+ HandleDuplicateShowIds(groupTimers);
+ }
+ }
+
private async Task UpdateTimersForSeriesTimer(List<ProgramInfo> epgData, SeriesTimerInfo seriesTimer, bool deleteInvalidTimers)
{
var allTimers = GetTimersForSeries(seriesTimer, epgData)
@@ -1752,6 +2166,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
var registration = await _liveTvManager.GetRegistrationInfo("seriesrecordings").ConfigureAwait(false);
+ var enabledTimersForSeries = new List<TimerInfo>();
+
if (registration.IsValid)
{
foreach (var timer in allTimers)
@@ -1764,6 +2180,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
{
timer.Status = RecordingStatus.Cancelled;
}
+ else
+ {
+ enabledTimersForSeries.Add(timer);
+ }
_timerProvider.Add(timer);
}
else
@@ -1779,6 +2199,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
existingTimer.Status = RecordingStatus.Cancelled;
}
+ if (existingTimer.Status != RecordingStatus.Cancelled)
+ {
+ enabledTimersForSeries.Add(existingTimer);
+ }
+
+ existingTimer.KeepUntil = seriesTimer.KeepUntil;
+ existingTimer.IsPostPaddingRequired = seriesTimer.IsPostPaddingRequired;
+ existingTimer.IsPrePaddingRequired = seriesTimer.IsPrePaddingRequired;
+ existingTimer.PostPaddingSeconds = seriesTimer.PostPaddingSeconds;
+ existingTimer.PrePaddingSeconds = seriesTimer.PrePaddingSeconds;
+ existingTimer.Priority = seriesTimer.Priority;
+
existingTimer.SeriesTimerId = seriesTimer.Id;
_timerProvider.Update(existingTimer);
}
@@ -1786,6 +2218,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
}
}
+ SearchForDuplicateShowIds(enabledTimersForSeries);
+
if (deleteInvalidTimers)
{
var allTimerIds = allTimers
@@ -1810,8 +2244,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
}
}
- private IEnumerable<TimerInfo> GetTimersForSeries(SeriesTimerInfo seriesTimer,
- IEnumerable<ProgramInfo> allPrograms)
+ private IEnumerable<TimerInfo> GetTimersForSeries(SeriesTimerInfo seriesTimer, IEnumerable<ProgramInfo> allPrograms)
{
if (seriesTimer == null)
{
@@ -1929,7 +2362,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
var defaultFolder = RecordingPath;
var defaultName = "Recordings";
- if (Directory.Exists(defaultFolder))
+ if (_fileSystem.DirectoryExists(defaultFolder))
{
list.Add(new VirtualFolderInfo
{
@@ -1939,7 +2372,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
}
var customPath = GetConfiguration().MovieRecordingPath;
- if ((!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase)) && Directory.Exists(customPath))
+ if ((!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase)) && _fileSystem.DirectoryExists(customPath))
{
list.Add(new VirtualFolderInfo
{
@@ -1950,7 +2383,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
}
customPath = GetConfiguration().SeriesRecordingPath;
- if ((!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase)) && Directory.Exists(customPath))
+ if ((!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase)) && _fileSystem.DirectoryExists(customPath))
{
list.Add(new VirtualFolderInfo
{
@@ -1971,4 +2404,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
public CancellationTokenSource CancellationTokenSource { get; set; }
}
}
+ public static class ConfigurationExtension
+ {
+ public static XbmcMetadataOptions GetNfoConfiguration(this IConfigurationManager manager)
+ {
+ return manager.GetConfiguration<XbmcMetadataOptions>("xbmcmetadata");
+ }
+ }
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTVRegistration.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTVRegistration.cs
index 675fca3258..b339537ae7 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTVRegistration.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTVRegistration.cs
@@ -1,7 +1,7 @@
using System.Threading.Tasks;
using MediaBrowser.Common.Security;
-namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
+namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
public class EmbyTVRegistration : IRequiresRegistration
{
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
index cdf8e75974..5e55b893f3 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
@@ -7,18 +7,20 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Model.Diagnostics;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
-namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
+namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
public class EncodedRecorder : IRecorder
{
@@ -31,11 +33,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
private bool _hasExited;
private Stream _logFileStream;
private string _targetPath;
- private Process _process;
+ private IProcess _process;
+ private readonly IProcessFactory _processFactory;
private readonly IJsonSerializer _json;
private readonly TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
- public EncodedRecorder(ILogger logger, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IServerApplicationPaths appPaths, IJsonSerializer json, LiveTvOptions liveTvOptions, IHttpClient httpClient)
+ public EncodedRecorder(ILogger logger, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IServerApplicationPaths appPaths, IJsonSerializer json, LiveTvOptions liveTvOptions, IHttpClient httpClient, IProcessFactory processFactory)
{
_logger = logger;
_fileSystem = fileSystem;
@@ -44,6 +47,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
_json = json;
_liveTvOptions = liveTvOptions;
_httpClient = httpClient;
+ _processFactory = processFactory;
}
private string OutputFormat
@@ -52,7 +56,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
{
var format = _liveTvOptions.RecordingEncodingFormat;
- if (string.Equals(format, "mkv", StringComparison.OrdinalIgnoreCase) || _liveTvOptions.EnableOriginalVideoWithEncodedRecordings)
+ if (string.Equals(format, "mkv", StringComparison.OrdinalIgnoreCase))
{
return "mkv";
}
@@ -81,27 +85,23 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
_targetPath = targetFile;
_fileSystem.CreateDirectory(Path.GetDirectoryName(targetFile));
- var process = new Process
+ var process = _processFactory.Create(new ProcessOptions
{
- StartInfo = new ProcessStartInfo
- {
- CreateNoWindow = true,
- UseShellExecute = false,
-
- // Must consume both stdout and stderr or deadlocks may occur
- //RedirectStandardOutput = true,
- RedirectStandardError = true,
- RedirectStandardInput = true,
+ CreateNoWindow = true,
+ UseShellExecute = false,
- FileName = _mediaEncoder.EncoderPath,
- Arguments = GetCommandLineArgs(mediaSource, inputFile, targetFile, duration),
+ // Must consume both stdout and stderr or deadlocks may occur
+ //RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ RedirectStandardInput = true,
- WindowStyle = ProcessWindowStyle.Hidden,
- ErrorDialog = false
- },
+ FileName = _mediaEncoder.EncoderPath,
+ Arguments = GetCommandLineArgs(mediaSource, inputFile, targetFile, duration),
+ IsHidden = true,
+ ErrorDialog = false,
EnableRaisingEvents = true
- };
+ });
_process = process;
@@ -112,7 +112,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
_fileSystem.CreateDirectory(Path.GetDirectoryName(logFilePath));
// FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
- _logFileStream = _fileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true);
+ _logFileStream = _fileSystem.GetFileStream(logFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true);
var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(_json.SerializeToString(mediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
_logFileStream.Write(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length);
@@ -177,6 +177,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
inputModifiers = "-ss " + _mediaEncoder.GetTimeParameter(startTimeTicks) + " " + inputModifiers;
}
+ var analyzeDurationSeconds = 5;
+ var analyzeDuration = " -analyzeduration " +
+ (analyzeDurationSeconds * 1000000).ToString(CultureInfo.InvariantCulture);
+ inputModifiers += analyzeDuration;
+
commandLineArgs = string.Format(commandLineArgs, inputTempFile, targetFile, videoArgs, GetAudioArgs(mediaSource), durationParam);
return inputModifiers + " " + commandLineArgs;
@@ -204,7 +209,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
private bool EncodeVideo(MediaSourceInfo mediaSource)
{
- if (_liveTvOptions.EnableOriginalAudioWithEncodedRecordings)
+ if (string.Equals(_liveTvOptions.RecordedVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
return false;
}
@@ -250,7 +255,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
/// <summary>
/// Processes the exited.
/// </summary>
- private void OnFfMpegProcessExited(Process process, string inputFile)
+ private void OnFfMpegProcessExited(IProcess process, string inputFile)
{
_hasExited = true;
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
index 713cb9cd30..139cf570ee 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
@@ -1,6 +1,6 @@
using MediaBrowser.Controller.Plugins;
-namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
+namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
public class EntryPoint : IServerEntryPoint
{
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs
index 5706b6ae9e..3b5e60c4a7 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs
@@ -3,7 +3,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Dto;
-namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
+namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
public interface IRecorder
{
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
index 7fe271bea4..16ae26d45d 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
@@ -4,9 +4,11 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
-namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
+namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
public class ItemDataProvider<T>
where T : class
@@ -52,13 +54,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
catch (FileNotFoundException)
{
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
}
- catch (IOException ex)
- {
- Logger.ErrorException("Error deserializing {0}", ex, jsonFile);
- }
catch (Exception ex)
{
Logger.ErrorException("Error deserializing {0}", ex, jsonFile);
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
index f7b4b3fde6..84f802d761 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
@@ -4,7 +4,7 @@ using System;
using System.Globalization;
using MediaBrowser.Model.LiveTv;
-namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
+namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
internal class RecordingHelper
{
@@ -15,22 +15,25 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
public static TimerInfo CreateTimer(ProgramInfo parent, SeriesTimerInfo seriesTimer)
{
- var timer = new TimerInfo();
-
- timer.ChannelId = parent.ChannelId;
- timer.Id = (seriesTimer.Id + parent.Id).GetMD5().ToString("N");
- timer.StartDate = parent.StartDate;
- timer.EndDate = parent.EndDate;
- timer.ProgramId = parent.Id;
- timer.PrePaddingSeconds = seriesTimer.PrePaddingSeconds;
- timer.PostPaddingSeconds = seriesTimer.PostPaddingSeconds;
- timer.IsPostPaddingRequired = seriesTimer.IsPostPaddingRequired;
- timer.IsPrePaddingRequired = seriesTimer.IsPrePaddingRequired;
- timer.KeepUntil = seriesTimer.KeepUntil;
- timer.Priority = seriesTimer.Priority;
- timer.Name = parent.Name;
- timer.Overview = parent.Overview;
- timer.SeriesTimerId = seriesTimer.Id;
+ var timer = new TimerInfo
+ {
+ ChannelId = parent.ChannelId,
+ Id = (seriesTimer.Id + parent.Id).GetMD5().ToString("N"),
+ StartDate = parent.StartDate,
+ EndDate = parent.EndDate,
+ ProgramId = parent.Id,
+ PrePaddingSeconds = seriesTimer.PrePaddingSeconds,
+ PostPaddingSeconds = seriesTimer.PostPaddingSeconds,
+ IsPostPaddingRequired = seriesTimer.IsPostPaddingRequired,
+ IsPrePaddingRequired = seriesTimer.IsPrePaddingRequired,
+ KeepUntil = seriesTimer.KeepUntil,
+ Priority = seriesTimer.Priority,
+ Name = parent.Name,
+ Overview = parent.Overview,
+ SeriesId = parent.SeriesId,
+ SeriesTimerId = seriesTimer.Id,
+ ShowId = parent.ShowId
+ };
CopyProgramInfoToTimerInfo(parent, timer);
@@ -39,6 +42,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
public static void CopyProgramInfoToTimerInfo(ProgramInfo programInfo, TimerInfo timerInfo)
{
+ timerInfo.Name = programInfo.Name;
+ timerInfo.StartDate = programInfo.StartDate;
+ timerInfo.EndDate = programInfo.EndDate;
+ timerInfo.ChannelId = programInfo.ChannelId;
+
timerInfo.SeasonNumber = programInfo.SeasonNumber;
timerInfo.EpisodeNumber = programInfo.EpisodeNumber;
timerInfo.IsMovie = programInfo.IsMovie;
@@ -52,9 +60,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
timerInfo.HomePageUrl = programInfo.HomePageUrl;
timerInfo.CommunityRating = programInfo.CommunityRating;
+ timerInfo.Overview = programInfo.Overview;
timerInfo.ShortOverview = programInfo.ShortOverview;
timerInfo.OfficialRating = programInfo.OfficialRating;
timerInfo.IsRepeat = programInfo.IsRepeat;
+ timerInfo.SeriesId = programInfo.SeriesId;
}
public static string GetRecordingName(TimerInfo info)
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
index 40e532c4e6..7bf6bf1ca2 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
@@ -2,9 +2,11 @@
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using System;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
-namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
+namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
public class SeriesTimerManager : ItemDataProvider<SeriesTimerInfo>
{
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
index bddce04201..35868d318e 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
@@ -8,22 +8,27 @@ using System.Collections.Concurrent;
using System.Globalization;
using System.Linq;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv;
+using MediaBrowser.Model.Threading;
-namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
+namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
public class TimerManager : ItemDataProvider<TimerInfo>
{
- private readonly ConcurrentDictionary<string, Timer> _timers = new ConcurrentDictionary<string, Timer>(StringComparer.OrdinalIgnoreCase);
+ private readonly ConcurrentDictionary<string, ITimer> _timers = new ConcurrentDictionary<string, ITimer>(StringComparer.OrdinalIgnoreCase);
private readonly ILogger _logger;
public event EventHandler<GenericEventArgs<TimerInfo>> TimerFired;
+ private readonly ITimerFactory _timerFactory;
- public TimerManager(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath, ILogger logger1)
+ public TimerManager(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath, ILogger logger1, ITimerFactory timerFactory)
: base(fileSystem, jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
{
_logger = logger1;
+ _timerFactory = timerFactory;
}
public void RestartTimers()
@@ -124,7 +129,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
private void StartTimer(TimerInfo item, TimeSpan dueTime)
{
- var timer = new Timer(TimerCallback, item.Id, dueTime, TimeSpan.Zero);
+ var timer = _timerFactory.Create(TimerCallback, item.Id, dueTime, TimeSpan.Zero);
if (_timers.TryAdd(item.Id, timer))
{
@@ -139,7 +144,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
private void StopTimer(TimerInfo item)
{
- Timer timer;
+ ITimer timer;
if (_timers.TryRemove(item.Id, out timer))
{
timer.Dispose();
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
new file mode 100644
index 0000000000..04fc78c958
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -0,0 +1,1311 @@
+using System.Net;
+using MediaBrowser.Common;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.LiveTv;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Serialization;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Emby.Server.Implementations.LiveTv.Listings
+{
+ public class SchedulesDirect : IListingsProvider
+ {
+ private readonly ILogger _logger;
+ private readonly IJsonSerializer _jsonSerializer;
+ private readonly IHttpClient _httpClient;
+ private readonly SemaphoreSlim _tokenSemaphore = new SemaphoreSlim(1, 1);
+ private readonly IApplicationHost _appHost;
+
+ private const string ApiUrl = "https://json.schedulesdirect.org/20141201";
+
+ private readonly Dictionary<string, Dictionary<string, ScheduleDirect.Station>> _channelPairingCache =
+ new Dictionary<string, Dictionary<string, ScheduleDirect.Station>>(StringComparer.OrdinalIgnoreCase);
+
+ public SchedulesDirect(ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IApplicationHost appHost)
+ {
+ _logger = logger;
+ _jsonSerializer = jsonSerializer;
+ _httpClient = httpClient;
+ _appHost = appHost;
+ }
+
+ private string UserAgent
+ {
+ get { return "Emby/" + _appHost.ApplicationVersion; }
+ }
+
+ private List<string> GetScheduleRequestDates(DateTime startDateUtc, DateTime endDateUtc)
+ {
+ List<string> dates = new List<string>();
+
+ var start = new List<DateTime> { startDateUtc, startDateUtc.ToLocalTime() }.Min().Date;
+ var end = new List<DateTime> { endDateUtc, endDateUtc.ToLocalTime() }.Max().Date;
+
+ while (start <= end)
+ {
+ dates.Add(start.ToString("yyyy-MM-dd"));
+ start = start.AddDays(1);
+ }
+
+ return dates;
+ }
+
+ public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelNumber, string channelName, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken)
+ {
+ List<ProgramInfo> programsInfo = new List<ProgramInfo>();
+
+ var token = await GetToken(info, cancellationToken).ConfigureAwait(false);
+
+ if (string.IsNullOrWhiteSpace(token))
+ {
+ _logger.Warn("SchedulesDirect token is empty, returning empty program list");
+ return programsInfo;
+ }
+
+ if (string.IsNullOrWhiteSpace(info.ListingsId))
+ {
+ _logger.Warn("ListingsId is null, returning empty program list");
+ return programsInfo;
+ }
+
+ var dates = GetScheduleRequestDates(startDateUtc, endDateUtc);
+
+ ScheduleDirect.Station station = GetStation(info.ListingsId, channelNumber, channelName);
+
+ if (station == null)
+ {
+ _logger.Info("No Schedules Direct Station found for channel {0} with name {1}", channelNumber, channelName);
+ return programsInfo;
+ }
+
+ string stationID = station.stationID;
+
+ _logger.Info("Channel Station ID is: " + stationID);
+ List<ScheduleDirect.RequestScheduleForChannel> requestList =
+ new List<ScheduleDirect.RequestScheduleForChannel>()
+ {
+ new ScheduleDirect.RequestScheduleForChannel()
+ {
+ stationID = stationID,
+ date = dates
+ }
+ };
+
+ var requestString = _jsonSerializer.SerializeToString(requestList);
+ _logger.Debug("Request string for schedules is: " + requestString);
+
+ var httpOptions = new HttpRequestOptions()
+ {
+ Url = ApiUrl + "/schedules",
+ UserAgent = UserAgent,
+ CancellationToken = cancellationToken,
+ // The data can be large so give it some extra time
+ TimeoutMs = 60000,
+ LogErrorResponseBody = true
+ };
+
+ httpOptions.RequestHeaders["token"] = token;
+
+ httpOptions.RequestContent = requestString;
+ using (var response = await Post(httpOptions, true, info).ConfigureAwait(false))
+ {
+ StreamReader reader = new StreamReader(response.Content);
+ string responseString = reader.ReadToEnd();
+ var dailySchedules = _jsonSerializer.DeserializeFromString<List<ScheduleDirect.Day>>(responseString);
+ _logger.Debug("Found " + dailySchedules.Count + " programs on " + channelNumber + " ScheduleDirect");
+
+ httpOptions = new HttpRequestOptions()
+ {
+ Url = ApiUrl + "/programs",
+ UserAgent = UserAgent,
+ CancellationToken = cancellationToken,
+ LogErrorResponseBody = true,
+ // The data can be large so give it some extra time
+ TimeoutMs = 60000
+ };
+
+ httpOptions.RequestHeaders["token"] = token;
+
+ List<string> programsID = new List<string>();
+ programsID = dailySchedules.SelectMany(d => d.programs.Select(s => s.programID)).Distinct().ToList();
+ var requestBody = "[\"" + string.Join("\", \"", programsID) + "\"]";
+ httpOptions.RequestContent = requestBody;
+
+ using (var innerResponse = await Post(httpOptions, true, info).ConfigureAwait(false))
+ {
+ StreamReader innerReader = new StreamReader(innerResponse.Content);
+ responseString = innerReader.ReadToEnd();
+
+ var programDetails =
+ _jsonSerializer.DeserializeFromString<List<ScheduleDirect.ProgramDetails>>(
+ responseString);
+ var programDict = programDetails.ToDictionary(p => p.programID, y => y);
+
+ var images = await GetImageForPrograms(info, programDetails.Where(p => p.hasImageArtwork).Select(p => p.programID).ToList(), cancellationToken);
+
+ var schedules = dailySchedules.SelectMany(d => d.programs);
+ foreach (ScheduleDirect.Program schedule in schedules)
+ {
+ //_logger.Debug("Proccesing Schedule for statio ID " + stationID +
+ // " which corresponds to channel " + channelNumber + " and program id " +
+ // schedule.programID + " which says it has images? " +
+ // programDict[schedule.programID].hasImageArtwork);
+
+ if (images != null)
+ {
+ var imageIndex = images.FindIndex(i => i.programID == schedule.programID.Substring(0, 10));
+ if (imageIndex > -1)
+ {
+ var programEntry = programDict[schedule.programID];
+
+ var data = images[imageIndex].data ?? new List<ScheduleDirect.ImageData>();
+ data = data.OrderByDescending(GetSizeOrder).ToList();
+
+ programEntry.primaryImage = GetProgramImage(ApiUrl, data, "Logo", true, 600);
+ //programEntry.thumbImage = GetProgramImage(ApiUrl, data, "Iconic", false);
+ //programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ??
+ // GetProgramImage(ApiUrl, data, "Banner-L1", false) ??
+ // GetProgramImage(ApiUrl, data, "Banner-LO", false) ??
+ // GetProgramImage(ApiUrl, data, "Banner-LOT", false);
+ }
+ }
+
+ programsInfo.Add(GetProgram(channelNumber, schedule, programDict[schedule.programID]));
+ }
+ _logger.Info("Finished with EPGData");
+ }
+ }
+
+ return programsInfo;
+ }
+
+ private int GetSizeOrder(ScheduleDirect.ImageData image)
+ {
+ if (!string.IsNullOrWhiteSpace(image.height))
+ {
+ int value;
+ if (int.TryParse(image.height, out value))
+ {
+ return value;
+ }
+ }
+
+ return 0;
+ }
+
+ private readonly object _channelCacheLock = new object();
+ private ScheduleDirect.Station GetStation(string listingsId, string channelNumber, string channelName)
+ {
+ lock (_channelCacheLock)
+ {
+ Dictionary<string, ScheduleDirect.Station> channelPair;
+ if (_channelPairingCache.TryGetValue(listingsId, out channelPair))
+ {
+ ScheduleDirect.Station station;
+
+ if (!string.IsNullOrWhiteSpace(channelNumber) && channelPair.TryGetValue(channelNumber, out station))
+ {
+ return station;
+ }
+
+ if (!string.IsNullOrWhiteSpace(channelName))
+ {
+ channelName = NormalizeName(channelName);
+
+ var result = channelPair.Values.FirstOrDefault(i => string.Equals(NormalizeName(i.callsign ?? string.Empty), channelName, StringComparison.OrdinalIgnoreCase));
+
+ if (result != null)
+ {
+ return result;
+ }
+ }
+
+ if (!string.IsNullOrWhiteSpace(channelNumber))
+ {
+ return channelPair.Values.FirstOrDefault(i => string.Equals(NormalizeName(i.stationID ?? string.Empty), channelNumber, StringComparison.OrdinalIgnoreCase));
+ }
+ }
+
+ return null;
+ }
+ }
+
+ private void AddToChannelPairCache(string listingsId, string channelNumber, ScheduleDirect.Station schChannel)
+ {
+ lock (_channelCacheLock)
+ {
+ Dictionary<string, ScheduleDirect.Station> cache;
+ if (_channelPairingCache.TryGetValue(listingsId, out cache))
+ {
+ cache[channelNumber] = schChannel;
+ }
+ else
+ {
+ cache = new Dictionary<string, ScheduleDirect.Station>();
+ cache[channelNumber] = schChannel;
+ _channelPairingCache[listingsId] = cache;
+ }
+ }
+ }
+
+ private void ClearPairCache(string listingsId)
+ {
+ lock (_channelCacheLock)
+ {
+ Dictionary<string, ScheduleDirect.Station> cache;
+ if (_channelPairingCache.TryGetValue(listingsId, out cache))
+ {
+ cache.Clear();
+ }
+ }
+ }
+
+ private int GetChannelPairCacheCount(string listingsId)
+ {
+ lock (_channelCacheLock)
+ {
+ Dictionary<string, ScheduleDirect.Station> cache;
+ if (_channelPairingCache.TryGetValue(listingsId, out cache))
+ {
+ return cache.Count;
+ }
+
+ return 0;
+ }
+ }
+
+ private string NormalizeName(string value)
+ {
+ return value.Replace(" ", string.Empty).Replace("-", string.Empty);
+ }
+
+ public async Task AddMetadata(ListingsProviderInfo info, List<ChannelInfo> channels,
+ CancellationToken cancellationToken)
+ {
+ var listingsId = info.ListingsId;
+ if (string.IsNullOrWhiteSpace(listingsId))
+ {
+ throw new Exception("ListingsId required");
+ }
+
+ var token = await GetToken(info, cancellationToken);
+
+ if (string.IsNullOrWhiteSpace(token))
+ {
+ throw new Exception("token required");
+ }
+
+ ClearPairCache(listingsId);
+
+ var httpOptions = new HttpRequestOptions()
+ {
+ Url = ApiUrl + "/lineups/" + listingsId,
+ UserAgent = UserAgent,
+ CancellationToken = cancellationToken,
+ LogErrorResponseBody = true,
+ // The data can be large so give it some extra time
+ TimeoutMs = 60000
+ };
+
+ httpOptions.RequestHeaders["token"] = token;
+
+ using (var response = await Get(httpOptions, true, info).ConfigureAwait(false))
+ {
+ var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Channel>(response);
+ _logger.Info("Found " + root.map.Count + " channels on the lineup on ScheduleDirect");
+ _logger.Info("Mapping Stations to Channel");
+ foreach (ScheduleDirect.Map map in root.map)
+ {
+ var channelNumber = map.logicalChannelNumber;
+
+ if (string.IsNullOrWhiteSpace(channelNumber))
+ {
+ channelNumber = map.channel;
+ }
+ if (string.IsNullOrWhiteSpace(channelNumber))
+ {
+ channelNumber = map.atscMajor + "." + map.atscMinor;
+ }
+ channelNumber = channelNumber.TrimStart('0');
+
+ _logger.Debug("Found channel: " + channelNumber + " in Schedules Direct");
+
+ var schChannel = (root.stations ?? new List<ScheduleDirect.Station>()).FirstOrDefault(item => string.Equals(item.stationID, map.stationID, StringComparison.OrdinalIgnoreCase));
+ if (schChannel != null)
+ {
+ AddToChannelPairCache(listingsId, channelNumber, schChannel);
+ }
+ else
+ {
+ AddToChannelPairCache(listingsId, channelNumber, new ScheduleDirect.Station
+ {
+ stationID = map.stationID
+ });
+ }
+ }
+ _logger.Info("Added " + GetChannelPairCacheCount(listingsId) + " channels to the dictionary");
+
+ foreach (ChannelInfo channel in channels)
+ {
+ var station = GetStation(listingsId, channel.Number, channel.Name);
+
+ if (station != null)
+ {
+ if (station.logo != null)
+ {
+ channel.ImageUrl = station.logo.URL;
+ channel.HasImage = true;
+ }
+
+ if (!string.IsNullOrWhiteSpace(station.name))
+ {
+ channel.Name = station.name;
+ }
+ }
+ else
+ {
+ _logger.Info("Schedules Direct doesnt have data for channel: " + channel.Number + " " + channel.Name);
+ }
+ }
+ }
+ }
+
+ private ProgramInfo GetProgram(string channel, ScheduleDirect.Program programInfo,
+ ScheduleDirect.ProgramDetails details)
+ {
+ //_logger.Debug("Show type is: " + (details.showType ?? "No ShowType"));
+ DateTime startAt = GetDate(programInfo.airDateTime);
+ DateTime endAt = startAt.AddSeconds(programInfo.duration);
+ ProgramAudio audioType = ProgramAudio.Stereo;
+
+ bool repeat = programInfo.@new == null;
+ string newID = programInfo.programID + "T" + startAt.Ticks + "C" + channel;
+
+ if (programInfo.audioProperties != null)
+ {
+ if (programInfo.audioProperties.Exists(item => string.Equals(item, "atmos", StringComparison.OrdinalIgnoreCase)))
+ {
+ audioType = ProgramAudio.Atmos;
+ }
+ else if (programInfo.audioProperties.Exists(item => string.Equals(item, "dd 5.1", StringComparison.OrdinalIgnoreCase)))
+ {
+ audioType = ProgramAudio.DolbyDigital;
+ }
+ else if (programInfo.audioProperties.Exists(item => string.Equals(item, "dd", StringComparison.OrdinalIgnoreCase)))
+ {
+ audioType = ProgramAudio.DolbyDigital;
+ }
+ else if (programInfo.audioProperties.Exists(item => string.Equals(item, "stereo", StringComparison.OrdinalIgnoreCase)))
+ {
+ audioType = ProgramAudio.Stereo;
+ }
+ else
+ {
+ audioType = ProgramAudio.Mono;
+ }
+ }
+
+ string episodeTitle = null;
+ if (details.episodeTitle150 != null)
+ {
+ episodeTitle = details.episodeTitle150;
+ }
+
+ var showType = details.showType ?? string.Empty;
+
+ var info = new ProgramInfo
+ {
+ ChannelId = channel,
+ Id = newID,
+ StartDate = startAt,
+ EndDate = endAt,
+ Name = details.titles[0].title120 ?? "Unkown",
+ OfficialRating = null,
+ CommunityRating = null,
+ EpisodeTitle = episodeTitle,
+ Audio = audioType,
+ IsRepeat = repeat,
+ IsSeries = showType.IndexOf("series", StringComparison.OrdinalIgnoreCase) != -1,
+ ImageUrl = details.primaryImage,
+ IsKids = string.Equals(details.audience, "children", StringComparison.OrdinalIgnoreCase),
+ IsSports = showType.IndexOf("sports", StringComparison.OrdinalIgnoreCase) != -1,
+ IsMovie = showType.IndexOf("movie", StringComparison.OrdinalIgnoreCase) != -1 || showType.IndexOf("film", StringComparison.OrdinalIgnoreCase) != -1,
+ ShowId = programInfo.programID,
+ Etag = programInfo.md5
+ };
+
+ if (programInfo.videoProperties != null)
+ {
+ info.IsHD = programInfo.videoProperties.Contains("hdtv", StringComparer.OrdinalIgnoreCase);
+ info.Is3D = programInfo.videoProperties.Contains("3d", StringComparer.OrdinalIgnoreCase);
+ }
+
+ if (details.contentRating != null && details.contentRating.Count > 0)
+ {
+ info.OfficialRating = details.contentRating[0].code.Replace("TV", "TV-").Replace("--", "-");
+
+ var invalid = new[] { "N/A", "Approved", "Not Rated", "Passed" };
+ if (invalid.Contains(info.OfficialRating, StringComparer.OrdinalIgnoreCase))
+ {
+ info.OfficialRating = null;
+ }
+ }
+
+ if (details.descriptions != null)
+ {
+ if (details.descriptions.description1000 != null)
+ {
+ info.Overview = details.descriptions.description1000[0].description;
+ }
+ else if (details.descriptions.description100 != null)
+ {
+ info.ShortOverview = details.descriptions.description100[0].description;
+ }
+ }
+
+ if (info.IsSeries)
+ {
+ info.SeriesId = programInfo.programID.Substring(0, 10);
+
+ if (details.metadata != null)
+ {
+ var gracenote = details.metadata.Find(x => x.Gracenote != null).Gracenote;
+ info.SeasonNumber = gracenote.season;
+ info.EpisodeNumber = gracenote.episode;
+ }
+ }
+
+ if (!string.IsNullOrWhiteSpace(details.originalAirDate) && (!info.IsSeries || info.IsRepeat))
+ {
+ info.OriginalAirDate = DateTime.Parse(details.originalAirDate);
+ info.ProductionYear = info.OriginalAirDate.Value.Year;
+ }
+
+ if (details.genres != null)
+ {
+ info.Genres = details.genres.Where(g => !string.IsNullOrWhiteSpace(g)).ToList();
+ info.IsNews = details.genres.Contains("news", StringComparer.OrdinalIgnoreCase);
+
+ if (info.Genres.Contains("children", StringComparer.OrdinalIgnoreCase))
+ {
+ info.IsKids = true;
+ }
+ }
+
+ return info;
+ }
+
+ private DateTime GetDate(string value)
+ {
+ var date = DateTime.ParseExact(value, "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'", CultureInfo.InvariantCulture);
+
+ if (date.Kind != DateTimeKind.Utc)
+ {
+ date = DateTime.SpecifyKind(date, DateTimeKind.Utc);
+ }
+ return date;
+ }
+
+ private string GetProgramImage(string apiUrl, List<ScheduleDirect.ImageData> images, string category, bool returnDefaultImage, int desiredWidth)
+ {
+ string url = null;
+
+ var matches = images
+ .Where(i => string.Equals(i.category, category, StringComparison.OrdinalIgnoreCase))
+ .ToList();
+
+ if (matches.Count == 0)
+ {
+ if (!returnDefaultImage)
+ {
+ return null;
+ }
+ matches = images;
+ }
+
+ var match = matches.FirstOrDefault(i =>
+ {
+ if (!string.IsNullOrWhiteSpace(i.width))
+ {
+ int value;
+ if (int.TryParse(i.width, out value))
+ {
+ return value <= desiredWidth;
+ }
+ }
+
+ return false;
+ });
+
+ if (match == null)
+ {
+ // Get the second lowest quality image, when possible
+ if (matches.Count > 1)
+ {
+ match = matches[matches.Count - 2];
+ }
+ else
+ {
+ match = matches.FirstOrDefault();
+ }
+ }
+
+ if (match == null)
+ {
+ return null;
+ }
+
+ var uri = match.uri;
+
+ if (!string.IsNullOrWhiteSpace(uri))
+ {
+ if (uri.IndexOf("http", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ url = uri;
+ }
+ else
+ {
+ url = apiUrl + "/image/" + uri;
+ }
+ }
+ //_logger.Debug("URL for image is : " + url);
+ return url;
+ }
+
+ private async Task<List<ScheduleDirect.ShowImages>> GetImageForPrograms(
+ ListingsProviderInfo info,
+ List<string> programIds,
+ CancellationToken cancellationToken)
+ {
+ var imageIdString = "[";
+
+ foreach (var i in programIds)
+ {
+ if (!imageIdString.Contains(i.Substring(0, 10)))
+ {
+ imageIdString += "\"" + i.Substring(0, 10) + "\",";
+ }
+ }
+
+ imageIdString = imageIdString.TrimEnd(',') + "]";
+
+ var httpOptions = new HttpRequestOptions()
+ {
+ Url = ApiUrl + "/metadata/programs",
+ UserAgent = UserAgent,
+ CancellationToken = cancellationToken,
+ RequestContent = imageIdString,
+ LogErrorResponseBody = true,
+ // The data can be large so give it some extra time
+ TimeoutMs = 60000
+ };
+ List<ScheduleDirect.ShowImages> images;
+ using (var innerResponse2 = await Post(httpOptions, true, info).ConfigureAwait(false))
+ {
+ images = _jsonSerializer.DeserializeFromStream<List<ScheduleDirect.ShowImages>>(
+ innerResponse2.Content);
+ }
+
+ return images;
+ }
+
+ public async Task<List<NameIdPair>> GetHeadends(ListingsProviderInfo info, string country, string location, CancellationToken cancellationToken)
+ {
+ var token = await GetToken(info, cancellationToken);
+
+ var lineups = new List<NameIdPair>();
+
+ if (string.IsNullOrWhiteSpace(token))
+ {
+ return lineups;
+ }
+
+ var options = new HttpRequestOptions()
+ {
+ Url = ApiUrl + "/headends?country=" + country + "&postalcode=" + location,
+ UserAgent = UserAgent,
+ CancellationToken = cancellationToken,
+ LogErrorResponseBody = true
+ };
+
+ options.RequestHeaders["token"] = token;
+
+ try
+ {
+ using (Stream responce = await Get(options, false, info).ConfigureAwait(false))
+ {
+ var root = _jsonSerializer.DeserializeFromStream<List<ScheduleDirect.Headends>>(responce);
+
+ if (root != null)
+ {
+ foreach (ScheduleDirect.Headends headend in root)
+ {
+ foreach (ScheduleDirect.Lineup lineup in headend.lineups)
+ {
+ lineups.Add(new NameIdPair
+ {
+ Name = string.IsNullOrWhiteSpace(lineup.name) ? lineup.lineup : lineup.name,
+ Id = lineup.uri.Substring(18)
+ });
+ }
+ }
+ }
+ else
+ {
+ _logger.Info("No lineups available");
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.Error("Error getting headends", ex);
+ }
+
+ return lineups;
+ }
+
+ private readonly ConcurrentDictionary<string, NameValuePair> _tokens = new ConcurrentDictionary<string, NameValuePair>();
+ private DateTime _lastErrorResponse;
+ private async Task<string> GetToken(ListingsProviderInfo info, CancellationToken cancellationToken)
+ {
+ var username = info.Username;
+
+ // Reset the token if there's no username
+ if (string.IsNullOrWhiteSpace(username))
+ {
+ return null;
+ }
+
+ var password = info.Password;
+ if (string.IsNullOrWhiteSpace(password))
+ {
+ return null;
+ }
+
+ // Avoid hammering SD
+ if ((DateTime.UtcNow - _lastErrorResponse).TotalMinutes < 1)
+ {
+ return null;
+ }
+
+ NameValuePair savedToken = null;
+ if (!_tokens.TryGetValue(username, out savedToken))
+ {
+ savedToken = new NameValuePair();
+ _tokens.TryAdd(username, savedToken);
+ }
+
+ if (!string.IsNullOrWhiteSpace(savedToken.Name) && !string.IsNullOrWhiteSpace(savedToken.Value))
+ {
+ long ticks;
+ if (long.TryParse(savedToken.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out ticks))
+ {
+ // If it's under 24 hours old we can still use it
+ if (DateTime.UtcNow.Ticks - ticks < TimeSpan.FromHours(20).Ticks)
+ {
+ return savedToken.Name;
+ }
+ }
+ }
+
+ await _tokenSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
+ try
+ {
+ var result = await GetTokenInternal(username, password, cancellationToken).ConfigureAwait(false);
+ savedToken.Name = result;
+ savedToken.Value = DateTime.UtcNow.Ticks.ToString(CultureInfo.InvariantCulture);
+ return result;
+ }
+ catch (HttpException ex)
+ {
+ if (ex.StatusCode.HasValue)
+ {
+ if ((int)ex.StatusCode.Value == 400)
+ {
+ _tokens.Clear();
+ _lastErrorResponse = DateTime.UtcNow;
+ }
+ }
+ throw;
+ }
+ finally
+ {
+ _tokenSemaphore.Release();
+ }
+ }
+
+ private async Task<HttpResponseInfo> Post(HttpRequestOptions options,
+ bool enableRetry,
+ ListingsProviderInfo providerInfo)
+ {
+ try
+ {
+ return await _httpClient.Post(options).ConfigureAwait(false);
+ }
+ catch (HttpException ex)
+ {
+ _tokens.Clear();
+
+ if (!ex.StatusCode.HasValue || (int)ex.StatusCode.Value >= 500)
+ {
+ enableRetry = false;
+ }
+
+ if (!enableRetry)
+ {
+ throw;
+ }
+ }
+
+ var newToken = await GetToken(providerInfo, options.CancellationToken).ConfigureAwait(false);
+ options.RequestHeaders["token"] = newToken;
+ return await Post(options, false, providerInfo).ConfigureAwait(false);
+ }
+
+ private async Task<Stream> Get(HttpRequestOptions options,
+ bool enableRetry,
+ ListingsProviderInfo providerInfo)
+ {
+ try
+ {
+ return await _httpClient.Get(options).ConfigureAwait(false);
+ }
+ catch (HttpException ex)
+ {
+ _tokens.Clear();
+
+ if (!ex.StatusCode.HasValue || (int)ex.StatusCode.Value >= 500)
+ {
+ enableRetry = false;
+ }
+
+ if (!enableRetry)
+ {
+ throw;
+ }
+ }
+
+ var newToken = await GetToken(providerInfo, options.CancellationToken).ConfigureAwait(false);
+ options.RequestHeaders["token"] = newToken;
+ return await Get(options, false, providerInfo).ConfigureAwait(false);
+ }
+
+ private async Task<string> GetTokenInternal(string username, string password,
+ CancellationToken cancellationToken)
+ {
+ var httpOptions = new HttpRequestOptions()
+ {
+ Url = ApiUrl + "/token",
+ UserAgent = UserAgent,
+ RequestContent = "{\"username\":\"" + username + "\",\"password\":\"" + password + "\"}",
+ CancellationToken = cancellationToken,
+ LogErrorResponseBody = true
+ };
+ //_logger.Info("Obtaining token from Schedules Direct from addres: " + httpOptions.Url + " with body " +
+ // httpOptions.RequestContent);
+
+ using (var responce = await Post(httpOptions, false, null).ConfigureAwait(false))
+ {
+ var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Token>(responce.Content);
+ if (root.message == "OK")
+ {
+ _logger.Info("Authenticated with Schedules Direct token: " + root.token);
+ return root.token;
+ }
+
+ throw new Exception("Could not authenticate with Schedules Direct Error: " + root.message);
+ }
+ }
+
+ private async Task AddLineupToAccount(ListingsProviderInfo info, CancellationToken cancellationToken)
+ {
+ var token = await GetToken(info, cancellationToken);
+
+ if (string.IsNullOrWhiteSpace(token))
+ {
+ throw new ArgumentException("Authentication required.");
+ }
+
+ if (string.IsNullOrWhiteSpace(info.ListingsId))
+ {
+ throw new ArgumentException("Listings Id required");
+ }
+
+ _logger.Info("Adding new LineUp ");
+
+ var httpOptions = new HttpRequestOptions()
+ {
+ Url = ApiUrl + "/lineups/" + info.ListingsId,
+ UserAgent = UserAgent,
+ CancellationToken = cancellationToken,
+ LogErrorResponseBody = true,
+ BufferContent = false
+ };
+
+ httpOptions.RequestHeaders["token"] = token;
+
+ using (var response = await _httpClient.SendAsync(httpOptions, "PUT"))
+ {
+ }
+ }
+
+ public string Name
+ {
+ get { return "Schedules Direct"; }
+ }
+
+ public static string TypeName = "SchedulesDirect";
+ public string Type
+ {
+ get { return TypeName; }
+ }
+
+ private async Task<bool> HasLineup(ListingsProviderInfo info, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrWhiteSpace(info.ListingsId))
+ {
+ throw new ArgumentException("Listings Id required");
+ }
+
+ var token = await GetToken(info, cancellationToken);
+
+ if (string.IsNullOrWhiteSpace(token))
+ {
+ throw new Exception("token required");
+ }
+
+ _logger.Info("Headends on account ");
+
+ var options = new HttpRequestOptions()
+ {
+ Url = ApiUrl + "/lineups",
+ UserAgent = UserAgent,
+ CancellationToken = cancellationToken,
+ LogErrorResponseBody = true
+ };
+
+ options.RequestHeaders["token"] = token;
+
+ try
+ {
+ using (var response = await Get(options, false, null).ConfigureAwait(false))
+ {
+ var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Lineups>(response);
+
+ return root.lineups.Any(i => string.Equals(info.ListingsId, i.lineup, StringComparison.OrdinalIgnoreCase));
+ }
+ }
+ catch (HttpException ex)
+ {
+ // Apparently we're supposed to swallow this
+ if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.BadRequest)
+ {
+ return false;
+ }
+
+ throw;
+ }
+ }
+
+ public async Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings)
+ {
+ if (validateLogin)
+ {
+ if (string.IsNullOrWhiteSpace(info.Username))
+ {
+ throw new ArgumentException("Username is required");
+ }
+ if (string.IsNullOrWhiteSpace(info.Password))
+ {
+ throw new ArgumentException("Password is required");
+ }
+ }
+ if (validateListings)
+ {
+ if (string.IsNullOrWhiteSpace(info.ListingsId))
+ {
+ throw new ArgumentException("Listings Id required");
+ }
+
+ var hasLineup = await HasLineup(info, CancellationToken.None).ConfigureAwait(false);
+
+ if (!hasLineup)
+ {
+ await AddLineupToAccount(info, CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+ }
+
+ public Task<List<NameIdPair>> GetLineups(ListingsProviderInfo info, string country, string location)
+ {
+ return GetHeadends(info, country, location, CancellationToken.None);
+ }
+
+ public async Task<List<ChannelInfo>> GetChannels(ListingsProviderInfo info, CancellationToken cancellationToken)
+ {
+ var listingsId = info.ListingsId;
+ if (string.IsNullOrWhiteSpace(listingsId))
+ {
+ throw new Exception("ListingsId required");
+ }
+
+ await AddMetadata(info, new List<ChannelInfo>(), cancellationToken).ConfigureAwait(false);
+
+ var token = await GetToken(info, cancellationToken);
+
+ if (string.IsNullOrWhiteSpace(token))
+ {
+ throw new Exception("token required");
+ }
+
+ var httpOptions = new HttpRequestOptions()
+ {
+ Url = ApiUrl + "/lineups/" + listingsId,
+ UserAgent = UserAgent,
+ CancellationToken = cancellationToken,
+ LogErrorResponseBody = true,
+ // The data can be large so give it some extra time
+ TimeoutMs = 60000
+ };
+
+ httpOptions.RequestHeaders["token"] = token;
+
+ var list = new List<ChannelInfo>();
+
+ using (var response = await Get(httpOptions, true, info).ConfigureAwait(false))
+ {
+ var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Channel>(response);
+ _logger.Info("Found " + root.map.Count + " channels on the lineup on ScheduleDirect");
+ _logger.Info("Mapping Stations to Channel");
+ foreach (ScheduleDirect.Map map in root.map)
+ {
+ var channelNumber = map.logicalChannelNumber;
+
+ if (string.IsNullOrWhiteSpace(channelNumber))
+ {
+ channelNumber = map.channel;
+ }
+ if (string.IsNullOrWhiteSpace(channelNumber))
+ {
+ channelNumber = map.atscMajor + "." + map.atscMinor;
+ }
+ channelNumber = channelNumber.TrimStart('0');
+
+ var name = channelNumber;
+ var station = GetStation(listingsId, channelNumber, null);
+
+ if (station != null && !string.IsNullOrWhiteSpace(station.name))
+ {
+ name = station.name;
+ }
+
+ list.Add(new ChannelInfo
+ {
+ Number = channelNumber,
+ Name = name
+ });
+ }
+ }
+
+ return list;
+ }
+
+ public class ScheduleDirect
+ {
+ public class Token
+ {
+ public int code { get; set; }
+ public string message { get; set; }
+ public string serverID { get; set; }
+ public string token { get; set; }
+ }
+ public class Lineup
+ {
+ public string lineup { get; set; }
+ public string name { get; set; }
+ public string transport { get; set; }
+ public string location { get; set; }
+ public string uri { get; set; }
+ }
+
+ public class Lineups
+ {
+ public int code { get; set; }
+ public string serverID { get; set; }
+ public string datetime { get; set; }
+ public List<Lineup> lineups { get; set; }
+ }
+
+
+ public class Headends
+ {
+ public string headend { get; set; }
+ public string transport { get; set; }
+ public string location { get; set; }
+ public List<Lineup> lineups { get; set; }
+ }
+
+
+
+ public class Map
+ {
+ public string stationID { get; set; }
+ public string channel { get; set; }
+ public string logicalChannelNumber { get; set; }
+ public int uhfVhf { get; set; }
+ public int atscMajor { get; set; }
+ public int atscMinor { get; set; }
+ }
+
+ public class Broadcaster
+ {
+ public string city { get; set; }
+ public string state { get; set; }
+ public string postalcode { get; set; }
+ public string country { get; set; }
+ }
+
+ public class Logo
+ {
+ public string URL { get; set; }
+ public int height { get; set; }
+ public int width { get; set; }
+ public string md5 { get; set; }
+ }
+
+ public class Station
+ {
+ public string stationID { get; set; }
+ public string name { get; set; }
+ public string callsign { get; set; }
+ public List<string> broadcastLanguage { get; set; }
+ public List<string> descriptionLanguage { get; set; }
+ public Broadcaster broadcaster { get; set; }
+ public string affiliate { get; set; }
+ public Logo logo { get; set; }
+ public bool? isCommercialFree { get; set; }
+ }
+
+ public class Metadata
+ {
+ public string lineup { get; set; }
+ public string modified { get; set; }
+ public string transport { get; set; }
+ }
+
+ public class Channel
+ {
+ public List<Map> map { get; set; }
+ public List<Station> stations { get; set; }
+ public Metadata metadata { get; set; }
+ }
+
+ public class RequestScheduleForChannel
+ {
+ public string stationID { get; set; }
+ public List<string> date { get; set; }
+ }
+
+
+
+
+ public class Rating
+ {
+ public string body { get; set; }
+ public string code { get; set; }
+ }
+
+ public class Multipart
+ {
+ public int partNumber { get; set; }
+ public int totalParts { get; set; }
+ }
+
+ public class Program
+ {
+ public string programID { get; set; }
+ public string airDateTime { get; set; }
+ public int duration { get; set; }
+ public string md5 { get; set; }
+ public List<string> audioProperties { get; set; }
+ public List<string> videoProperties { get; set; }
+ public List<Rating> ratings { get; set; }
+ public bool? @new { get; set; }
+ public Multipart multipart { get; set; }
+ }
+
+
+
+ public class MetadataSchedule
+ {
+ public string modified { get; set; }
+ public string md5 { get; set; }
+ public string startDate { get; set; }
+ public string endDate { get; set; }
+ public int days { get; set; }
+ }
+
+ public class Day
+ {
+ public string stationID { get; set; }
+ public List<Program> programs { get; set; }
+ public MetadataSchedule metadata { get; set; }
+
+ public Day()
+ {
+ programs = new List<Program>();
+ }
+ }
+
+ //
+ public class Title
+ {
+ public string title120 { get; set; }
+ }
+
+ public class EventDetails
+ {
+ public string subType { get; set; }
+ }
+
+ public class Description100
+ {
+ public string descriptionLanguage { get; set; }
+ public string description { get; set; }
+ }
+
+ public class Description1000
+ {
+ public string descriptionLanguage { get; set; }
+ public string description { get; set; }
+ }
+
+ public class DescriptionsProgram
+ {
+ public List<Description100> description100 { get; set; }
+ public List<Description1000> description1000 { get; set; }
+ }
+
+ public class Gracenote
+ {
+ public int season { get; set; }
+ public int episode { get; set; }
+ }
+
+ public class MetadataPrograms
+ {
+ public Gracenote Gracenote { get; set; }
+ }
+
+ public class ContentRating
+ {
+ public string body { get; set; }
+ public string code { get; set; }
+ }
+
+ public class Cast
+ {
+ public string billingOrder { get; set; }
+ public string role { get; set; }
+ public string nameId { get; set; }
+ public string personId { get; set; }
+ public string name { get; set; }
+ public string characterName { get; set; }
+ }
+
+ public class Crew
+ {
+ public string billingOrder { get; set; }
+ public string role { get; set; }
+ public string nameId { get; set; }
+ public string personId { get; set; }
+ public string name { get; set; }
+ }
+
+ public class QualityRating
+ {
+ public string ratingsBody { get; set; }
+ public string rating { get; set; }
+ public string minRating { get; set; }
+ public string maxRating { get; set; }
+ public string increment { get; set; }
+ }
+
+ public class Movie
+ {
+ public string year { get; set; }
+ public int duration { get; set; }
+ public List<QualityRating> qualityRating { get; set; }
+ }
+
+ public class Recommendation
+ {
+ public string programID { get; set; }
+ public string title120 { get; set; }
+ }
+
+ public class ProgramDetails
+ {
+ public string audience { get; set; }
+ public string programID { get; set; }
+ public List<Title> titles { get; set; }
+ public EventDetails eventDetails { get; set; }
+ public DescriptionsProgram descriptions { get; set; }
+ public string originalAirDate { get; set; }
+ public List<string> genres { get; set; }
+ public string episodeTitle150 { get; set; }
+ public List<MetadataPrograms> metadata { get; set; }
+ public List<ContentRating> contentRating { get; set; }
+ public List<Cast> cast { get; set; }
+ public List<Crew> crew { get; set; }
+ public string showType { get; set; }
+ public bool hasImageArtwork { get; set; }
+ public string primaryImage { get; set; }
+ public string thumbImage { get; set; }
+ public string bannerImage { get; set; }
+ public string imageID { get; set; }
+ public string md5 { get; set; }
+ public List<string> contentAdvisory { get; set; }
+ public Movie movie { get; set; }
+ public List<Recommendation> recommendations { get; set; }
+ }
+
+ public class Caption
+ {
+ public string content { get; set; }
+ public string lang { get; set; }
+ }
+
+ public class ImageData
+ {
+ public string width { get; set; }
+ public string height { get; set; }
+ public string uri { get; set; }
+ public string size { get; set; }
+ public string aspect { get; set; }
+ public string category { get; set; }
+ public string text { get; set; }
+ public string primary { get; set; }
+ public string tier { get; set; }
+ public Caption caption { get; set; }
+ }
+
+ public class ShowImages
+ {
+ public string programID { get; set; }
+ public List<ImageData> data { get; set; }
+ }
+
+ }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
index d3549aef55..57307aa73a 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
@@ -15,21 +15,24 @@ using Emby.XmlTv.Entities;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
-namespace MediaBrowser.Server.Implementations.LiveTv.Listings
+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;
- public XmlTvListingsProvider(IServerConfigurationManager config, IHttpClient httpClient, ILogger logger)
+ public XmlTvListingsProvider(IServerConfigurationManager config, IHttpClient httpClient, ILogger logger, IFileSystem fileSystem)
{
_config = config;
_httpClient = httpClient;
_logger = logger;
+ _fileSystem = fileSystem;
}
public string Name
@@ -58,7 +61,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
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 (File.Exists(cacheFile))
+ if (_fileSystem.FileExists(cacheFile))
{
return cacheFile;
}
@@ -70,21 +73,23 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
CancellationToken = cancellationToken,
Url = path,
Progress = new Progress<Double>(),
- DecompressionMethod = DecompressionMethods.GZip,
+ 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
+ EnableHttpCompression = true,
+
+ UserAgent = "Emby/3.0"
}).ConfigureAwait(false);
- Directory.CreateDirectory(Path.GetDirectoryName(cacheFile));
+ _fileSystem.CreateDirectory(Path.GetDirectoryName(cacheFile));
- using (var stream = File.OpenRead(tempFile))
+ using (var stream = _fileSystem.OpenRead(tempFile))
{
using (var reader = new StreamReader(stream, Encoding.UTF8))
{
- using (var fileStream = File.OpenWrite(cacheFile))
+ using (var fileStream = _fileSystem.GetFileStream(cacheFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
{
using (var writer = new StreamWriter(fileStream))
{
@@ -113,7 +118,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
}
var path = await GetXml(info.Path, cancellationToken).ConfigureAwait(false);
- var reader = new XmlTvReader(path, GetLanguage(), null);
+ var reader = new XmlTvReader(path, GetLanguage());
var results = reader.GetProgrammes(channelNumber, startDateUtc, endDateUtc, cancellationToken);
return results.Select(p => GetProgramInfo(p, info));
@@ -121,12 +126,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
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 = p.Episode == null ? null : p.Episode.Title,
+ EpisodeTitle = episodeTitle,
Genres = p.Categories,
Id = String.Format("{0}_{1:O}", p.ChannelId, p.StartDate), // Construct an id from the channel and start date,
StartDate = GetDate(p.StartDate),
@@ -136,17 +143,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
ProductionYear = !p.CopyrightDate.HasValue ? (int?)null : p.CopyrightDate.Value.Year,
SeasonNumber = p.Episode == null ? null : p.Episode.Series,
IsSeries = p.Episode != null,
- IsRepeat = p.IsRepeat,
+ IsRepeat = p.IsPreviouslyShown && !p.IsNew,
IsPremiere = p.Premiere != null,
- IsKids = p.Categories.Any(c => info.KidsCategories.Contains(c, StringComparer.InvariantCultureIgnoreCase)),
- IsMovie = p.Categories.Any(c => info.MovieCategories.Contains(c, StringComparer.InvariantCultureIgnoreCase)),
- IsNews = p.Categories.Any(c => info.NewsCategories.Contains(c, StringComparer.InvariantCultureIgnoreCase)),
- IsSports = p.Categories.Any(c => info.SportsCategories.Contains(c, StringComparer.InvariantCultureIgnoreCase)),
+ 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
+ SeriesId = p.Episode != null ? p.Title.GetMD5().ToString("N") : null,
+ ShowId = ((p.Title ?? string.Empty) + (episodeTitle ?? string.Empty)).GetMD5().ToString("N")
};
if (programInfo.IsMovie)
@@ -172,12 +180,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
{
// Add the channel image url
var path = await GetXml(info.Path, cancellationToken).ConfigureAwait(false);
- var reader = new XmlTvReader(path, GetLanguage(), null);
+ var reader = new XmlTvReader(path, GetLanguage());
var results = reader.GetChannels().ToList();
if (channels != null)
{
- channels.ForEach(c =>
+ foreach (var c in channels)
{
var channelNumber = info.GetMappedChannel(c.Number);
var match = results.FirstOrDefault(r => string.Equals(r.Id, channelNumber, StringComparison.OrdinalIgnoreCase));
@@ -186,14 +194,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
{
c.ImageUrl = match.Icon.Source;
}
- });
+ }
}
}
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) && !File.Exists(info.Path))
+ if (!info.Path.StartsWith("http", StringComparison.OrdinalIgnoreCase) && !_fileSystem.FileExists(info.Path))
{
throw new FileNotFoundException("Could not find the XmlTv file specified:", info.Path);
}
@@ -205,7 +213,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
{
// In theory this should never be called because there is always only one lineup
var path = await GetXml(info.Path, CancellationToken.None).ConfigureAwait(false);
- var reader = new XmlTvReader(path, GetLanguage(), null);
+ var reader = new XmlTvReader(path, GetLanguage());
var results = reader.GetChannels();
// Should this method be async?
@@ -216,7 +224,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
{
// In theory this should never be called because there is always only one lineup
var path = await GetXml(info.Path, cancellationToken).ConfigureAwait(false);
- var reader = new XmlTvReader(path, GetLanguage(), null);
+ var reader = new XmlTvReader(path, GetLanguage());
var results = reader.GetChannels();
// Should this method be async?
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveStreamHelper.cs b/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs
index 336c32baef..a338ae23ae 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveStreamHelper.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs
@@ -8,7 +8,7 @@ using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Logging;
-namespace MediaBrowser.Server.Implementations.LiveTv
+namespace Emby.Server.Implementations.LiveTv
{
public class LiveStreamHelper
{
@@ -57,7 +57,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
mediaSource.RunTimeTicks = null;
}
- var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == Model.Entities.MediaStreamType.Audio);
+ var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Audio);
if (audioStream == null || audioStream.Index == -1)
{
@@ -68,7 +68,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
mediaSource.DefaultAudioStreamIndex = audioStream.Index;
}
- var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == Model.Entities.MediaStreamType.Video);
+ var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Video);
if (videoStream != null)
{
if (!videoStream.BitRate.HasValue)
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs b/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs
index 57d1d79e16..2be6427372 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs
@@ -2,7 +2,7 @@
using MediaBrowser.Model.LiveTv;
using System.Collections.Generic;
-namespace MediaBrowser.Server.Implementations.LiveTv
+namespace Emby.Server.Implementations.LiveTv
{
public class LiveTvConfigurationFactory : IConfigurationFactory
{
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
index c7a2d295d9..e73378ddeb 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
@@ -13,8 +13,10 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Model.Dto;
-namespace MediaBrowser.Server.Implementations.LiveTv
+namespace Emby.Server.Implementations.LiveTv
{
public class LiveTvDtoService
{
@@ -79,6 +81,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv
}
dto.ProgramInfo.SeriesTimerId = dto.SeriesTimerId;
+
+ if (!string.IsNullOrWhiteSpace(info.SeriesTimerId))
+ {
+ FillImages(dto.ProgramInfo, info.Name, info.SeriesId);
+ }
}
if (channel != null)
@@ -130,12 +137,59 @@ namespace MediaBrowser.Server.Implementations.LiveTv
dto.DayPattern = info.Days == null ? null : GetDayPattern(info.Days);
- if (!string.IsNullOrWhiteSpace(info.SeriesId))
+ FillImages(dto, info.Name, info.SeriesId);
+
+ return dto;
+ }
+
+ private void FillImages(BaseItemDto dto, string seriesName, string programSeriesId)
+ {
+ var librarySeries = _libraryManager.GetItemList(new InternalItemsQuery
+ {
+ IncludeItemTypes = new string[] { typeof(Series).Name },
+ Name = seriesName,
+ Limit = 1,
+ ImageTypes = new ImageType[] { ImageType.Thumb }
+
+ }).FirstOrDefault();
+
+ if (librarySeries != null)
+ {
+ var image = librarySeries.GetImageInfo(ImageType.Thumb, 0);
+ if (image != null)
+ {
+ try
+ {
+ dto.ParentThumbImageTag = _imageProcessor.GetImageCacheTag(librarySeries, image);
+ dto.ParentThumbItemId = librarySeries.Id.ToString("N");
+ }
+ catch (Exception ex)
+ {
+ }
+ }
+ image = librarySeries.GetImageInfo(ImageType.Backdrop, 0);
+ if (image != null)
+ {
+ try
+ {
+ dto.ParentBackdropImageTags = new List<string>
+ {
+ _imageProcessor.GetImageCacheTag(librarySeries, image)
+ };
+ dto.ParentBackdropItemId = librarySeries.Id.ToString("N");
+ }
+ catch (Exception ex)
+ {
+ }
+ }
+ }
+
+ if (!string.IsNullOrWhiteSpace(programSeriesId))
{
var program = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
- ExternalSeriesId = info.SeriesId,
+ ExternalSeriesId = programSeriesId,
Limit = 1,
ImageTypes = new ImageType[] { ImageType.Primary }
@@ -155,10 +209,117 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{
}
}
+
+ if (dto.ParentBackdropImageTags == null || dto.ParentBackdropImageTags.Count == 0)
+ {
+ image = program.GetImageInfo(ImageType.Backdrop, 0);
+ if (image != null)
+ {
+ try
+ {
+ dto.ParentBackdropImageTags = new List<string>
+ {
+ _imageProcessor.GetImageCacheTag(program, image)
+ };
+ dto.ParentBackdropItemId = program.Id.ToString("N");
+ }
+ catch (Exception ex)
+ {
+ }
+ }
+ }
}
}
+ }
- return dto;
+ private void FillImages(SeriesTimerInfoDto dto, string seriesName, string programSeriesId)
+ {
+ var librarySeries = _libraryManager.GetItemList(new InternalItemsQuery
+ {
+ IncludeItemTypes = new string[] { typeof(Series).Name },
+ Name = seriesName,
+ Limit = 1,
+ ImageTypes = new ImageType[] { ImageType.Thumb }
+
+ }).FirstOrDefault();
+
+ if (librarySeries != null)
+ {
+ var image = librarySeries.GetImageInfo(ImageType.Thumb, 0);
+ if (image != null)
+ {
+ try
+ {
+ dto.ParentThumbImageTag = _imageProcessor.GetImageCacheTag(librarySeries, image);
+ dto.ParentThumbItemId = librarySeries.Id.ToString("N");
+ }
+ catch (Exception ex)
+ {
+ }
+ }
+ image = librarySeries.GetImageInfo(ImageType.Backdrop, 0);
+ if (image != null)
+ {
+ try
+ {
+ dto.ParentBackdropImageTags = new List<string>
+ {
+ _imageProcessor.GetImageCacheTag(librarySeries, image)
+ };
+ dto.ParentBackdropItemId = librarySeries.Id.ToString("N");
+ }
+ catch (Exception ex)
+ {
+ }
+ }
+ }
+
+ if (!string.IsNullOrWhiteSpace(programSeriesId))
+ {
+ var program = _libraryManager.GetItemList(new InternalItemsQuery
+ {
+ IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
+ ExternalSeriesId = programSeriesId,
+ Limit = 1,
+ ImageTypes = new ImageType[] { ImageType.Primary }
+
+ }).FirstOrDefault();
+
+ 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.Count == 0)
+ {
+ image = program.GetImageInfo(ImageType.Backdrop, 0);
+ if (image != null)
+ {
+ try
+ {
+ dto.ParentBackdropImageTags = new List<string>
+ {
+ _imageProcessor.GetImageCacheTag(program, image)
+ };
+ dto.ParentBackdropItemId = program.Id.ToString("N");
+ }
+ catch (Exception ex)
+ {
+ }
+ }
+ }
+ }
+ }
}
public DayPattern? GetDayPattern(List<DayOfWeek> days)
@@ -269,6 +430,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv
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
@@ -304,7 +477,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
if (channel != null)
{
- info.ChannelId = channel.ExternalId;
+ info.ChannelId = GetItemExternalId(channel);
}
}
@@ -314,7 +487,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
if (program != null)
{
- info.ProgramId = program.ExternalId;
+ info.ProgramId = GetItemExternalId(program);
}
}
@@ -370,7 +543,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
if (channel != null)
{
- info.ChannelId = channel.ExternalId;
+ info.ChannelId = GetItemExternalId(channel);
}
}
@@ -380,7 +553,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
if (program != null)
{
- info.ProgramId = program.ExternalId;
+ info.ProgramId = GetItemExternalId(program);
}
}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index 902afb2003..265817cbe1 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -2,14 +2,12 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
-using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Sorting;
@@ -19,7 +17,6 @@ using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Serialization;
-using MoreLinq;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -27,17 +24,20 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
-using IniParser;
-using IniParser.Model;
+using MediaBrowser.Model.IO;
using MediaBrowser.Common.Events;
+using MediaBrowser.Common.IO;
using MediaBrowser.Common.Security;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Events;
-using MediaBrowser.Server.Implementations.LiveTv.Listings;
+using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Tasks;
+using Emby.Server.Implementations.LiveTv.Listings;
-namespace MediaBrowser.Server.Implementations.LiveTv
+namespace Emby.Server.Implementations.LiveTv
{
/// <summary>
/// Class LiveTvManager
@@ -73,6 +73,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCreated;
public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCreated;
+ public string GetEmbyTvActiveRecordingPath(string id)
+ {
+ return EmbyTV.EmbyTV.Current.GetActiveRecordingPath(id);
+ }
+
+ public Task<LiveStream> GetEmbyTvLiveStream(string id)
+ {
+ return EmbyTV.EmbyTV.Current.GetLiveStream(id);
+ }
+
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)
{
_config = config;
@@ -241,12 +251,24 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return await GetLiveStream(id, mediaSourceId, true, cancellationToken).ConfigureAwait(false);
}
+ private string GetItemExternalId(BaseItem item)
+ {
+ var externalId = item.ExternalId;
+
+ if (string.IsNullOrWhiteSpace(externalId))
+ {
+ externalId = item.GetProviderId("ProviderExternalId");
+ }
+
+ return externalId;
+ }
+
public async Task<IEnumerable<MediaSourceInfo>> GetRecordingMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
{
var baseItem = (BaseItem)item;
var service = GetService(baseItem);
- return await service.GetRecordingStreamMediaSources(baseItem.ExternalId, cancellationToken).ConfigureAwait(false);
+ return await service.GetRecordingStreamMediaSources(GetItemExternalId(baseItem), cancellationToken).ConfigureAwait(false);
}
public async Task<IEnumerable<MediaSourceInfo>> GetChannelMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
@@ -303,18 +325,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv
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, channel.ExternalId);
+ _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(channel.ExternalId, mediaSourceId, cancellationToken).ConfigureAwait(false);
+ var streamInfo = await supportsManagedStream.GetChannelStreamWithDirectStreamProvider(GetItemExternalId(channel), mediaSourceId, cancellationToken).ConfigureAwait(false);
info = streamInfo.Item1;
directStreamProvider = streamInfo.Item2;
}
else
{
- info = await service.GetChannelStream(channel.ExternalId, mediaSourceId, cancellationToken).ConfigureAwait(false);
+ info = await service.GetChannelStream(GetItemExternalId(channel), mediaSourceId, cancellationToken).ConfigureAwait(false);
}
info.RequiresClosing = true;
@@ -331,8 +353,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
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, recording.ExternalId);
- info = await service.GetRecordingStream(recording.ExternalId, null, cancellationToken).ConfigureAwait(false);
+ _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)
@@ -469,7 +491,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var id = _tvDtoService.GetInternalChannelId(serviceName, channelInfo.Id);
- var item = _itemRepo.RetrieveItem(id) as LiveTvChannel;
+ var item = _libraryManager.GetItemById(id) as LiveTvChannel;
if (item == null)
{
@@ -483,7 +505,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
isNew = true;
}
- if (!string.Equals(channelInfo.Id, item.ExternalId))
+ if (!string.Equals(channelInfo.Id, item.ExternalId, StringComparison.Ordinal))
{
isNew = true;
}
@@ -591,7 +613,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
item.EpisodeTitle = info.EpisodeTitle;
item.ExternalId = info.Id;
- item.ExternalSeriesIdLegacy = seriesId;
if (!string.IsNullOrWhiteSpace(seriesId) && !string.Equals(item.ExternalSeriesId, seriesId, StringComparison.Ordinal))
{
@@ -831,6 +852,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return item.Id;
}
+
+
+ private string GetExternalSeriesIdLegacy(BaseItem item)
+ {
+ return item.GetProviderId("ProviderExternalSeriesId");
+ }
+
public async Task<BaseItemDto> GetProgram(string id, CancellationToken cancellationToken, User user = null)
{
var program = GetInternalProgram(id);
@@ -838,7 +866,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var dto = _dtoService.GetBaseItemDto(program, new DtoOptions(), user);
var list = new List<Tuple<BaseItemDto, string, string, string>>();
- list.Add(new Tuple<BaseItemDto, string, string, string>(dto, program.ServiceName, program.ExternalId, program.ExternalSeriesIdLegacy));
+
+ var externalSeriesId = program.ExternalSeriesId;
+
+ if (string.IsNullOrWhiteSpace(externalSeriesId))
+ {
+ externalSeriesId = GetExternalSeriesIdLegacy(program);
+ }
+
+ list.Add(new Tuple<BaseItemDto, string, string, string>(dto, program.ServiceName, GetItemExternalId(program), externalSeriesId));
await AddRecordingInfo(list, cancellationToken).ConfigureAwait(false);
@@ -1195,6 +1231,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
if (coreService != null)
{
await coreService.RefreshSeriesTimers(cancellationToken, new Progress<double>()).ConfigureAwait(false);
+ await coreService.RefreshTimers(cancellationToken, new Progress<double>()).ConfigureAwait(false);
}
// Load these now which will prefetch metadata
@@ -1273,7 +1310,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var isKids = false;
var iSSeries = false;
- var channelPrograms = await service.GetProgramsAsync(currentChannel.ExternalId, start, end, cancellationToken).ConfigureAwait(false);
+ var channelPrograms = await service.GetProgramsAsync(GetItemExternalId(currentChannel), start, end, cancellationToken).ConfigureAwait(false);
var existingPrograms = _libraryManager.GetItemList(new InternalItemsQuery
{
@@ -1820,7 +1857,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv
dto.ServiceName = serviceName;
}
- programTuples.Add(new Tuple<BaseItemDto, string, string, string>(dto, serviceName, program.ExternalId, program.ExternalSeriesIdLegacy));
+ var externalSeriesId = program.ExternalSeriesId;
+
+ if (string.IsNullOrWhiteSpace(externalSeriesId))
+ {
+ externalSeriesId = GetExternalSeriesIdLegacy(program);
+ }
+
+ programTuples.Add(new Tuple<BaseItemDto, string, string, string>(dto, serviceName, GetItemExternalId(program), externalSeriesId));
}
await AddRecordingInfo(programTuples, CancellationToken.None).ConfigureAwait(false);
@@ -1965,6 +2009,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv
.Where(i => _tvDtoService.GetInternalSeriesTimerId(i.Item2.Name, i.Item1.SeriesTimerId) == guid);
}
+ if (!string.IsNullOrEmpty(query.Id))
+ {
+ var guid = new Guid(query.Id);
+
+ timers = timers
+ .Where(i => _tvDtoService.GetInternalTimerId(i.Item2.Name, i.Item1.Id) == guid);
+ }
+
var returnList = new List<TimerInfoDto>();
foreach (var i in timers)
@@ -1996,7 +2048,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
if (service is EmbyTV.EmbyTV)
{
// We can't trust that we'll be able to direct stream it through emby server, no matter what the provider says
- return service.DeleteRecordingAsync(recording.ExternalId, CancellationToken.None);
+ return service.DeleteRecordingAsync(GetItemExternalId(recording), CancellationToken.None);
}
return Task.FromResult(true);
@@ -2020,7 +2072,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
try
{
- await service.DeleteRecordingAsync(recording.ExternalId, CancellationToken.None).ConfigureAwait(false);
+ await service.DeleteRecordingAsync(GetItemExternalId(recording), CancellationToken.None).ConfigureAwait(false);
}
catch (ResourceNotFoundException)
{
@@ -2099,7 +2151,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public async Task<TimerInfoDto> GetTimer(string id, CancellationToken cancellationToken)
{
- var results = await GetTimers(new TimerQuery(), cancellationToken).ConfigureAwait(false);
+ var results = await GetTimers(new TimerQuery
+ {
+ Id = id
+
+ }, cancellationToken).ConfigureAwait(false);
return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
}
@@ -2279,12 +2335,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
programInfo = new ProgramInfo
{
Audio = program.Audio,
- ChannelId = channel.ExternalId,
+ ChannelId = GetItemExternalId(channel),
CommunityRating = program.CommunityRating,
EndDate = program.EndDate ?? DateTime.MinValue,
EpisodeTitle = program.EpisodeTitle,
Genres = program.Genres,
- Id = program.ExternalId,
+ Id = GetItemExternalId(program),
IsHD = program.IsHD,
IsKids = program.IsKids,
IsLive = program.IsLive,
@@ -2350,7 +2406,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
info.Name = program.Name;
info.Overview = program.Overview;
info.ProgramId = programDto.Id;
- info.ExternalProgramId = program.ExternalId;
+ info.ExternalProgramId = GetItemExternalId(program);
if (program.EndDate.HasValue)
{
@@ -2794,7 +2850,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public async Task<ListingsProviderInfo> SaveListingProvider(ListingsProviderInfo info, bool validateLogin, bool validateListings)
{
- info = _jsonSerializer.DeserializeFromString< ListingsProviderInfo>(_jsonSerializer.SerializeToString(info));
+ info = _jsonSerializer.DeserializeFromString<ListingsProviderInfo>(_jsonSerializer.SerializeToString(info));
var provider = _listingProviders.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase));
@@ -2955,43 +3011,46 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public List<NameValuePair> GetSatIniMappings()
{
- var names = GetType().Assembly.GetManifestResourceNames().Where(i => i.IndexOf("SatIp.ini", StringComparison.OrdinalIgnoreCase) != -1).ToList();
+ return new List<NameValuePair>();
+ //var names = GetType().Assembly.GetManifestResourceNames().Where(i => i.IndexOf("SatIp.ini", StringComparison.OrdinalIgnoreCase) != -1).ToList();
- return names.Select(GetSatIniMappings).Where(i => i != null).DistinctBy(i => i.Value.Split('|')[0]).ToList();
+ //return names.Select(GetSatIniMappings).Where(i => i != null).DistinctBy(i => i.Value.Split('|')[0]).ToList();
}
public NameValuePair GetSatIniMappings(string resource)
{
- using (var stream = GetType().Assembly.GetManifestResourceStream(resource))
- {
- using (var reader = new StreamReader(stream))
- {
- var parser = new StreamIniDataParser();
- IniData data = parser.ReadData(reader);
-
- var satType1 = data["SATTYPE"]["1"];
- var satType2 = data["SATTYPE"]["2"];
-
- if (string.IsNullOrWhiteSpace(satType2))
- {
- return null;
- }
-
- var srch = "SatIp.ini.";
- var filename = Path.GetFileName(resource);
-
- return new NameValuePair
- {
- Name = satType1 + " " + satType2,
- Value = satType2 + "|" + filename.Substring(filename.IndexOf(srch) + srch.Length)
- };
- }
- }
+ return new NameValuePair();
+ //using (var stream = GetType().Assembly.GetManifestResourceStream(resource))
+ //{
+ // using (var reader = new StreamReader(stream))
+ // {
+ // var parser = new StreamIniDataParser();
+ // IniData data = parser.ReadData(reader);
+
+ // var satType1 = data["SATTYPE"]["1"];
+ // var satType2 = data["SATTYPE"]["2"];
+
+ // if (string.IsNullOrWhiteSpace(satType2))
+ // {
+ // return null;
+ // }
+
+ // var srch = "SatIp.ini.";
+ // var filename = Path.GetFileName(resource);
+
+ // return new NameValuePair
+ // {
+ // Name = satType1 + " " + satType2,
+ // Value = satType2 + "|" + filename.Substring(filename.IndexOf(srch) + srch.Length)
+ // };
+ // }
+ //}
}
public Task<List<ChannelInfo>> GetSatChannelScanResult(TunerHostInfo info, CancellationToken cancellationToken)
{
- return new TunerHosts.SatIp.ChannelScan(_logger).Scan(info, cancellationToken);
+ return Task.FromResult(new List<ChannelInfo>());
+ //return new TunerHosts.SatIp.ChannelScan(_logger).Scan(info, cancellationToken);
}
public Task<List<ChannelInfo>> GetChannelsForListingsProvider(string id, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
index 393708fb7d..e0a35686ec 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
@@ -15,7 +15,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Dlna;
-namespace MediaBrowser.Server.Implementations.LiveTv
+namespace Emby.Server.Implementations.LiveTv
{
public class LiveTvMediaSourceProvider : IMediaSourceProvider
{
@@ -159,17 +159,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
private async Task AddMediaInfo(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken)
{
- var originalRuntime = mediaSource.RunTimeTicks;
-
mediaSource.DefaultSubtitleStreamIndex = null;
// Null this out so that it will be treated like a live stream
- if (!originalRuntime.HasValue)
- {
- mediaSource.RunTimeTicks = null;
- }
+ mediaSource.RunTimeTicks = null;
- var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == Model.Entities.MediaStreamType.Audio);
+ var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Audio);
if (audioStream == null || audioStream.Index == -1)
{
@@ -180,7 +175,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
mediaSource.DefaultAudioStreamIndex = audioStream.Index;
}
- var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == Model.Entities.MediaStreamType.Video);
+ var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Video);
if (videoStream != null)
{
if (!videoStream.BitRate.HasValue)
diff --git a/MediaBrowser.Server.Implementations/LiveTv/ProgramImageProvider.cs b/Emby.Server.Implementations/LiveTv/ProgramImageProvider.cs
index 3f0538bd0b..5a0389b16a 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/ProgramImageProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/ProgramImageProvider.cs
@@ -8,7 +8,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-namespace MediaBrowser.Server.Implementations.LiveTv
+namespace Emby.Server.Implementations.LiveTv
{
public class ProgramImageProvider : IDynamicImageProvider, IHasItemChangeMonitor, IHasOrder
{
@@ -24,6 +24,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return new[] { ImageType.Primary };
}
+ private string GetItemExternalId(BaseItem item)
+ {
+ var externalId = item.ExternalId;
+
+ if (string.IsNullOrWhiteSpace(externalId))
+ {
+ externalId = item.GetProviderId("ProviderExternalId");
+ }
+
+ return externalId;
+ }
+
public async Task<DynamicImageResponse> GetImage(IHasImages item, ImageType type, CancellationToken cancellationToken)
{
var liveTvItem = (LiveTvProgram)item;
@@ -38,7 +50,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{
var channel = _liveTvManager.GetInternalChannel(liveTvItem.ChannelId);
- var response = await service.GetProgramImageAsync(liveTvItem.ExternalId, channel.ExternalId, cancellationToken).ConfigureAwait(false);
+ var response = await service.GetProgramImageAsync(GetItemExternalId(liveTvItem), GetItemExternalId(channel), cancellationToken).ConfigureAwait(false);
if (response != null)
{
diff --git a/MediaBrowser.Server.Implementations/LiveTv/RecordingImageProvider.cs b/Emby.Server.Implementations/LiveTv/RecordingImageProvider.cs
index 25678c29d3..47663bdbc5 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/RecordingImageProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/RecordingImageProvider.cs
@@ -8,7 +8,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-namespace MediaBrowser.Server.Implementations.LiveTv
+namespace Emby.Server.Implementations.LiveTv
{
public class RecordingImageProvider : IDynamicImageProvider, IHasItemChangeMonitor
{
diff --git a/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs
index 3fb1d96614..f2806292d0 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs
+++ b/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs
@@ -1,15 +1,15 @@
using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.LiveTv;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Server.Implementations.LiveTv
+namespace Emby.Server.Implementations.LiveTv
{
- public class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask, IHasKey
+ public class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask
{
private readonly ILiveTvManager _liveTvManager;
private readonly IConfigurationManager _config;
@@ -42,11 +42,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return manager.RefreshChannels(progress, cancellationToken);
}
- public IEnumerable<ITaskTrigger> GetDefaultTriggers()
+ /// <summary>
+ /// Creates the triggers that define when the task will run
+ /// </summary>
+ /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
+ public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- return new ITaskTrigger[]
- {
- new IntervalTrigger{ Interval = TimeSpan.FromHours(12)}
+ return new[] {
+
+ // Every so often
+ new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(12).Ticks}
};
}
@@ -65,6 +70,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv
get { return true; }
}
+ public bool IsLogged
+ {
+ get { return true; }
+ }
+
public string Key
{
get { return "RefreshGuide"; }
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
index 0fe74798f5..5fae3f666b 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
@@ -15,7 +15,7 @@ using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Serialization;
-namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
+namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
public abstract class BaseTunerHost
{
@@ -101,6 +101,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
public async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken)
{
+ if (string.IsNullOrWhiteSpace(channelId))
+ {
+ throw new ArgumentNullException("channelId");
+ }
+
if (IsValidChannelId(channelId))
{
var hosts = GetTunerHosts();
@@ -161,6 +166,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
public async Task<LiveStream> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken)
{
+ if (string.IsNullOrWhiteSpace(channelId))
+ {
+ throw new ArgumentNullException("channelId");
+ }
+
if (!IsValidChannelId(channelId))
{
throw new FileNotFoundException();
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs
index cd168ba580..f2e48fbc0f 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs
@@ -10,10 +10,11 @@ using System;
using System.Linq;
using System.Threading;
using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Serialization;
-namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
+namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
public class HdHomerunDiscovery : IServerEntryPoint
{
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index 97d52836d3..77efe8585b 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -14,15 +14,17 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Net;
-namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
+namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
public class HdHomerunHost : BaseTunerHost, ITunerHost, IConfigurableTunerHost
{
@@ -57,7 +59,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private string GetChannelId(TunerHostInfo info, Channels i)
{
- var id = ChannelIdPrefix + i.GuideNumber.ToString(CultureInfo.InvariantCulture);
+ var id = ChannelIdPrefix + i.GuideNumber;
if (info.DataVersion >= 1)
{
@@ -95,7 +97,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
return lineup.Select(i => new ChannelInfo
{
Name = i.GuideName,
- Number = i.GuideNumber.ToString(CultureInfo.InvariantCulture),
+ Number = i.GuideNumber,
Id = GetChannelId(info, i),
IsFavorite = i.Favorite,
TunerHostId = info.Id,
@@ -375,7 +377,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
var url = GetApiUrl(info, true) + "/auto/v" + channelId;
- if (!string.IsNullOrWhiteSpace(profile) && !string.Equals(profile, "native", StringComparison.OrdinalIgnoreCase))
+ // If raw was used, the tuner doesn't support params
+ if (!string.IsNullOrWhiteSpace(profile)
+ && !string.Equals(profile, "native", StringComparison.OrdinalIgnoreCase))
{
url += "?transcode=" + profile;
}
@@ -449,16 +453,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
var hdhrId = GetHdHrIdFromChannelId(channelId);
- list.Add(await GetMediaSource(info, hdhrId, "native").ConfigureAwait(false));
-
try
{
- if (info.AllowHWTranscoding)
+ var model = await GetModelInfo(info, cancellationToken).ConfigureAwait(false);
+ model = model ?? string.Empty;
+
+ if ((model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1))
{
- string model = await GetModelInfo(info, cancellationToken).ConfigureAwait(false);
- model = model ?? string.Empty;
+ list.Add(await GetMediaSource(info, hdhrId, "native").ConfigureAwait(false));
- if ((model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1))
+ if (info.AllowHWTranscoding)
{
list.Add(await GetMediaSource(info, hdhrId, "heavy").ConfigureAwait(false));
@@ -475,6 +479,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
+ if (list.Count == 0)
+ {
+ list.Add(await GetMediaSource(info, hdhrId, "native").ConfigureAwait(false));
+ }
+
return list;
}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs
index 91f0ee832f..4852270d5e 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs
@@ -2,7 +2,7 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.LiveTv;
@@ -10,12 +10,8 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Server.Implementations.LiveTv.EmbyTV;
-using System.Collections.Generic;
-using System.Linq;
-using MediaBrowser.Common.Extensions;
-namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
+namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
public class HdHomerunLiveStream : LiveStream, IDirectStreamProvider
{
@@ -93,7 +89,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
Url = url,
CancellationToken = cancellationToken,
- BufferContent = false
+ BufferContent = false,
+
+ // Increase a little bit
+ TimeoutMs = 30000
}, "GET").ConfigureAwait(false))
{
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
index 48117f2251..8027ce2dd7 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
@@ -11,15 +11,16 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Server.Implementations.LiveTv.EmbyTV;
-namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
+namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
public class M3UTunerHost : BaseTunerHost, ITunerHost, IConfigurableTunerHost
{
@@ -86,6 +87,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
protected override bool IsValidChannelId(string channelId)
{
+ if (string.IsNullOrWhiteSpace(channelId))
+ {
+ throw new ArgumentNullException("channelId");
+ }
+
return channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase);
}
@@ -143,8 +149,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
}
},
- RequiresOpening = false,
- RequiresClosing = false,
+ RequiresOpening = true,
+ RequiresClosing = true,
ReadAtNativeFramerate = false,
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
new file mode 100644
index 0000000000..e0f0402813
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
@@ -0,0 +1,276 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Model.Logging;
+
+namespace Emby.Server.Implementations.LiveTv.TunerHosts
+{
+ public class M3uParser
+ {
+ private readonly ILogger _logger;
+ private readonly IFileSystem _fileSystem;
+ private readonly IHttpClient _httpClient;
+ private readonly IServerApplicationHost _appHost;
+
+ public M3uParser(ILogger logger, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost)
+ {
+ _logger = logger;
+ _fileSystem = fileSystem;
+ _httpClient = httpClient;
+ _appHost = appHost;
+ }
+
+ public async Task<List<M3UChannel>> Parse(string url, string channelIdPrefix, string tunerHostId, CancellationToken cancellationToken)
+ {
+ var urlHash = url.GetMD5().ToString("N");
+
+ // Read the file and display it line by line.
+ using (var reader = new StreamReader(await GetListingsStream(url, cancellationToken).ConfigureAwait(false)))
+ {
+ return GetChannels(reader, urlHash, channelIdPrefix, tunerHostId);
+ }
+ }
+
+ public Task<Stream> GetListingsStream(string url, CancellationToken cancellationToken)
+ {
+ if (url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
+ {
+ return _httpClient.Get(new HttpRequestOptions
+ {
+ Url = url,
+ CancellationToken = cancellationToken,
+ // Some data providers will require a user agent
+ UserAgent = _appHost.FriendlyName + "/" + _appHost.ApplicationVersion
+ });
+ }
+ return Task.FromResult(_fileSystem.OpenRead(url));
+ }
+
+ const string ExtInfPrefix = "#EXTINF:";
+ private List<M3UChannel> GetChannels(StreamReader reader, string urlHash, string channelIdPrefix, string tunerHostId)
+ {
+ var channels = new List<M3UChannel>();
+ string line;
+ string extInf = "";
+ while ((line = reader.ReadLine()) != null)
+ {
+ line = line.Trim();
+ if (string.IsNullOrWhiteSpace(line))
+ {
+ continue;
+ }
+
+ if (line.StartsWith("#EXTM3U", StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
+ if (line.StartsWith(ExtInfPrefix, StringComparison.OrdinalIgnoreCase))
+ {
+ extInf = line.Substring(ExtInfPrefix.Length).Trim();
+ _logger.Info("Found m3u channel: {0}", extInf);
+ }
+ else if (!string.IsNullOrWhiteSpace(extInf) && !line.StartsWith("#", StringComparison.OrdinalIgnoreCase))
+ {
+ var channel = GetChannelnfo(extInf, tunerHostId, line);
+ channel.Id = channelIdPrefix + urlHash + line.GetMD5().ToString("N");
+ channel.Path = line;
+ channels.Add(channel);
+ extInf = "";
+ }
+ }
+ return channels;
+ }
+
+ private M3UChannel GetChannelnfo(string extInf, string tunerHostId, string mediaUrl)
+ {
+ var channel = new M3UChannel();
+ channel.TunerHostId = tunerHostId;
+
+ extInf = extInf.Trim();
+
+ string remaining;
+ var attributes = ParseExtInf(extInf, out remaining);
+ extInf = remaining;
+
+ string value;
+ if (attributes.TryGetValue("tvg-logo", out value))
+ {
+ channel.ImageUrl = value;
+ }
+
+ channel.Name = GetChannelName(extInf, attributes);
+ channel.Number = GetChannelNumber(extInf, attributes, mediaUrl);
+
+ return channel;
+ }
+
+ private string GetChannelNumber(string extInf, Dictionary<string, string> attributes, string mediaUrl)
+ {
+ var nameParts = extInf.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+ var nameInExtInf = nameParts.Length > 1 ? nameParts.Last().Trim() : null;
+
+ var numberString = nameParts[0];
+
+ //Check for channel number with the format from SatIp
+ int number;
+ if (!string.IsNullOrWhiteSpace(nameInExtInf))
+ {
+ var numberIndex = nameInExtInf.IndexOf('.');
+ if (numberIndex > 0)
+ {
+ if (int.TryParse(nameInExtInf.Substring(0, numberIndex), out number))
+ {
+ numberString = number.ToString();
+ }
+ }
+ }
+
+ if (!string.IsNullOrWhiteSpace(numberString))
+ {
+ numberString = numberString.Trim();
+ }
+
+ if (string.IsNullOrWhiteSpace(numberString) ||
+ string.Equals(numberString, "-1", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(numberString, "0", StringComparison.OrdinalIgnoreCase))
+ {
+ string value;
+ if (attributes.TryGetValue("tvg-id", out value))
+ {
+ numberString = value;
+ }
+ }
+
+ if (!string.IsNullOrWhiteSpace(numberString))
+ {
+ numberString = numberString.Trim();
+ }
+
+ if (string.IsNullOrWhiteSpace(numberString) ||
+ string.Equals(numberString, "-1", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(numberString, "0", StringComparison.OrdinalIgnoreCase))
+ {
+ string value;
+ if (attributes.TryGetValue("channel-id", out value))
+ {
+ numberString = value;
+ }
+ }
+
+ if (!string.IsNullOrWhiteSpace(numberString))
+ {
+ numberString = numberString.Trim();
+ }
+
+ if (string.IsNullOrWhiteSpace(numberString) ||
+ string.Equals(numberString, "-1", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(numberString, "0", StringComparison.OrdinalIgnoreCase))
+ {
+ numberString = null;
+ }
+
+ if (string.IsNullOrWhiteSpace(numberString))
+ {
+ if (string.IsNullOrWhiteSpace(mediaUrl))
+ {
+ numberString = null;
+ }
+ else
+ {
+ numberString = Path.GetFileNameWithoutExtension(mediaUrl.Split('/').Last());
+
+ double value;
+ if (!double.TryParse(numberString, NumberStyles.Any, CultureInfo.InvariantCulture, out value))
+ {
+ numberString = null;
+ }
+ }
+ }
+
+ return numberString;
+ }
+
+ private string GetChannelName(string extInf, Dictionary<string, string> attributes)
+ {
+ var nameParts = extInf.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+ var nameInExtInf = nameParts.Length > 1 ? nameParts.Last().Trim() : null;
+
+ //Check for channel number with the format from SatIp
+ int number;
+ if (!string.IsNullOrWhiteSpace(nameInExtInf))
+ {
+ var numberIndex = nameInExtInf.IndexOf('.');
+ if (numberIndex > 0)
+ {
+ if (int.TryParse(nameInExtInf.Substring(0, numberIndex), out number))
+ {
+ //channel.Number = number.ToString();
+ nameInExtInf = nameInExtInf.Substring(numberIndex + 1);
+ }
+ }
+ }
+
+ string name;
+ attributes.TryGetValue("tvg-name", out name);
+
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ name = nameInExtInf;
+ }
+
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ attributes.TryGetValue("tvg-id", out name);
+ }
+
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ name = null;
+ }
+
+ return name;
+ }
+
+ private Dictionary<string, string> ParseExtInf(string line, out string remaining)
+ {
+ var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+ var reg = new Regex(@"([a-z0-9\-_]+)=\""([^""]+)\""", RegexOptions.IgnoreCase);
+ var matches = reg.Matches(line);
+ var minIndex = int.MaxValue;
+ foreach (Match match in matches)
+ {
+ dict[match.Groups[1].Value] = match.Groups[2].Value;
+ minIndex = Math.Min(minIndex, match.Index);
+ }
+
+ if (minIndex > 0 && minIndex < line.Length)
+ {
+ line = line.Substring(0, minIndex);
+ }
+
+ remaining = line;
+
+ return dict;
+ }
+ }
+
+
+ public class M3UChannel : ChannelInfo
+ {
+ public string Path { get; set; }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs
index 8ff3fd6c17..7b88be19c1 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs
@@ -7,7 +7,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Logging;
-namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
+namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
public class MulticastStream
{
@@ -25,10 +25,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
{
_cancellationToken = cancellationToken;
+ byte[] buffer = new byte[BufferSize];
+
while (!cancellationToken.IsCancellationRequested)
{
- byte[] buffer = new byte[BufferSize];
-
var bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
if (bytesRead > 0)
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs
index c1566b9006..bd6f319062 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs
@@ -8,7 +8,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Logging;
-namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
+namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
public class QueueStream
{
@@ -19,6 +19,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
public Action<QueueStream> OnFinished { get; set; }
private readonly ILogger _logger;
+ private bool _isActive;
public QueueStream(Stream outputStream, ILogger logger)
{
@@ -29,7 +30,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
public void Queue(byte[] bytes)
{
- _queue.Enqueue(bytes);
+ if (_isActive)
+ {
+ _queue.Enqueue(bytes);
+ }
}
public void Start(CancellationToken cancellationToken)
@@ -57,6 +61,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
{
while (!cancellationToken.IsCancellationRequested)
{
+ _isActive = true;
+
var bytes = Dequeue();
if (bytes != null)
{
@@ -83,6 +89,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
}
finally
{
+ _isActive = false;
+
if (OnFinished != null)
{
OnFinished(this);
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json
index 28977c4f92..28977c4f92 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/ar.json
+++ b/Emby.Server.Implementations/Localization/Core/ar.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/bg-BG.json b/Emby.Server.Implementations/Localization/Core/bg-BG.json
index 22b99408df..22b99408df 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/bg-BG.json
+++ b/Emby.Server.Implementations/Localization/Core/bg-BG.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json
index 7ca8e1553d..7ca8e1553d 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/ca.json
+++ b/Emby.Server.Implementations/Localization/Core/ca.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/core.json b/Emby.Server.Implementations/Localization/Core/core.json
index 976faa8cbc..976faa8cbc 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/core.json
+++ b/Emby.Server.Implementations/Localization/Core/core.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/cs.json b/Emby.Server.Implementations/Localization/Core/cs.json
index e3055f5bac..e3055f5bac 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/cs.json
+++ b/Emby.Server.Implementations/Localization/Core/cs.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json
index d2a628a809..d2a628a809 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/da.json
+++ b/Emby.Server.Implementations/Localization/Core/da.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json
index 30e3d9215e..30e3d9215e 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/de.json
+++ b/Emby.Server.Implementations/Localization/Core/de.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json
index 9e2d321cc4..9e2d321cc4 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/el.json
+++ b/Emby.Server.Implementations/Localization/Core/el.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/en-GB.json b/Emby.Server.Implementations/Localization/Core/en-GB.json
index 493c6c4e99..493c6c4e99 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/en-GB.json
+++ b/Emby.Server.Implementations/Localization/Core/en-GB.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json
index bc0dc236d4..bc0dc236d4 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/en-US.json
+++ b/Emby.Server.Implementations/Localization/Core/en-US.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/es-AR.json b/Emby.Server.Implementations/Localization/Core/es-AR.json
index 0555aa9d9c..0555aa9d9c 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/es-AR.json
+++ b/Emby.Server.Implementations/Localization/Core/es-AR.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/es-MX.json b/Emby.Server.Implementations/Localization/Core/es-MX.json
index 630c7a0379..630c7a0379 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/es-MX.json
+++ b/Emby.Server.Implementations/Localization/Core/es-MX.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json
index d1a56240dd..d1a56240dd 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/es.json
+++ b/Emby.Server.Implementations/Localization/Core/es.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/fi.json b/Emby.Server.Implementations/Localization/Core/fi.json
index 20efa14067..20efa14067 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/fi.json
+++ b/Emby.Server.Implementations/Localization/Core/fi.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/fr-CA.json b/Emby.Server.Implementations/Localization/Core/fr-CA.json
index 789817c843..789817c843 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/fr-CA.json
+++ b/Emby.Server.Implementations/Localization/Core/fr-CA.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json
index 25c722989c..25c722989c 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/fr.json
+++ b/Emby.Server.Implementations/Localization/Core/fr.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/gsw.json b/Emby.Server.Implementations/Localization/Core/gsw.json
index 88af82b7e4..88af82b7e4 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/gsw.json
+++ b/Emby.Server.Implementations/Localization/Core/gsw.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json
index 137b455441..137b455441 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/he.json
+++ b/Emby.Server.Implementations/Localization/Core/he.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/hr.json b/Emby.Server.Implementations/Localization/Core/hr.json
index 7a94dc32b7..7a94dc32b7 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/hr.json
+++ b/Emby.Server.Implementations/Localization/Core/hr.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json
index 2b9d28d8c0..2b9d28d8c0 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/hu.json
+++ b/Emby.Server.Implementations/Localization/Core/hu.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/id.json b/Emby.Server.Implementations/Localization/Core/id.json
index 8d64b63c46..8d64b63c46 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/id.json
+++ b/Emby.Server.Implementations/Localization/Core/id.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json
index d2d697c3ee..d2d697c3ee 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/it.json
+++ b/Emby.Server.Implementations/Localization/Core/it.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/kk.json b/Emby.Server.Implementations/Localization/Core/kk.json
index 93252c30b8..93252c30b8 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/kk.json
+++ b/Emby.Server.Implementations/Localization/Core/kk.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json
index 834ccc17bc..834ccc17bc 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/ko.json
+++ b/Emby.Server.Implementations/Localization/Core/ko.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/ms.json b/Emby.Server.Implementations/Localization/Core/ms.json
index fe5eef894d..fe5eef894d 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/ms.json
+++ b/Emby.Server.Implementations/Localization/Core/ms.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json
index 315d49b5fd..315d49b5fd 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/nb.json
+++ b/Emby.Server.Implementations/Localization/Core/nb.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json
index 2818fbf6a6..2818fbf6a6 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/nl.json
+++ b/Emby.Server.Implementations/Localization/Core/nl.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/pl.json b/Emby.Server.Implementations/Localization/Core/pl.json
index cdaa87c4d8..cdaa87c4d8 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/pl.json
+++ b/Emby.Server.Implementations/Localization/Core/pl.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json
index 67f204b2ee..67f204b2ee 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/pt-BR.json
+++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/pt-PT.json b/Emby.Server.Implementations/Localization/Core/pt-PT.json
index f12939b102..f12939b102 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/pt-PT.json
+++ b/Emby.Server.Implementations/Localization/Core/pt-PT.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/ro.json b/Emby.Server.Implementations/Localization/Core/ro.json
index c58df27d57..c58df27d57 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/ro.json
+++ b/Emby.Server.Implementations/Localization/Core/ro.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json
index 62fe3b4964..62fe3b4964 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/ru.json
+++ b/Emby.Server.Implementations/Localization/Core/ru.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/sl-SI.json b/Emby.Server.Implementations/Localization/Core/sl-SI.json
index 0631e3fa88..0631e3fa88 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/sl-SI.json
+++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json
index 4a6565affe..4a6565affe 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/sv.json
+++ b/Emby.Server.Implementations/Localization/Core/sv.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json
index a691e9d025..a691e9d025 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/tr.json
+++ b/Emby.Server.Implementations/Localization/Core/tr.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/uk.json b/Emby.Server.Implementations/Localization/Core/uk.json
index 0dc6afe8a2..0dc6afe8a2 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/uk.json
+++ b/Emby.Server.Implementations/Localization/Core/uk.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/vi.json b/Emby.Server.Implementations/Localization/Core/vi.json
index 6ea1d1d3fd..6ea1d1d3fd 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/vi.json
+++ b/Emby.Server.Implementations/Localization/Core/vi.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json
index 580832a9ea..580832a9ea 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/zh-CN.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json
index a70e7a0035..a70e7a0035 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/zh-HK.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json
diff --git a/MediaBrowser.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json
index b711aab1f2..b711aab1f2 100644
--- a/MediaBrowser.Server.Implementations/Localization/Core/zh-TW.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json
diff --git a/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs
index ec544dd70c..120f445c22 100644
--- a/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs
+++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs
@@ -1,6 +1,5 @@
using MediaBrowser.Model.Extensions;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Serialization;
@@ -10,11 +9,11 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
-using System.Reflection;
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Reflection;
-namespace MediaBrowser.Server.Implementations.Localization
+namespace Emby.Server.Implementations.Localization
{
/// <summary>
/// Class LocalizationManager
@@ -37,6 +36,8 @@ namespace MediaBrowser.Server.Implementations.Localization
private readonly IFileSystem _fileSystem;
private readonly IJsonSerializer _jsonSerializer;
private readonly ILogger _logger;
+ private readonly IAssemblyInfo _assemblyInfo;
+ private readonly ITextLocalizer _textLocalizer;
/// <summary>
/// Initializes a new instance of the <see cref="LocalizationManager" /> class.
@@ -44,12 +45,14 @@ namespace MediaBrowser.Server.Implementations.Localization
/// <param name="configurationManager">The configuration manager.</param>
/// <param name="fileSystem">The file system.</param>
/// <param name="jsonSerializer">The json serializer.</param>
- public LocalizationManager(IServerConfigurationManager configurationManager, IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger)
+ public LocalizationManager(IServerConfigurationManager configurationManager, IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, IAssemblyInfo assemblyInfo, ITextLocalizer textLocalizer)
{
_configurationManager = configurationManager;
_fileSystem = fileSystem;
_jsonSerializer = jsonSerializer;
_logger = logger;
+ _assemblyInfo = assemblyInfo;
+ _textLocalizer = textLocalizer;
ExtractAll();
}
@@ -63,38 +66,46 @@ namespace MediaBrowser.Server.Implementations.Localization
_fileSystem.CreateDirectory(localizationPath);
- var existingFiles = Directory.EnumerateFiles(localizationPath, "ratings-*.txt", SearchOption.TopDirectoryOnly)
+ var existingFiles = GetRatingsFiles(localizationPath)
.Select(Path.GetFileName)
.ToList();
// Extract from the assembly
- foreach (var resource in type.Assembly
- .GetManifestResourceNames()
+ foreach (var resource in _assemblyInfo
+ .GetManifestResourceNames(type)
.Where(i => i.StartsWith(resourcePath)))
{
var filename = "ratings-" + resource.Substring(resourcePath.Length);
if (!existingFiles.Contains(filename))
{
- using (var stream = type.Assembly.GetManifestResourceStream(resource))
+ using (var stream = _assemblyInfo.GetManifestResourceStream(type, resource))
{
var target = Path.Combine(localizationPath, filename);
_logger.Info("Extracting ratings to {0}", target);
- using (var fs = _fileSystem.GetFileStream(target, FileMode.Create, FileAccess.Write, FileShare.Read))
+ using (var fs = _fileSystem.GetFileStream(target, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
{
stream.CopyTo(fs);
}
}
}
}
-
- foreach (var file in Directory.EnumerateFiles(localizationPath, "ratings-*.txt", SearchOption.TopDirectoryOnly))
+
+ foreach (var file in GetRatingsFiles(localizationPath))
{
LoadRatings(file);
}
}
+ private List<string> GetRatingsFiles(string directory)
+ {
+ return _fileSystem.GetFilePaths(directory, false)
+ .Where(i => string.Equals(Path.GetExtension(i), ".txt", StringComparison.OrdinalIgnoreCase))
+ .Where(i => Path.GetFileName(i).StartsWith("ratings-", StringComparison.OrdinalIgnoreCase))
+ .ToList();
+ }
+
/// <summary>
/// Gets the localization path.
/// </summary>
@@ -107,6 +118,16 @@ namespace MediaBrowser.Server.Implementations.Localization
}
}
+ public string RemoveDiacritics(string text)
+ {
+ return _textLocalizer.RemoveDiacritics(text);
+ }
+
+ public string NormalizeFormKD(string text)
+ {
+ return _textLocalizer.NormalizeFormKD(text);
+ }
+
/// <summary>
/// Gets the cultures.
/// </summary>
@@ -118,7 +139,7 @@ namespace MediaBrowser.Server.Implementations.Localization
var list = new List<CultureDto>();
- using (var stream = type.Assembly.GetManifestResourceStream(path))
+ using (var stream = _assemblyInfo.GetManifestResourceStream(type, path))
{
using (var reader = new StreamReader(stream))
{
@@ -160,7 +181,7 @@ namespace MediaBrowser.Server.Implementations.Localization
var type = GetType();
var path = type.Namespace + ".countries.json";
- using (var stream = type.Assembly.GetManifestResourceStream(path))
+ using (var stream = _assemblyInfo.GetManifestResourceStream(type, path))
{
return _jsonSerializer.DeserializeFromStream<List<CountryInfo>>(stream);
}
@@ -218,7 +239,7 @@ namespace MediaBrowser.Server.Implementations.Localization
/// <returns>Dictionary{System.StringParentalRating}.</returns>
private void LoadRatings(string file)
{
- var dict = File.ReadAllLines(file).Select(i =>
+ var dict = _fileSystem.ReadAllLines(file).Select(i =>
{
if (!string.IsNullOrWhiteSpace(i))
{
@@ -321,18 +342,17 @@ namespace MediaBrowser.Server.Implementations.Localization
{
var dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- var assembly = GetType().Assembly;
var namespaceName = GetType().Namespace + "." + prefix;
- CopyInto(dictionary, namespaceName + "." + baseFilename, assembly);
- CopyInto(dictionary, namespaceName + "." + GetResourceFilename(culture), assembly);
+ CopyInto(dictionary, namespaceName + "." + baseFilename);
+ CopyInto(dictionary, namespaceName + "." + GetResourceFilename(culture));
return dictionary;
}
- private void CopyInto(IDictionary<string, string> dictionary, string resourcePath, Assembly assembly)
+ private void CopyInto(IDictionary<string, string> dictionary, string resourcePath)
{
- using (var stream = assembly.GetManifestResourceStream(resourcePath))
+ using (var stream = _assemblyInfo.GetManifestResourceStream(GetType(), resourcePath))
{
if (stream != null)
{
@@ -403,4 +423,11 @@ namespace MediaBrowser.Server.Implementations.Localization
}.OrderBy(i => i.Name);
}
}
+
+ public interface ITextLocalizer
+ {
+ string RemoveDiacritics(string text);
+
+ string NormalizeFormKD(string text);
+ }
}
diff --git a/MediaBrowser.Server.Implementations/Localization/Ratings/au.txt b/Emby.Server.Implementations/Localization/Ratings/au.txt
index fa60f53055..fa60f53055 100644
--- a/MediaBrowser.Server.Implementations/Localization/Ratings/au.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/au.txt
diff --git a/MediaBrowser.Server.Implementations/Localization/Ratings/be.txt b/Emby.Server.Implementations/Localization/Ratings/be.txt
index 99a53f664a..99a53f664a 100644
--- a/MediaBrowser.Server.Implementations/Localization/Ratings/be.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/be.txt
diff --git a/MediaBrowser.Server.Implementations/Localization/Ratings/br.txt b/Emby.Server.Implementations/Localization/Ratings/br.txt
index 62f00fb87e..62f00fb87e 100644
--- a/MediaBrowser.Server.Implementations/Localization/Ratings/br.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/br.txt
diff --git a/MediaBrowser.Server.Implementations/Localization/Ratings/ca.txt b/Emby.Server.Implementations/Localization/Ratings/ca.txt
index 5a110648cd..5a110648cd 100644
--- a/MediaBrowser.Server.Implementations/Localization/Ratings/ca.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/ca.txt
diff --git a/MediaBrowser.Server.Implementations/Localization/Ratings/co.txt b/Emby.Server.Implementations/Localization/Ratings/co.txt
index a694a0be66..a694a0be66 100644
--- a/MediaBrowser.Server.Implementations/Localization/Ratings/co.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/co.txt
diff --git a/MediaBrowser.Server.Implementations/Localization/Ratings/de.txt b/Emby.Server.Implementations/Localization/Ratings/de.txt
index ad1f186197..ad1f186197 100644
--- a/MediaBrowser.Server.Implementations/Localization/Ratings/de.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/de.txt
diff --git a/MediaBrowser.Server.Implementations/Localization/Ratings/dk.txt b/Emby.Server.Implementations/Localization/Ratings/dk.txt
index b9a085e012..b9a085e012 100644
--- a/MediaBrowser.Server.Implementations/Localization/Ratings/dk.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/dk.txt
diff --git a/MediaBrowser.Server.Implementations/Localization/Ratings/fr.txt b/Emby.Server.Implementations/Localization/Ratings/fr.txt
index 2bb205b0da..2bb205b0da 100644
--- a/MediaBrowser.Server.Implementations/Localization/Ratings/fr.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/fr.txt
diff --git a/MediaBrowser.Server.Implementations/Localization/Ratings/gb.txt b/Emby.Server.Implementations/Localization/Ratings/gb.txt
index c1f7d04529..c1f7d04529 100644
--- a/MediaBrowser.Server.Implementations/Localization/Ratings/gb.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/gb.txt
diff --git a/MediaBrowser.Server.Implementations/Localization/Ratings/ie.txt b/Emby.Server.Implementations/Localization/Ratings/ie.txt
index 283f077672..283f077672 100644
--- a/MediaBrowser.Server.Implementations/Localization/Ratings/ie.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/ie.txt
diff --git a/MediaBrowser.Server.Implementations/Localization/Ratings/jp.txt b/Emby.Server.Implementations/Localization/Ratings/jp.txt
index 2e1da30d81..2e1da30d81 100644
--- a/MediaBrowser.Server.Implementations/Localization/Ratings/jp.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/jp.txt
diff --git a/MediaBrowser.Server.Implementations/Localization/Ratings/kz.txt b/Emby.Server.Implementations/Localization/Ratings/kz.txt
index b31e12d969..b31e12d969 100644
--- a/MediaBrowser.Server.Implementations/Localization/Ratings/kz.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/kz.txt
diff --git a/MediaBrowser.Server.Implementations/Localization/Ratings/mx.txt b/Emby.Server.Implementations/Localization/Ratings/mx.txt
index 93b609c3d9..93b609c3d9 100644
--- a/MediaBrowser.Server.Implementations/Localization/Ratings/mx.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/mx.txt
diff --git a/MediaBrowser.Server.Implementations/Localization/Ratings/nl.txt b/Emby.Server.Implementations/Localization/Ratings/nl.txt
index f69cc2bcc9..f69cc2bcc9 100644
--- a/MediaBrowser.Server.Implementations/Localization/Ratings/nl.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/nl.txt
diff --git a/MediaBrowser.Server.Implementations/Localization/Ratings/nz.txt b/Emby.Server.Implementations/Localization/Ratings/nz.txt
index bc761dcab0..bc761dcab0 100644
--- a/MediaBrowser.Server.Implementations/Localization/Ratings/nz.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/nz.txt
diff --git a/MediaBrowser.Server.Implementations/Localization/Ratings/ru.txt b/Emby.Server.Implementations/Localization/Ratings/ru.txt
index 1bc94affd6..1bc94affd6 100644
--- a/MediaBrowser.Server.Implementations/Localization/Ratings/ru.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/ru.txt
diff --git a/MediaBrowser.Server.Implementations/Localization/Ratings/us.txt b/Emby.Server.Implementations/Localization/Ratings/us.txt
index 3f5311e0ea..3f5311e0ea 100644
--- a/MediaBrowser.Server.Implementations/Localization/Ratings/us.txt
+++ b/Emby.Server.Implementations/Localization/Ratings/us.txt
diff --git a/MediaBrowser.Server.Implementations/Localization/countries.json b/Emby.Server.Implementations/Localization/countries.json
index e671b36853..e671b36853 100644
--- a/MediaBrowser.Server.Implementations/Localization/countries.json
+++ b/Emby.Server.Implementations/Localization/countries.json
diff --git a/MediaBrowser.Server.Implementations/Localization/iso6392.txt b/Emby.Server.Implementations/Localization/iso6392.txt
index 665a5375e4..665a5375e4 100644
--- a/MediaBrowser.Server.Implementations/Localization/iso6392.txt
+++ b/Emby.Server.Implementations/Localization/iso6392.txt
diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
index 21e847c680..204e04061d 100644
--- a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs
+++ b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
@@ -13,10 +13,12 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Library;
-namespace MediaBrowser.Server.Implementations.MediaEncoder
+namespace Emby.Server.Implementations.MediaEncoder
{
public class EncodingManager : IEncodingManager
{
@@ -141,11 +143,11 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
var container = video.Container;
var tempFile = await _encoder.ExtractVideoImage(inputPath, container, protocol, video.Video3DFormat, time, cancellationToken).ConfigureAwait(false);
- File.Copy(tempFile, path, true);
+ _fileSystem.CopyFile(tempFile, path, true);
try
{
- File.Delete(tempFile);
+ _fileSystem.DeleteFile(tempFile);
}
catch
{
@@ -203,7 +205,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
return _fileSystem.GetFilePaths(path)
.ToList();
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
return new List<string>();
}
diff --git a/MediaBrowser.Server.Startup.Common/Migrations/IVersionMigration.cs b/Emby.Server.Implementations/Migrations/IVersionMigration.cs
index 6ef08fae97..7804912e3b 100644
--- a/MediaBrowser.Server.Startup.Common/Migrations/IVersionMigration.cs
+++ b/Emby.Server.Implementations/Migrations/IVersionMigration.cs
@@ -1,6 +1,6 @@
using System.Threading.Tasks;
-namespace MediaBrowser.Server.Startup.Common.Migrations
+namespace Emby.Server.Implementations.Migrations
{
public interface IVersionMigration
{
diff --git a/Emby.Server.Implementations/Migrations/LibraryScanMigration.cs b/Emby.Server.Implementations/Migrations/LibraryScanMigration.cs
new file mode 100644
index 0000000000..c494abc0bb
--- /dev/null
+++ b/Emby.Server.Implementations/Migrations/LibraryScanMigration.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Common.Updates;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Tasks;
+using MediaBrowser.Model.Updates;
+using System.Linq;
+
+namespace Emby.Server.Implementations.Migrations
+{
+ public class LibraryScanMigration : IVersionMigration
+ {
+ private readonly IServerConfigurationManager _config;
+ private readonly ITaskManager _taskManager;
+
+ public LibraryScanMigration(IServerConfigurationManager config, ITaskManager taskManager)
+ {
+ _config = config;
+ _taskManager = taskManager;
+ }
+
+ public async Task Run()
+ {
+ var name = "LibraryScan6";
+
+ if (!_config.Configuration.Migrations.Contains(name, StringComparer.OrdinalIgnoreCase))
+ {
+ Task.Run(() =>
+ {
+ var task = _taskManager.ScheduledTasks.Select(i => i.ScheduledTask)
+ .First(i => string.Equals(i.Key, "RefreshLibrary", StringComparison.OrdinalIgnoreCase));
+
+ _taskManager.QueueScheduledTask(task);
+ });
+
+ var list = _config.Configuration.Migrations.ToList();
+ list.Add(name);
+ _config.Configuration.Migrations = list.ToArray();
+ _config.SaveConfiguration();
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Startup.Common/Migrations/UpdateLevelMigration.cs b/Emby.Server.Implementations/Migrations/UpdateLevelMigration.cs
index 8483ca904c..c532ea08d1 100644
--- a/MediaBrowser.Server.Startup.Common/Migrations/UpdateLevelMigration.cs
+++ b/Emby.Server.Implementations/Migrations/UpdateLevelMigration.cs
@@ -2,15 +2,15 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Common.Implementations.Updates;
using MediaBrowser.Common.Net;
+using MediaBrowser.Common.Updates;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Updates;
-namespace MediaBrowser.Server.Startup.Common.Migrations
+namespace Emby.Server.Implementations.Migrations
{
public class UpdateLevelMigration : IVersionMigration
{
diff --git a/Emby.Server.Implementations/News/NewsEntryPoint.cs b/Emby.Server.Implementations/News/NewsEntryPoint.cs
new file mode 100644
index 0000000000..53c862d470
--- /dev/null
+++ b/Emby.Server.Implementations/News/NewsEntryPoint.cs
@@ -0,0 +1,275 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Notifications;
+using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.News;
+using MediaBrowser.Model.Notifications;
+using MediaBrowser.Model.Serialization;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Xml;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Threading;
+
+namespace Emby.Server.Implementations.News
+{
+ public class NewsEntryPoint : IServerEntryPoint
+ {
+ private ITimer _timer;
+ private readonly IHttpClient _httpClient;
+ private readonly IApplicationPaths _appPaths;
+ private readonly IFileSystem _fileSystem;
+ private readonly ILogger _logger;
+ private readonly IJsonSerializer _json;
+
+ private readonly INotificationManager _notifications;
+ private readonly IUserManager _userManager;
+
+ private readonly TimeSpan _frequency = TimeSpan.FromHours(24);
+ private readonly ITimerFactory _timerFactory;
+
+ public NewsEntryPoint(IHttpClient httpClient, IApplicationPaths appPaths, IFileSystem fileSystem, ILogger logger, IJsonSerializer json, INotificationManager notifications, IUserManager userManager, ITimerFactory timerFactory)
+ {
+ _httpClient = httpClient;
+ _appPaths = appPaths;
+ _fileSystem = fileSystem;
+ _logger = logger;
+ _json = json;
+ _notifications = notifications;
+ _userManager = userManager;
+ _timerFactory = timerFactory;
+ }
+
+ public void Run()
+ {
+ _timer = _timerFactory.Create(OnTimerFired, null, TimeSpan.FromMilliseconds(500), _frequency);
+ }
+
+ /// <summary>
+ /// Called when [timer fired].
+ /// </summary>
+ /// <param name="state">The state.</param>
+ private async void OnTimerFired(object state)
+ {
+ var path = Path.Combine(_appPaths.CachePath, "news.json");
+
+ try
+ {
+ await DownloadNews(path).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error downloading news", ex);
+ }
+ }
+
+ private async Task DownloadNews(string path)
+ {
+ DateTime? lastUpdate = null;
+
+ if (_fileSystem.FileExists(path))
+ {
+ lastUpdate = _fileSystem.GetLastWriteTimeUtc(path);
+ }
+
+ var requestOptions = new HttpRequestOptions
+ {
+ Url = "http://emby.media/community/index.php?/blog/rss/1-media-browser-developers-blog",
+ Progress = new Progress<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
+ };
+
+ using (var stream = await _httpClient.Get(requestOptions).ConfigureAwait(false))
+ {
+ using (var reader = XmlReader.Create(stream))
+ {
+ var news = ParseRssItems(reader).ToList();
+
+ _json.SerializeToFile(news, path);
+
+ await CreateNotifications(news, lastUpdate, CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+ }
+
+ private Task CreateNotifications(List<NewsItem> items, DateTime? lastUpdate, CancellationToken cancellationToken)
+ {
+ if (lastUpdate.HasValue)
+ {
+ items = items.Where(i => i.Date.ToUniversalTime() >= lastUpdate.Value)
+ .ToList();
+ }
+
+ var tasks = items.Select(i => _notifications.SendNotification(new NotificationRequest
+ {
+ Date = i.Date,
+ Name = i.Title,
+ Description = i.Description,
+ Url = i.Link,
+ UserIds = _userManager.Users.Select(u => u.Id.ToString("N")).ToList()
+
+ }, cancellationToken));
+
+ return Task.WhenAll(tasks);
+ }
+
+ private IEnumerable<NewsItem> ParseRssItems(XmlReader reader)
+ {
+ reader.MoveToContent();
+ reader.Read();
+
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "channel":
+ {
+ if (!reader.IsEmptyElement)
+ {
+ using (var subReader = reader.ReadSubtree())
+ {
+ return ParseFromChannelNode(subReader);
+ }
+ }
+ else
+ {
+ reader.Read();
+ }
+ break;
+ }
+ default:
+ {
+ reader.Skip();
+ break;
+ }
+ }
+ }
+ else
+ {
+ reader.Read();
+ }
+ }
+
+ return new List<NewsItem>();
+ }
+
+ private IEnumerable<NewsItem> ParseFromChannelNode(XmlReader reader)
+ {
+ var list = new List<NewsItem>();
+
+ reader.MoveToContent();
+ reader.Read();
+
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "item":
+ {
+ if (!reader.IsEmptyElement)
+ {
+ using (var subReader = reader.ReadSubtree())
+ {
+ list.Add(ParseItem(subReader));
+ }
+ }
+ else
+ {
+ reader.Read();
+ }
+ break;
+ }
+ default:
+ {
+ reader.Skip();
+ break;
+ }
+ }
+ }
+ else
+ {
+ reader.Read();
+ }
+ }
+
+ return list;
+ }
+
+ private NewsItem ParseItem(XmlReader reader)
+ {
+ var item = new NewsItem();
+
+ reader.MoveToContent();
+ reader.Read();
+
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "title":
+ {
+ item.Title = reader.ReadElementContentAsString();
+ break;
+ }
+ case "link":
+ {
+ item.Link = reader.ReadElementContentAsString();
+ break;
+ }
+ case "description":
+ {
+ item.DescriptionHtml = reader.ReadElementContentAsString();
+ item.Description = item.DescriptionHtml.StripHtml();
+ break;
+ }
+ case "pubDate":
+ {
+ var date = reader.ReadElementContentAsString();
+ DateTime parsedDate;
+
+ if (DateTime.TryParse(date, out parsedDate))
+ {
+ item.Date = parsedDate;
+ }
+ break;
+ }
+ default:
+ {
+ reader.Skip();
+ break;
+ }
+ }
+ }
+ else
+ {
+ reader.Read();
+ }
+ }
+
+ return item;
+ }
+
+ public void Dispose()
+ {
+ if (_timer != null)
+ {
+ _timer.Dispose();
+ _timer = null;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/News/NewsService.cs b/Emby.Server.Implementations/News/NewsService.cs
index 684363d01a..80e7996342 100644
--- a/MediaBrowser.Server.Implementations/News/NewsService.cs
+++ b/Emby.Server.Implementations/News/NewsService.cs
@@ -1,5 +1,4 @@
using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.News;
using MediaBrowser.Model.News;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Serialization;
@@ -7,7 +6,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
-namespace MediaBrowser.Server.Implementations.News
+namespace Emby.Server.Implementations.News
{
public class NewsService : INewsService
{
@@ -26,7 +25,7 @@ namespace MediaBrowser.Server.Implementations.News
{
return GetProductNewsInternal(query);
}
- catch (DirectoryNotFoundException)
+ catch (FileNotFoundException)
{
// No biggie
return new QueryResult<NewsItem>
@@ -34,7 +33,7 @@ namespace MediaBrowser.Server.Implementations.News
Items = new NewsItem[] { }
};
}
- catch (FileNotFoundException)
+ catch (IOException)
{
// No biggie
return new QueryResult<NewsItem>
diff --git a/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs b/Emby.Server.Implementations/Notifications/CoreNotificationTypes.cs
index 8ca6677398..f9fb98f85e 100644
--- a/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs
+++ b/Emby.Server.Implementations/Notifications/CoreNotificationTypes.cs
@@ -1,12 +1,12 @@
using MediaBrowser.Controller;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Notifications;
using MediaBrowser.Model.Notifications;
using System;
using System.Collections.Generic;
using System.Linq;
+using MediaBrowser.Model.Globalization;
-namespace MediaBrowser.Server.Implementations.Notifications
+namespace Emby.Server.Implementations.Notifications
{
public class CoreNotificationTypes : INotificationTypeFactory
{
diff --git a/MediaBrowser.Server.Implementations/Notifications/IConfigurableNotificationService.cs b/Emby.Server.Implementations/Notifications/IConfigurableNotificationService.cs
index cdfd0f640f..d74667c486 100644
--- a/MediaBrowser.Server.Implementations/Notifications/IConfigurableNotificationService.cs
+++ b/Emby.Server.Implementations/Notifications/IConfigurableNotificationService.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Server.Implementations.Notifications
+namespace Emby.Server.Implementations.Notifications
{
public interface IConfigurableNotificationService
{
diff --git a/MediaBrowser.Server.Implementations/Notifications/InternalNotificationService.cs b/Emby.Server.Implementations/Notifications/InternalNotificationService.cs
index 4a625f0fb0..61c564f188 100644
--- a/MediaBrowser.Server.Implementations/Notifications/InternalNotificationService.cs
+++ b/Emby.Server.Implementations/Notifications/InternalNotificationService.cs
@@ -5,7 +5,7 @@ using System.Threading;
using System.Threading.Tasks;
using System;
-namespace MediaBrowser.Server.Implementations.Notifications
+namespace Emby.Server.Implementations.Notifications
{
public class InternalNotificationService : INotificationService, IConfigurableNotificationService
{
diff --git a/MediaBrowser.Server.Implementations/Notifications/NotificationConfigurationFactory.cs b/Emby.Server.Implementations/Notifications/NotificationConfigurationFactory.cs
index a336eba0ed..a7c5b12337 100644
--- a/MediaBrowser.Server.Implementations/Notifications/NotificationConfigurationFactory.cs
+++ b/Emby.Server.Implementations/Notifications/NotificationConfigurationFactory.cs
@@ -2,7 +2,7 @@
using MediaBrowser.Model.Notifications;
using System.Collections.Generic;
-namespace MediaBrowser.Server.Implementations.Notifications
+namespace Emby.Server.Implementations.Notifications
{
public class NotificationConfigurationFactory : IConfigurationFactory
{
diff --git a/MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs b/Emby.Server.Implementations/Notifications/NotificationManager.cs
index f19ff8a5f2..db79804978 100644
--- a/MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs
+++ b/Emby.Server.Implementations/Notifications/NotificationManager.cs
@@ -13,7 +13,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Extensions;
-namespace MediaBrowser.Server.Implementations.Notifications
+namespace Emby.Server.Implementations.Notifications
{
public class NotificationManager : INotificationManager
{
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs b/Emby.Server.Implementations/Notifications/Notifications.cs
index f7fe707da3..2d441c18cc 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs
+++ b/Emby.Server.Implementations/Notifications/Notifications.cs
@@ -1,6 +1,5 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Plugins;
-using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Devices;
@@ -23,8 +22,9 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Model.Threading;
-namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
+namespace Emby.Server.Implementations.Notifications
{
/// <summary>
/// Creates notifications for various system events
@@ -41,14 +41,15 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
private readonly ILibraryManager _libraryManager;
private readonly ISessionManager _sessionManager;
private readonly IServerApplicationHost _appHost;
+ private readonly ITimerFactory _timerFactory;
- private Timer LibraryUpdateTimer { get; set; }
+ 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)
+ 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;
@@ -60,6 +61,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
_appHost = appHost;
_config = config;
_deviceManager = deviceManager;
+ _timerFactory = timerFactory;
}
public void Run()
@@ -257,9 +259,8 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
}
var item = e.MediaInfo;
- var themeMedia = item as IThemeMedia;
- if (themeMedia != null && themeMedia.IsThemeMedia)
+ if ( item.IsThemeMedia)
{
// Don't report theme song or local trailer playback
return;
@@ -334,7 +335,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
{
if (LibraryUpdateTimer == null)
{
- LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, 5000,
+ LibraryUpdateTimer = _timerFactory.Create(LibraryUpdateTimerCallback, null, 5000,
Timeout.Infinite);
}
else
diff --git a/Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs b/Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs
new file mode 100644
index 0000000000..f18278cb25
--- /dev/null
+++ b/Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs
@@ -0,0 +1,337 @@
+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.Logging;
+using MediaBrowser.Model.Notifications;
+using SQLitePCL.pretty;
+
+namespace Emby.Server.Implementations.Notifications
+{
+ public class SqliteNotificationsRepository : BaseSqliteRepository, INotificationsRepository
+ {
+ public SqliteNotificationsRepository(ILogger logger, IServerApplicationPaths appPaths) : base(logger)
+ {
+ 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()
+ {
+ 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.ToGuidParamValue());
+
+ var whereClause = " where " + string.Join(" And ", clauses.ToArray());
+
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
+ {
+ result.TotalRecordCount = connection.Query("select count(Id) from Notifications" + whereClause, paramList.ToArray()).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()))
+ {
+ resultList.Add(GetNotification(row));
+ }
+
+ result.Notifications = resultList.ToArray();
+ }
+ }
+
+ 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.ToGuidParamValue());
+
+ 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].ReadGuid().ToString("N"),
+ UserId = reader[1].ReadGuid().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.ToGuidParamValue());
+ statement.TryBind("@UserId", notification.UserId.ToGuidParamValue());
+ 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();
+
+ await MarkReadInternal(idArray, userId, isRead, cancellationToken).ConfigureAwait(false);
+
+ if (NotificationsMarkedRead != null)
+ {
+ try
+ {
+ NotificationsMarkedRead(this, new NotificationReadEventArgs
+ {
+ IdList = list.ToArray(),
+ 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.ToGuidParamValue());
+
+ 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.ToGuidParamValue());
+
+ foreach (var id in notificationIdList)
+ {
+ statement.Reset();
+
+ statement.TryBind("@Id", id.ToGuidParamValue());
+
+ statement.MoveNext();
+ }
+ }
+
+ }, TransactionMode);
+ }
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/WebSocketNotifier.cs b/Emby.Server.Implementations/Notifications/WebSocketNotifier.cs
index 916b4a6224..8b3367217c 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/WebSocketNotifier.cs
+++ b/Emby.Server.Implementations/Notifications/WebSocketNotifier.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Controller.Notifications;
using MediaBrowser.Controller.Plugins;
using System.Linq;
-namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
+namespace Emby.Server.Implementations.Notifications
{
/// <summary>
/// Notifies clients anytime a notification is added or udpated
diff --git a/MediaBrowser.Server.Implementations/Photos/PhotoAlbumImageProvider.cs b/Emby.Server.Implementations/Photos/PhotoAlbumImageProvider.cs
index fafb2f2685..cc1756f960 100644
--- a/MediaBrowser.Server.Implementations/Photos/PhotoAlbumImageProvider.cs
+++ b/Emby.Server.Implementations/Photos/PhotoAlbumImageProvider.cs
@@ -5,10 +5,11 @@ using MediaBrowser.Controller.Providers;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-using CommonIO;
+using Emby.Server.Implementations.Images;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Entities;
-namespace MediaBrowser.Server.Implementations.Photos
+namespace Emby.Server.Implementations.Photos
{
public class PhotoAlbumImageProvider : BaseDynamicImageProvider<PhotoAlbum>
{
diff --git a/MediaBrowser.Server.Implementations/Playlists/PlaylistImageProvider.cs b/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs
index 5b234d0c67..ef7d6dba82 100644
--- a/MediaBrowser.Server.Implementations/Playlists/PlaylistImageProvider.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs
@@ -6,16 +6,18 @@ using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Server.Implementations.Photos;
-using MoreLinq;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-using CommonIO;
+using Emby.Server.Implementations.Images;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Querying;
-namespace MediaBrowser.Server.Implementations.Playlists
+namespace Emby.Server.Implementations.Playlists
{
public class PlaylistImageProvider : BaseDynamicImageProvider<Playlist>
{
diff --git a/MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
index ba1559bd03..9583141e0d 100644
--- a/MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
@@ -12,9 +12,11 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
-namespace MediaBrowser.Server.Implementations.Playlists
+namespace Emby.Server.Implementations.Playlists
{
public class PlaylistManager : IPlaylistManager
{
@@ -265,9 +267,10 @@ namespace MediaBrowser.Server.Implementations.Playlists
public Folder GetPlaylistsFolder(string userId)
{
- return _libraryManager.RootFolder.Children.OfType<PlaylistsFolder>()
- .FirstOrDefault() ?? _libraryManager.GetUserRootFolder().Children.OfType<PlaylistsFolder>()
- .FirstOrDefault();
+ var typeName = "PlaylistsFolder";
+
+ return _libraryManager.RootFolder.Children.OfType<Folder>().FirstOrDefault(i => string.Equals(i.GetType().Name, typeName, StringComparison.Ordinal)) ??
+ _libraryManager.GetUserRootFolder().Children.OfType<Folder>().FirstOrDefault(i => string.Equals(i.GetType().Name, typeName, StringComparison.Ordinal));
}
}
}
diff --git a/Emby.Server.Implementations/Playlists/PlaylistsDynamicFolder.cs b/Emby.Server.Implementations/Playlists/PlaylistsDynamicFolder.cs
new file mode 100644
index 0000000000..dacc937e1a
--- /dev/null
+++ b/Emby.Server.Implementations/Playlists/PlaylistsDynamicFolder.cs
@@ -0,0 +1,32 @@
+using System.IO;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Server.Implementations.Playlists;
+
+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/Properties/AssemblyInfo.cs b/Emby.Server.Implementations/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..ed7f9631fa
--- /dev/null
+++ b/Emby.Server.Implementations/Properties/AssemblyInfo.cs
@@ -0,0 +1,30 @@
+using System.Resources;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Emby.Server.Implementations")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Emby.Server.Implementations")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[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
+//
+// 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/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs
index 607a043a64..d758158479 100644
--- a/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs
@@ -1,5 +1,4 @@
using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
@@ -11,10 +10,13 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Server.Implementations.ScheduledTasks
+namespace Emby.Server.Implementations.ScheduledTasks
{
/// <summary>
/// Class ChapterImagesTask
@@ -30,12 +32,6 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
/// </summary>
private readonly ILibraryManager _libraryManager;
- /// <summary>
- /// The current new item timer
- /// </summary>
- /// <value>The new item timer.</value>
- private Timer NewItemTimer { get; set; }
-
private readonly IItemRepository _itemRepo;
private readonly IApplicationPaths _appPaths;
@@ -46,9 +42,6 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
/// <summary>
/// Initializes a new instance of the <see cref="ChapterImagesTask" /> class.
/// </summary>
- /// <param name="logManager">The log manager.</param>
- /// <param name="libraryManager">The library manager.</param>
- /// <param name="itemRepo">The item repo.</param>
public ChapterImagesTask(ILogManager logManager, ILibraryManager libraryManager, IItemRepository itemRepo, IApplicationPaths appPaths, IEncodingManager encodingManager, IFileSystem fileSystem)
{
_logger = logManager.GetLogger(GetType().Name);
@@ -62,20 +55,22 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
/// <summary>
/// Creates the triggers that define when the task will run
/// </summary>
- /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
- public IEnumerable<ITaskTrigger> GetDefaultTriggers()
+ public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- return new ITaskTrigger[]
+ return new[] {
+
+ new TaskTriggerInfo
{
- new DailyTrigger
- {
- TimeOfDay = TimeSpan.FromHours(1),
- TaskOptions = new TaskExecutionOptions
- {
- MaxRuntimeMs = Convert.ToInt32(TimeSpan.FromHours(4).TotalMilliseconds)
- }
- }
- };
+ Type = TaskTriggerInfo.TriggerDaily,
+ TimeOfDayTicks = TimeSpan.FromHours(1).Ticks,
+ MaxRuntimeMs = Convert.ToInt32(TimeSpan.FromHours(4).TotalMilliseconds)
+ }
+ };
+ }
+
+ public string Key
+ {
+ get { return "RefreshChapterImages"; }
}
/// <summary>
@@ -111,7 +106,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
{
previouslyFailedImages = new List<string>();
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
previouslyFailedImages = new List<string>();
}
diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/PeopleValidationTask.cs b/Emby.Server.Implementations/ScheduledTasks/PeopleValidationTask.cs
index 05c3db63c8..02568fe3ac 100644
--- a/MediaBrowser.Server.Implementations/ScheduledTasks/PeopleValidationTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/PeopleValidationTask.cs
@@ -1,12 +1,12 @@
-using MediaBrowser.Common.ScheduledTasks;
-using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Library;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller;
+using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Server.Implementations.ScheduledTasks
+namespace Emby.Server.Implementations.ScheduledTasks
{
/// <summary>
/// Class PeopleValidationTask
@@ -33,16 +33,22 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
/// <summary>
/// Creates the triggers that define when the task will run
/// </summary>
- /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
- public IEnumerable<ITaskTrigger> GetDefaultTriggers()
+ public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- // Randomize the default start hour because this operation can really hammer internet metadata providers
- var startHour = new Random(_appHost.SystemId.GetHashCode()).Next(0, 8);
-
- return new ITaskTrigger[]
+ return new[]
+ {
+ // Every so often
+ new TaskTriggerInfo
{
- new DailyTrigger { TimeOfDay = TimeSpan.FromHours(startHour) },
- };
+ Type = TaskTriggerInfo.TriggerInterval,
+ IntervalTicks = TimeSpan.FromDays(7).Ticks
+ }
+ };
+ }
+
+ public string Key
+ {
+ get { return "RefreshPeople"; }
}
/// <summary>
diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs b/Emby.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs
index ff0960259c..e619b68649 100644
--- a/MediaBrowser.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs
@@ -1,5 +1,4 @@
using MediaBrowser.Common;
-using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Common.Updates;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
@@ -9,8 +8,9 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Server.Implementations.ScheduledTasks
+namespace Emby.Server.Implementations.ScheduledTasks
{
/// <summary>
/// Plugin Update Task
@@ -37,18 +37,23 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
/// Creates the triggers that define when the task will run
/// </summary>
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
- public IEnumerable<ITaskTrigger> GetDefaultTriggers()
+ public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- return new ITaskTrigger[] {
+ return new[] {
// At startup
- new StartupTrigger(),
+ new TaskTriggerInfo {Type = TaskTriggerInfo.TriggerStartup},
// Every so often
- new IntervalTrigger { Interval = TimeSpan.FromHours(24)}
+ new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
};
}
+ public string Key
+ {
+ get { return "PluginUpdates"; }
+ }
+
/// <summary>
/// Update installed plugins
/// </summary>
diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshIntrosTask.cs b/Emby.Server.Implementations/ScheduledTasks/RefreshIntrosTask.cs
index 3192c91f4a..749233fa19 100644
--- a/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshIntrosTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/RefreshIntrosTask.cs
@@ -4,9 +4,11 @@ using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
-namespace MediaBrowser.Server.Implementations.ScheduledTasks
+namespace Emby.Server.Implementations.ScheduledTasks
{
/// <summary>
/// Class RefreshIntrosTask
diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs b/Emby.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs
index 64ae249cd4..fb07b8e99a 100644
--- a/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs
@@ -1,19 +1,18 @@
-using System.Linq;
-using MediaBrowser.Common.ScheduledTasks;
-using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Server.Implementations.Library;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using Emby.Server.Implementations.Library;
+using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Server.Implementations.ScheduledTasks
+namespace Emby.Server.Implementations.ScheduledTasks
{
/// <summary>
/// Class RefreshMediaLibraryTask
/// </summary>
- public class RefreshMediaLibraryTask : IScheduledTask, IHasKey
+ public class RefreshMediaLibraryTask : IScheduledTask
{
/// <summary>
/// The _library manager
@@ -32,18 +31,16 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
}
/// <summary>
- /// Gets the default triggers.
+ /// Creates the triggers that define when the task will run
/// </summary>
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
- public IEnumerable<ITaskTrigger> GetDefaultTriggers()
+ public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- var list = new ITaskTrigger[] {
-
- new IntervalTrigger{ Interval = TimeSpan.FromHours(12)}
-
- }.ToList();
-
- return list;
+ return new[] {
+
+ // Every so often
+ new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(12).Ticks}
+ };
}
/// <summary>
diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/SystemUpdateTask.cs b/Emby.Server.Implementations/ScheduledTasks/SystemUpdateTask.cs
index 0ba9d4f324..28fd8b68cb 100644
--- a/MediaBrowser.Server.Implementations/ScheduledTasks/SystemUpdateTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/SystemUpdateTask.cs
@@ -1,18 +1,18 @@
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Server.Implementations.ScheduledTasks
+namespace Emby.Server.Implementations.ScheduledTasks
{
/// <summary>
/// Plugin Update Task
/// </summary>
- public class SystemUpdateTask : IScheduledTask, IHasKey
+ public class SystemUpdateTask : IScheduledTask
{
/// <summary>
/// The _app host
@@ -47,16 +47,15 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
/// Creates the triggers that define when the task will run
/// </summary>
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
- public IEnumerable<ITaskTrigger> GetDefaultTriggers()
+ public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- // Until we can vary these default triggers per server and MBT, we need something that makes sense for both
- return new ITaskTrigger[] {
+ return new[] {
// At startup
- new StartupTrigger(),
+ new TaskTriggerInfo {Type = TaskTriggerInfo.TriggerStartup},
// Every so often
- new IntervalTrigger { Interval = TimeSpan.FromHours(24)}
+ new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
};
}
diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs
new file mode 100644
index 0000000000..a2d61873b8
--- /dev/null
+++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs
@@ -0,0 +1,318 @@
+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.Security;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Querying;
+using SQLitePCL.pretty;
+
+namespace Emby.Server.Implementations.Security
+{
+ public class AuthenticationRepository : BaseSqliteRepository, IAuthenticationRepository
+ {
+ private readonly IServerApplicationPaths _appPaths;
+ private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+
+ public AuthenticationRepository(ILogger logger, IServerApplicationPaths appPaths)
+ : base(logger)
+ {
+ _appPaths = appPaths;
+ DbFilePath = Path.Combine(appPaths.DataPath, "authentication.db");
+ }
+
+ public void Initialize()
+ {
+ using (var connection = CreateConnection())
+ {
+ RunDefaultInitialization(connection);
+
+ string[] queries = {
+
+ "create table if not exists AccessTokens (Id GUID PRIMARY KEY, AccessToken TEXT NOT NULL, DeviceId TEXT, AppName TEXT, AppVersion TEXT, DeviceName TEXT, UserId TEXT, IsActive BIT, DateCreated DATETIME NOT NULL, DateRevoked DATETIME)",
+ "create index if not exists idx_AccessTokens on AccessTokens(Id)"
+ };
+
+ connection.RunQueries(queries);
+
+ connection.RunInTransaction(db =>
+ {
+ var existingColumnNames = GetColumnNames(db, "AccessTokens");
+
+ AddColumn(db, "AccessTokens", "AppVersion", "TEXT", existingColumnNames);
+
+ }, TransactionMode);
+ }
+ }
+
+ public Task Create(AuthenticationInfo info, CancellationToken cancellationToken)
+ {
+ info.Id = Guid.NewGuid().ToString("N");
+
+ return Update(info, cancellationToken);
+ }
+
+ public async Task Update(AuthenticationInfo info, CancellationToken cancellationToken)
+ {
+ if (info == null)
+ {
+ throw new ArgumentNullException("info");
+ }
+
+ 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)"))
+ {
+ statement.TryBind("@Id", info.Id.ToGuidParamValue());
+ 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("@DateCreated", info.DateCreated.ToDateTimeParamValue());
+
+ if (info.DateRevoked.HasValue)
+ {
+ statement.TryBind("@DateRevoked", info.DateRevoked.Value.ToDateTimeParamValue());
+ }
+ else
+ {
+ statement.TryBindNull("@DateRevoked");
+ }
+
+ statement.MoveNext();
+ }
+
+ }, TransactionMode);
+ }
+ }
+ }
+
+ private const string BaseSelectText = "select Id, AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, IsActive, DateCreated, DateRevoked from AccessTokens";
+
+ private void BindAuthenticationQueryParams(AuthenticationInfoQuery query, IStatement statement)
+ {
+ if (!string.IsNullOrWhiteSpace(query.AccessToken))
+ {
+ statement.TryBind("@AccessToken", query.AccessToken);
+ }
+
+ if (!string.IsNullOrWhiteSpace(query.UserId))
+ {
+ statement.TryBind("@UserId", query.UserId);
+ }
+
+ if (!string.IsNullOrWhiteSpace(query.DeviceId))
+ {
+ statement.TryBind("@DeviceId", query.DeviceId);
+ }
+
+ if (query.IsActive.HasValue)
+ {
+ statement.TryBind("@IsActive", query.IsActive.Value);
+ }
+ }
+
+ public QueryResult<AuthenticationInfo> Get(AuthenticationInfoQuery query)
+ {
+ if (query == null)
+ {
+ throw new ArgumentNullException("query");
+ }
+
+ var commandText = BaseSelectText;
+
+ var whereClauses = new List<string>();
+
+ var startIndex = query.StartIndex ?? 0;
+
+ if (!string.IsNullOrWhiteSpace(query.AccessToken))
+ {
+ whereClauses.Add("AccessToken=@AccessToken");
+ }
+
+ if (!string.IsNullOrWhiteSpace(query.UserId))
+ {
+ whereClauses.Add("UserId=@UserId");
+ }
+
+ if (!string.IsNullOrWhiteSpace(query.DeviceId))
+ {
+ whereClauses.Add("DeviceId=@DeviceId");
+ }
+
+ if (query.IsActive.HasValue)
+ {
+ whereClauses.Add("IsActive=@IsActive");
+ }
+
+ if (query.HasUser.HasValue)
+ {
+ if (query.HasUser.Value)
+ {
+ whereClauses.Add("UserId not null");
+ }
+ else
+ {
+ whereClauses.Add("UserId is null");
+ }
+ }
+
+ var whereTextWithoutPaging = whereClauses.Count == 0 ?
+ string.Empty :
+ " where " + string.Join(" AND ", whereClauses.ToArray());
+
+ if (startIndex > 0)
+ {
+ var pagingWhereText = whereClauses.Count == 0 ?
+ string.Empty :
+ " where " + string.Join(" AND ", whereClauses.ToArray());
+
+ whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM AccessTokens {0} ORDER BY DateCreated LIMIT {1})",
+ pagingWhereText,
+ startIndex.ToString(_usCulture)));
+ }
+
+ var whereText = whereClauses.Count == 0 ?
+ string.Empty :
+ " where " + string.Join(" AND ", whereClauses.ToArray());
+
+ commandText += whereText;
+
+ commandText += " ORDER BY DateCreated";
+
+ if (query.Limit.HasValue)
+ {
+ commandText += " LIMIT " + query.Limit.Value.ToString(_usCulture);
+ }
+
+ var list = new List<AuthenticationInfo>();
+
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
+ {
+ return connection.RunInTransaction(db =>
+ {
+ var result = new QueryResult<AuthenticationInfo>();
+
+ var statementTexts = new List<string>();
+ statementTexts.Add(commandText);
+ statementTexts.Add("select count (Id) from AccessTokens" + whereTextWithoutPaging);
+
+ var statements = PrepareAllSafe(db, statementTexts)
+ .ToList();
+
+ using (var statement = statements[0])
+ {
+ BindAuthenticationQueryParams(query, statement);
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ list.Add(Get(row));
+ }
+
+ using (var totalCountStatement = statements[1])
+ {
+ BindAuthenticationQueryParams(query, totalCountStatement);
+
+ result.TotalRecordCount = totalCountStatement.ExecuteQuery()
+ .SelectScalarInt()
+ .First();
+ }
+ }
+
+ result.Items = list.ToArray();
+ return result;
+
+ }, ReadTransactionMode);
+ }
+ }
+ }
+
+ 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.ToGuidParamValue());
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ return Get(row);
+ }
+ return null;
+ }
+ }
+ }
+ }
+
+ private AuthenticationInfo Get(IReadOnlyList<IResultSetValue> reader)
+ {
+ var info = new AuthenticationInfo
+ {
+ Id = reader[0].ReadGuid().ToString("N"),
+ AccessToken = reader[1].ToString()
+ };
+
+ if (reader[2].SQLiteType != SQLiteType.Null)
+ {
+ info.DeviceId = reader[2].ToString();
+ }
+
+ if (reader[3].SQLiteType != SQLiteType.Null)
+ {
+ info.AppName = reader[3].ToString();
+ }
+
+ if (reader[4].SQLiteType != SQLiteType.Null)
+ {
+ info.AppVersion = reader[4].ToString();
+ }
+
+ if (reader[5].SQLiteType != SQLiteType.Null)
+ {
+ info.DeviceName = reader[5].ToString();
+ }
+
+ if (reader[6].SQLiteType != SQLiteType.Null)
+ {
+ info.UserId = reader[6].ToString();
+ }
+
+ info.IsActive = reader[7].ToBool();
+ info.DateCreated = reader[8].ReadDateTime();
+
+ if (reader[9].SQLiteType != SQLiteType.Null)
+ {
+ info.DateRevoked = reader[9].ReadDateTime();
+ }
+
+ return info;
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Security/EncryptionManager.cs b/Emby.Server.Implementations/Security/EncryptionManager.cs
index cd9b9651ec..271b0bbdb5 100644
--- a/MediaBrowser.Server.Implementations/Security/EncryptionManager.cs
+++ b/Emby.Server.Implementations/Security/EncryptionManager.cs
@@ -2,7 +2,7 @@
using System;
using System.Text;
-namespace MediaBrowser.Server.Implementations.Security
+namespace Emby.Server.Implementations.Security
{
public class EncryptionManager : IEncryptionManager
{
@@ -45,7 +45,7 @@ namespace MediaBrowser.Server.Implementations.Security
// Yes, this isn't good, but ProtectedData in mono is throwing exceptions, so use this for now
var bytes = Convert.FromBase64String(value);
- return Encoding.UTF8.GetString(bytes);
+ return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
}
}
}
diff --git a/Emby.Server.Implementations/Security/MBLicenseFile.cs b/Emby.Server.Implementations/Security/MBLicenseFile.cs
new file mode 100644
index 0000000000..c791d6a52e
--- /dev/null
+++ b/Emby.Server.Implementations/Security/MBLicenseFile.cs
@@ -0,0 +1,214 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Model.Cryptography;
+using MediaBrowser.Model.IO;
+
+namespace Emby.Server.Implementations.Security
+{
+ internal class MBLicenseFile
+ {
+ private readonly IApplicationPaths _appPaths;
+ private readonly IFileSystem _fileSystem;
+ private readonly ICryptoProvider _cryptographyProvider;
+
+ public string RegKey
+ {
+ get { return _regKey; }
+ set
+ {
+ if (value != _regKey)
+ {
+ //if key is changed - clear out our saved validations
+ _updateRecords.Clear();
+ _regKey = value;
+ }
+ }
+ }
+
+ private string Filename
+ {
+ get
+ {
+ return Path.Combine(_appPaths.ConfigurationDirectoryPath, "mb.lic");
+ }
+ }
+
+ private readonly ConcurrentDictionary<Guid, FeatureRegInfo> _updateRecords = new ConcurrentDictionary<Guid, FeatureRegInfo>();
+ private readonly object _fileLock = new object();
+ private string _regKey;
+
+ public MBLicenseFile(IApplicationPaths appPaths, IFileSystem fileSystem, ICryptoProvider cryptographyProvider)
+ {
+ _appPaths = appPaths;
+ _fileSystem = fileSystem;
+ _cryptographyProvider = cryptographyProvider;
+
+ Load();
+ }
+
+ private void SetUpdateRecord(Guid key, FeatureRegInfo value)
+ {
+ _updateRecords.AddOrUpdate(key, value, (k, v) => value);
+ }
+
+ private Guid GetKey(string featureId)
+ {
+ return new Guid(_cryptographyProvider.ComputeMD5(Encoding.Unicode.GetBytes(featureId)));
+ }
+
+ public void AddRegCheck(string featureId, DateTime expirationDate)
+ {
+ var key = GetKey(featureId);
+ var value = new FeatureRegInfo
+ {
+ ExpirationDate = expirationDate,
+ LastChecked = DateTime.UtcNow
+ };
+
+ SetUpdateRecord(key, value);
+ Save();
+ }
+
+ public void RemoveRegCheck(string featureId)
+ {
+ var key = GetKey(featureId);
+ FeatureRegInfo val;
+
+ _updateRecords.TryRemove(key, out val);
+
+ Save();
+ }
+
+ public FeatureRegInfo GetRegInfo(string featureId)
+ {
+ var key = GetKey(featureId);
+ FeatureRegInfo info = null;
+ _updateRecords.TryGetValue(key, out info);
+
+ if (info == null)
+ {
+ return null;
+ }
+
+ // guard agains people just putting a large number in the file
+ return info.LastChecked < DateTime.UtcNow ? info : null;
+ }
+
+ private void Load()
+ {
+ string[] contents = null;
+ var licenseFile = Filename;
+ lock (_fileLock)
+ {
+ try
+ {
+ contents = _fileSystem.ReadAllLines(licenseFile);
+ }
+ catch (FileNotFoundException)
+ {
+ lock (_fileLock)
+ {
+ _fileSystem.WriteAllBytes(licenseFile, new byte[] { });
+ }
+ }
+ catch (IOException)
+ {
+ lock (_fileLock)
+ {
+ _fileSystem.WriteAllBytes(licenseFile, new byte[] { });
+ }
+ }
+ }
+ if (contents != null && contents.Length > 0)
+ {
+ //first line is reg key
+ RegKey = contents[0];
+
+ //next is legacy key
+ if (contents.Length > 1)
+ {
+ // Don't need this anymore
+ }
+
+ //the rest of the lines should be pairs of features and timestamps
+ for (var i = 2; i < contents.Length; i = i + 2)
+ {
+ var line = contents[i];
+ if (string.IsNullOrWhiteSpace(line))
+ {
+ continue;
+ }
+
+ Guid feat;
+ if (Guid.TryParse(line, out feat))
+ {
+ var lineParts = contents[i + 1].Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
+
+ long ticks;
+ if (long.TryParse(lineParts[0], out ticks))
+ {
+ var info = new FeatureRegInfo
+ {
+ LastChecked = new DateTime(ticks)
+ };
+
+ if (lineParts.Length > 1 && long.TryParse(lineParts[1], out ticks))
+ {
+ info.ExpirationDate = new DateTime(ticks);
+ }
+
+ SetUpdateRecord(feat, info);
+ }
+ }
+ }
+ }
+ }
+
+ public void Save()
+ {
+ //build our array
+ var lines = new List<string>
+ {
+ RegKey,
+
+ // Legacy key
+ string.Empty
+ };
+
+ foreach (var pair in _updateRecords
+ .ToList())
+ {
+ lines.Add(pair.Key.ToString());
+
+ var dateLine = pair.Value.LastChecked.Ticks.ToString(CultureInfo.InvariantCulture) + "|" +
+ pair.Value.ExpirationDate.Ticks.ToString(CultureInfo.InvariantCulture);
+
+ lines.Add(dateLine);
+ }
+
+ var licenseFile = Filename;
+ _fileSystem.CreateDirectory(Path.GetDirectoryName(licenseFile));
+ lock (_fileLock)
+ {
+ _fileSystem.WriteAllLines(licenseFile, lines);
+ }
+ }
+ }
+
+ internal class FeatureRegInfo
+ {
+ public DateTime ExpirationDate { get; set; }
+ public DateTime LastChecked { get; set; }
+
+ public FeatureRegInfo()
+ {
+ ExpirationDate = DateTime.MinValue;
+ }
+ }
+}
diff --git a/MediaBrowser.Common.Implementations/Security/PluginSecurityManager.cs b/Emby.Server.Implementations/Security/PluginSecurityManager.cs
index 5d440609ef..f21259137e 100644
--- a/MediaBrowser.Common.Implementations/Security/PluginSecurityManager.cs
+++ b/Emby.Server.Implementations/Security/PluginSecurityManager.cs
@@ -1,26 +1,29 @@
-using System.IO;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Common.Security;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Serialization;
-using System;
+using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Common.Security;
+using MediaBrowser.Controller;
+using MediaBrowser.Model.Cryptography;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Serialization;
-namespace MediaBrowser.Common.Implementations.Security
+namespace Emby.Server.Implementations.Security
{
/// <summary>
/// Class PluginSecurityManager
/// </summary>
public class PluginSecurityManager : ISecurityManager
{
- private const string MBValidateUrl = MbAdmin.HttpsUrl + "service/registration/validate";
+ 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>
@@ -52,14 +55,16 @@ namespace MediaBrowser.Common.Implementations.Security
private MBLicenseFile _licenseFile;
private MBLicenseFile LicenseFile
{
- get { return _licenseFile ?? (_licenseFile = new MBLicenseFile(_appPaths)); }
+ get { return _licenseFile ?? (_licenseFile = new MBLicenseFile(_appPaths, _fileSystem, _cryptographyProvider)); }
}
private readonly IHttpClient _httpClient;
private readonly IJsonSerializer _jsonSerializer;
- private readonly IApplicationHost _appHost;
+ private readonly IServerApplicationHost _appHost;
private readonly ILogger _logger;
private readonly IApplicationPaths _appPaths;
+ private readonly IFileSystem _fileSystem;
+ private readonly ICryptoProvider _cryptographyProvider;
private IEnumerable<IRequiresRegistration> _registeredEntities;
protected IEnumerable<IRequiresRegistration> RegisteredEntities
@@ -73,8 +78,8 @@ namespace MediaBrowser.Common.Implementations.Security
/// <summary>
/// Initializes a new instance of the <see cref="PluginSecurityManager" /> class.
/// </summary>
- public PluginSecurityManager(IApplicationHost appHost, IHttpClient httpClient, IJsonSerializer jsonSerializer,
- IApplicationPaths appPaths, ILogManager logManager)
+ public PluginSecurityManager(IServerApplicationHost appHost, IHttpClient httpClient, IJsonSerializer jsonSerializer,
+ IApplicationPaths appPaths, ILogManager logManager, IFileSystem fileSystem, ICryptoProvider cryptographyProvider)
{
if (httpClient == null)
{
@@ -85,6 +90,8 @@ namespace MediaBrowser.Common.Implementations.Security
_httpClient = httpClient;
_jsonSerializer = jsonSerializer;
_appPaths = appPaths;
+ _fileSystem = fileSystem;
+ _cryptographyProvider = cryptographyProvider;
_logger = logManager.GetLogger("SecurityManager");
}
@@ -186,7 +193,7 @@ namespace MediaBrowser.Common.Implementations.Security
{
var msg = "Result from appstore registration was null.";
_logger.Error(msg);
- throw new ApplicationException(msg);
+ throw new ArgumentException(msg);
}
if (!String.IsNullOrEmpty(reg.key))
{
@@ -195,7 +202,7 @@ namespace MediaBrowser.Common.Implementations.Security
}
}
- catch (ApplicationException)
+ catch (ArgumentException)
{
SaveAppStoreInfo(parameters);
throw;
@@ -208,14 +215,14 @@ namespace MediaBrowser.Common.Implementations.Security
{
throw new PaymentRequiredException();
}
- throw new ApplicationException("Error registering store sale");
+ throw new Exception("Error registering store sale");
}
catch (Exception e)
{
_logger.ErrorException("Error registering appstore purchase {0}", e, parameters ?? "NO PARMS SENT");
SaveAppStoreInfo(parameters);
//TODO - could create a re-try routine on start-up if this file is there. For now we can handle manually.
- throw new ApplicationException("Error registering store sale");
+ throw new Exception("Error registering store sale");
}
}
@@ -226,7 +233,7 @@ namespace MediaBrowser.Common.Implementations.Security
try
{
- File.WriteAllText(Path.Combine(_appPaths.ProgramDataPath, "apptrans-error.txt"), info);
+ _fileSystem.WriteAllText(Path.Combine(_appPaths.ProgramDataPath, "apptrans-error.txt"), info);
}
catch (IOException)
{
@@ -238,18 +245,29 @@ namespace MediaBrowser.Common.Implementations.Security
string mb2Equivalent = null,
string version = null)
{
- var lastChecked = LicenseFile.LastChecked(feature);
+ var regInfo = LicenseFile.GetRegInfo(feature);
+ var lastChecked = regInfo == null ? DateTime.MinValue : regInfo.LastChecked;
+ var expDate = regInfo == null ? DateTime.MinValue : regInfo.ExpirationDate;
+
+ var maxCacheDays = 14;
+ var nextCheckDate = new [] { expDate, lastChecked.AddDays(maxCacheDays) }.Min();
+
+ if (nextCheckDate > DateTime.UtcNow.AddDays(maxCacheDays))
+ {
+ nextCheckDate = DateTime.MinValue;
+ }
//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 = lastChecked > DateTime.UtcNow.AddDays(-7)
+ registered = regInfo != null && nextCheckDate >= DateTime.UtcNow && expDate >= DateTime.UtcNow,
+ expDate = expDate
};
var success = reg.registered;
- if (!(lastChecked > DateTime.UtcNow.AddDays(-1)))
+ if (!(lastChecked > DateTime.UtcNow.AddDays(-1)) || !reg.registered)
{
var data = new Dictionary<string, string>
{
@@ -284,7 +302,7 @@ namespace MediaBrowser.Common.Implementations.Security
if (reg.registered)
{
- LicenseFile.AddRegCheck(feature);
+ LicenseFile.AddRegCheck(feature, reg.expDate);
}
else
{
diff --git a/MediaBrowser.Common.Implementations/Security/RegRecord.cs b/Emby.Server.Implementations/Security/RegRecord.cs
index ece70b7726..d484085d3f 100644
--- a/MediaBrowser.Common.Implementations/Security/RegRecord.cs
+++ b/Emby.Server.Implementations/Security/RegRecord.cs
@@ -1,6 +1,6 @@
using System;
-namespace MediaBrowser.Common.Implementations.Security
+namespace Emby.Server.Implementations.Security
{
class RegRecord
{
diff --git a/MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs b/Emby.Server.Implementations/ServerManager/ServerManager.cs
index 893592fa38..f7e4c0ce2b 100644
--- a/MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs
+++ b/Emby.Server.Implementations/ServerManager/ServerManager.cs
@@ -10,12 +10,14 @@ using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
-using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Services;
+using MediaBrowser.Model.Text;
-namespace MediaBrowser.Server.Implementations.ServerManager
+namespace Emby.Server.Implementations.ServerManager
{
/// <summary>
/// Manages the Http Server, Udp Server and WebSocket connections
@@ -73,7 +75,8 @@ namespace MediaBrowser.Server.Implementations.ServerManager
private readonly List<IWebSocketListener> _webSocketListeners = new List<IWebSocketListener>();
private bool _disposed;
- private readonly IMemoryStreamProvider _memoryStreamProvider;
+ private readonly IMemoryStreamFactory _memoryStreamProvider;
+ private readonly ITextEncoding _textEncoding;
/// <summary>
/// Initializes a new instance of the <see cref="ServerManager" /> class.
@@ -83,7 +86,7 @@ namespace MediaBrowser.Server.Implementations.ServerManager
/// <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, IMemoryStreamProvider memoryStreamProvider)
+ public ServerManager(IServerApplicationHost applicationHost, IJsonSerializer jsonSerializer, ILogger logger, IServerConfigurationManager configurationManager, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding)
{
if (applicationHost == null)
{
@@ -103,37 +106,36 @@ namespace MediaBrowser.Server.Implementations.ServerManager
_applicationHost = applicationHost;
ConfigurationManager = configurationManager;
_memoryStreamProvider = memoryStreamProvider;
+ _textEncoding = textEncoding;
}
/// <summary>
/// Starts this instance.
/// </summary>
- public void Start(IEnumerable<string> urlPrefixes, string certificatePath)
+ public void Start(IEnumerable<string> urlPrefixes)
{
- ReloadHttpServer(urlPrefixes, certificatePath);
+ ReloadHttpServer(urlPrefixes);
}
/// <summary>
/// Restarts the Http Server, or starts it if not currently running
/// </summary>
- private void ReloadHttpServer(IEnumerable<string> urlPrefixes, string certificatePath)
+ private void ReloadHttpServer(IEnumerable<string> urlPrefixes)
{
_logger.Info("Loading Http Server");
try
{
HttpServer = _applicationHost.Resolve<IHttpServer>();
- HttpServer.StartServer(urlPrefixes, certificatePath);
- }
- catch (SocketException ex)
- {
- _logger.ErrorException("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.", ex);
-
- throw;
+ HttpServer.StartServer(urlPrefixes);
}
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);
throw;
}
@@ -153,11 +155,11 @@ namespace MediaBrowser.Server.Implementations.ServerManager
return;
}
- var connection = new WebSocketConnection(e.WebSocket, e.Endpoint, _jsonSerializer, _logger, _memoryStreamProvider)
+ var connection = new WebSocketConnection(e.WebSocket, e.Endpoint, _jsonSerializer, _logger, _memoryStreamProvider, _textEncoding)
{
OnReceive = ProcessWebSocketMessageReceived,
Url = e.Url,
- QueryString = new NameValueCollection(e.QueryString ?? new NameValueCollection())
+ QueryString = e.QueryString ?? new QueryParamCollection()
};
_webSocketConnections.Add(connection);
diff --git a/MediaBrowser.Server.Implementations/ServerManager/WebSocketConnection.cs b/Emby.Server.Implementations/ServerManager/WebSocketConnection.cs
index 60b04cf82f..4608a13e6f 100644
--- a/MediaBrowser.Server.Implementations/ServerManager/WebSocketConnection.cs
+++ b/Emby.Server.Implementations/ServerManager/WebSocketConnection.cs
@@ -10,9 +10,12 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Services;
+using MediaBrowser.Model.Text;
using UniversalDetector;
-namespace MediaBrowser.Server.Implementations.ServerManager
+namespace Emby.Server.Implementations.ServerManager
{
/// <summary>
/// Class WebSocketConnection
@@ -73,8 +76,9 @@ namespace MediaBrowser.Server.Implementations.ServerManager
/// Gets or sets the query string.
/// </summary>
/// <value>The query string.</value>
- public NameValueCollection QueryString { get; set; }
- private readonly IMemoryStreamProvider _memoryStreamProvider;
+ public QueryParamCollection QueryString { get; set; }
+ private readonly IMemoryStreamFactory _memoryStreamProvider;
+ private readonly ITextEncoding _textEncoding;
/// <summary>
/// Initializes a new instance of the <see cref="WebSocketConnection" /> class.
@@ -84,7 +88,7 @@ namespace MediaBrowser.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, IMemoryStreamProvider memoryStreamProvider)
+ public WebSocketConnection(IWebSocket socket, string remoteEndPoint, IJsonSerializer jsonSerializer, ILogger logger, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding)
{
if (socket == null)
{
@@ -111,6 +115,7 @@ namespace MediaBrowser.Server.Implementations.ServerManager
RemoteEndPoint = remoteEndPoint;
_logger = logger;
_memoryStreamProvider = memoryStreamProvider;
+ _textEncoding = textEncoding;
socket.Closed += socket_Closed;
}
@@ -136,11 +141,11 @@ namespace MediaBrowser.Server.Implementations.ServerManager
if (string.Equals(charset, "utf-8", StringComparison.OrdinalIgnoreCase))
{
- OnReceiveInternal(Encoding.UTF8.GetString(bytes));
+ OnReceiveInternal(Encoding.UTF8.GetString(bytes, 0, bytes.Length));
}
else
{
- OnReceiveInternal(Encoding.ASCII.GetString(bytes));
+ OnReceiveInternal(_textEncoding.GetASCIIEncoding().GetString(bytes, 0, bytes.Length));
}
}
private string DetectCharset(byte[] bytes)
diff --git a/MediaBrowser.Server.Implementations/Session/HttpSessionController.cs b/Emby.Server.Implementations/Session/HttpSessionController.cs
index f54c452cca..cea5d9b406 100644
--- a/MediaBrowser.Server.Implementations/Session/HttpSessionController.cs
+++ b/Emby.Server.Implementations/Session/HttpSessionController.cs
@@ -12,7 +12,7 @@ using System.Net;
using System.Threading;
using System.Threading.Tasks;
-namespace MediaBrowser.Server.Implementations.Session
+namespace Emby.Server.Implementations.Session
{
public class HttpSessionController : ISessionController, IDisposable
{
diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index 9326c4f43e..a20fb67b29 100644
--- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -30,8 +30,9 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.Threading;
-namespace MediaBrowser.Server.Implementations.Session
+namespace Emby.Server.Implementations.Session
{
/// <summary>
/// Class SessionManager
@@ -61,6 +62,7 @@ namespace MediaBrowser.Server.Implementations.Session
private readonly IAuthenticationRepository _authRepo;
private readonly IDeviceManager _deviceManager;
+ private readonly ITimerFactory _timerFactory;
/// <summary>
/// The _active connections
@@ -94,7 +96,7 @@ namespace MediaBrowser.Server.Implementations.Session
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)
+ 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;
_logger = logger;
@@ -109,6 +111,7 @@ namespace MediaBrowser.Server.Implementations.Session
_authRepo = authRepo;
_deviceManager = deviceManager;
_mediaSourceManager = mediaSourceManager;
+ _timerFactory = timerFactory;
_deviceManager.DeviceOptionsUpdated += _deviceManager_DeviceOptionsUpdated;
}
@@ -503,13 +506,13 @@ namespace MediaBrowser.Server.Implementations.Session
return users;
}
- private Timer _idleTimer;
+ private ITimer _idleTimer;
private void StartIdleCheckTimer()
{
if (_idleTimer == null)
{
- _idleTimer = new Timer(CheckForIdlePlayback, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
+ _idleTimer = _timerFactory.Create(CheckForIdlePlayback, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
}
}
private void StopIdleCheckTimer()
@@ -563,6 +566,23 @@ namespace MediaBrowser.Server.Implementations.Session
}
}
+ private BaseItem GetNowPlayingItem(SessionInfo session, string itemId)
+ {
+ var idGuid = new Guid(itemId);
+
+ var item = session.FullNowPlayingItem;
+ if (item != null && item.Id == idGuid)
+ {
+ return item;
+ }
+
+ item = _libraryManager.GetItemById(itemId);
+
+ session.FullNowPlayingItem = item;
+
+ return item;
+ }
+
/// <summary>
/// Used to report that playback has started for an item
/// </summary>
@@ -580,7 +600,7 @@ namespace MediaBrowser.Server.Implementations.Session
var libraryItem = string.IsNullOrWhiteSpace(info.ItemId)
? null
- : _libraryManager.GetItemById(new Guid(info.ItemId));
+ : GetNowPlayingItem(session, info.ItemId);
await UpdateNowPlayingItem(session, info, libraryItem).ConfigureAwait(false);
@@ -666,7 +686,7 @@ namespace MediaBrowser.Server.Implementations.Session
var libraryItem = string.IsNullOrWhiteSpace(info.ItemId)
? null
- : _libraryManager.GetItemById(new Guid(info.ItemId));
+ : GetNowPlayingItem(session, info.ItemId);
await UpdateNowPlayingItem(session, info, libraryItem).ConfigureAwait(false);
@@ -770,7 +790,7 @@ namespace MediaBrowser.Server.Implementations.Session
var libraryItem = string.IsNullOrWhiteSpace(info.ItemId)
? null
- : _libraryManager.GetItemById(new Guid(info.ItemId));
+ : GetNowPlayingItem(session, info.ItemId);
// Normalize
if (string.IsNullOrWhiteSpace(info.MediaSourceId))
@@ -800,6 +820,17 @@ namespace MediaBrowser.Server.Implementations.Session
}
}
+ if (info.Item != null)
+ {
+ var msString = info.PositionTicks.HasValue ? (info.PositionTicks.Value / 10000).ToString(CultureInfo.InvariantCulture) : "unknown";
+
+ _logger.Info("Playback stopped reported by app {0} {1} playing {2}. Stopped at {3} ms",
+ session.Client,
+ session.ApplicationVersion,
+ info.Item.Name,
+ msString);
+ }
+
RemoveNowPlayingItem(session);
var users = GetUsers(session);
@@ -854,7 +885,7 @@ namespace MediaBrowser.Server.Implementations.Session
{
playedToCompletion = _userDataManager.UpdatePlayState(item, data, positionTicks.Value);
}
- else
+ else
{
// If the client isn't able to report this, then we'll just have to make an assumption
data.PlayCount++;
@@ -953,7 +984,7 @@ namespace MediaBrowser.Server.Implementations.Session
var subItems = await TranslateItemForPlayback(itemId, user).ConfigureAwait(false);
list.AddRange(subItems);
}
-
+
items = list
.Where(i => i.LocationType != LocationType.Virtual)
.ToList();
@@ -1611,7 +1642,8 @@ namespace MediaBrowser.Server.Implementations.Session
IndexNumber = item.IndexNumber,
ParentIndexNumber = item.ParentIndexNumber,
PremiereDate = item.PremiereDate,
- ProductionYear = item.ProductionYear
+ ProductionYear = item.ProductionYear,
+ IsThemeMedia = item.IsThemeMedia
};
info.PrimaryImageTag = GetImageCacheTag(item, ImageType.Primary);
@@ -1778,18 +1810,18 @@ namespace MediaBrowser.Server.Implementations.Session
throw new ArgumentNullException("itemId");
}
- var item = _libraryManager.GetItemById(new Guid(itemId));
+ //var item = _libraryManager.GetItemById(new Guid(itemId));
- var info = GetItemInfo(item, null, null);
+ //var info = GetItemInfo(item, null, null);
- ReportNowViewingItem(sessionId, info);
+ //ReportNowViewingItem(sessionId, info);
}
public void ReportNowViewingItem(string sessionId, BaseItemInfo item)
{
- var session = GetSession(sessionId);
+ //var session = GetSession(sessionId);
- session.NowViewingItem = item;
+ //session.NowViewingItem = item;
}
public void ReportTranscodingInfo(string deviceId, TranscodingInfo info)
diff --git a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
index ddd7ba53a6..336c2caee4 100644
--- a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs
+++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
@@ -9,8 +9,9 @@ using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
-namespace MediaBrowser.Server.Implementations.Session
+namespace Emby.Server.Implementations.Session
{
/// <summary>
/// Class SessionWebSocketListener
@@ -104,7 +105,7 @@ namespace MediaBrowser.Server.Implementations.Session
//}
}
- private Task<SessionInfo> GetSession(NameValueCollection queryString, string remoteEndpoint)
+ private Task<SessionInfo> GetSession(QueryParamCollection queryString, string remoteEndpoint)
{
if (queryString == null)
{
diff --git a/MediaBrowser.Server.Implementations/Session/WebSocketController.cs b/Emby.Server.Implementations/Session/WebSocketController.cs
index 765664299e..f0ff0b5ddd 100644
--- a/MediaBrowser.Server.Implementations/Session/WebSocketController.cs
+++ b/Emby.Server.Implementations/Session/WebSocketController.cs
@@ -11,7 +11,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-namespace MediaBrowser.Server.Implementations.Session
+namespace Emby.Server.Implementations.Session
{
public class WebSocketController : ISessionController, IDisposable
{
diff --git a/MediaBrowser.Server.Implementations/Social/SharingManager.cs b/Emby.Server.Implementations/Social/SharingManager.cs
index 95f0ece0c2..54614c8797 100644
--- a/MediaBrowser.Server.Implementations/Social/SharingManager.cs
+++ b/Emby.Server.Implementations/Social/SharingManager.cs
@@ -3,21 +3,20 @@ using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Social;
using MediaBrowser.Model.Social;
using System;
using System.Threading.Tasks;
-namespace MediaBrowser.Server.Implementations.Social
+namespace Emby.Server.Implementations.Social
{
public class SharingManager : ISharingManager
{
- private readonly SharingRepository _repository;
+ private readonly ISharingRepository _repository;
private readonly IServerConfigurationManager _config;
private readonly ILibraryManager _libraryManager;
private readonly IServerApplicationHost _appHost;
- public SharingManager(SharingRepository repository, IServerConfigurationManager config, ILibraryManager libraryManager, IServerApplicationHost appHost)
+ public SharingManager(ISharingRepository repository, IServerConfigurationManager config, ILibraryManager libraryManager, IServerApplicationHost appHost)
{
_repository = repository;
_config = config;
diff --git a/Emby.Server.Implementations/Social/SharingRepository.cs b/Emby.Server.Implementations/Social/SharingRepository.cs
new file mode 100644
index 0000000000..e8230947ec
--- /dev/null
+++ b/Emby.Server.Implementations/Social/SharingRepository.cs
@@ -0,0 +1,116 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using Emby.Server.Implementations.Data;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Social;
+using SQLitePCL.pretty;
+
+namespace Emby.Server.Implementations.Social
+{
+ public class SharingRepository : BaseSqliteRepository, ISharingRepository
+ {
+ public SharingRepository(ILogger logger, IApplicationPaths appPaths)
+ : base(logger)
+ {
+ DbFilePath = Path.Combine(appPaths.DataPath, "shares.db");
+ }
+
+ /// <summary>
+ /// Opens the connection to the database
+ /// </summary>
+ /// <returns>Task.</returns>
+ public void Initialize()
+ {
+ using (var connection = CreateConnection())
+ {
+ RunDefaultInitialization(connection);
+
+ string[] queries = {
+
+ "create table if not exists Shares (Id GUID, ItemId TEXT, UserId TEXT, ExpirationDate DateTime, PRIMARY KEY (Id))",
+ "create index if not exists idx_Shares on Shares(Id)",
+
+ "pragma shrink_memory"
+ };
+
+ connection.RunQueries(queries);
+ }
+ }
+
+ public async Task 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.ToGuidParamValue(),
+ 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.ToGuidParamValue());
+
+ foreach (var row in connection.Query(commandText, paramList.ToArray()))
+ {
+ return GetSocialShareInfo(row);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private SocialShareInfo GetSocialShareInfo(IReadOnlyList<IResultSetValue> reader)
+ {
+ var info = new SocialShareInfo();
+
+ info.Id = reader[0].ReadGuid().ToString("N");
+ info.ItemId = reader[1].ToString();
+ info.UserId = reader[2].ToString();
+ info.ExpirationDate = reader[3].ReadDateTime();
+
+ return info;
+ }
+
+ public async Task DeleteShare(string id)
+ {
+
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Sorting/AirTimeComparer.cs b/Emby.Server.Implementations/Sorting/AirTimeComparer.cs
index 7e6a252cdc..bc05e9af3f 100644
--- a/MediaBrowser.Server.Implementations/Sorting/AirTimeComparer.cs
+++ b/Emby.Server.Implementations/Sorting/AirTimeComparer.cs
@@ -4,7 +4,7 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
using System;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
public class AirTimeComparer : IBaseItemComparer
{
diff --git a/MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs b/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs
index 91abbe34c9..494668cb9e 100644
--- a/MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs
+++ b/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs
@@ -4,7 +4,7 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
using System;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
class AiredEpisodeOrderComparer : IBaseItemComparer
{
diff --git a/MediaBrowser.Server.Implementations/Sorting/AlbumArtistComparer.cs b/Emby.Server.Implementations/Sorting/AlbumArtistComparer.cs
index 3c79b0c326..cd38340807 100644
--- a/MediaBrowser.Server.Implementations/Sorting/AlbumArtistComparer.cs
+++ b/Emby.Server.Implementations/Sorting/AlbumArtistComparer.cs
@@ -5,7 +5,7 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
using System;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
/// <summary>
/// Class AlbumArtistComparer
diff --git a/MediaBrowser.Server.Implementations/Sorting/AlbumComparer.cs b/Emby.Server.Implementations/Sorting/AlbumComparer.cs
index f455d5c2bd..68f5f173e0 100644
--- a/MediaBrowser.Server.Implementations/Sorting/AlbumComparer.cs
+++ b/Emby.Server.Implementations/Sorting/AlbumComparer.cs
@@ -4,7 +4,7 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
using System;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
/// <summary>
/// Class AlbumComparer
diff --git a/MediaBrowser.Server.Implementations/Sorting/AlphanumComparator.cs b/Emby.Server.Implementations/Sorting/AlphanumComparator.cs
index 232bdb3b58..4bfcda1acf 100644
--- a/MediaBrowser.Server.Implementations/Sorting/AlphanumComparator.cs
+++ b/Emby.Server.Implementations/Sorting/AlphanumComparator.cs
@@ -2,7 +2,7 @@
using System.Text;
using MediaBrowser.Controller.Sorting;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
public class AlphanumComparator : IComparer<string>
{
diff --git a/MediaBrowser.Server.Implementations/Sorting/ArtistComparer.cs b/Emby.Server.Implementations/Sorting/ArtistComparer.cs
index 9ff8a5ace3..edb195820f 100644
--- a/MediaBrowser.Server.Implementations/Sorting/ArtistComparer.cs
+++ b/Emby.Server.Implementations/Sorting/ArtistComparer.cs
@@ -4,7 +4,7 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
using System;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
/// <summary>
/// Class ArtistComparer
diff --git a/MediaBrowser.Server.Implementations/Sorting/BudgetComparer.cs b/Emby.Server.Implementations/Sorting/BudgetComparer.cs
index 87a7325c63..f3aef69f17 100644
--- a/MediaBrowser.Server.Implementations/Sorting/BudgetComparer.cs
+++ b/Emby.Server.Implementations/Sorting/BudgetComparer.cs
@@ -2,7 +2,7 @@
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
public class BudgetComparer : IBaseItemComparer
{
diff --git a/MediaBrowser.Server.Implementations/Sorting/CommunityRatingComparer.cs b/Emby.Server.Implementations/Sorting/CommunityRatingComparer.cs
index bdd18a648b..396bbbdb97 100644
--- a/MediaBrowser.Server.Implementations/Sorting/CommunityRatingComparer.cs
+++ b/Emby.Server.Implementations/Sorting/CommunityRatingComparer.cs
@@ -2,7 +2,7 @@
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
public class CommunityRatingComparer : IBaseItemComparer
{
diff --git a/MediaBrowser.Server.Implementations/Sorting/CriticRatingComparer.cs b/Emby.Server.Implementations/Sorting/CriticRatingComparer.cs
index 9484130cbc..877dbfcc1e 100644
--- a/MediaBrowser.Server.Implementations/Sorting/CriticRatingComparer.cs
+++ b/Emby.Server.Implementations/Sorting/CriticRatingComparer.cs
@@ -2,7 +2,7 @@
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
/// <summary>
/// Class CriticRatingComparer
diff --git a/MediaBrowser.Server.Implementations/Sorting/DateCreatedComparer.cs b/Emby.Server.Implementations/Sorting/DateCreatedComparer.cs
index 9862f0a8a6..c436fcb4a3 100644
--- a/MediaBrowser.Server.Implementations/Sorting/DateCreatedComparer.cs
+++ b/Emby.Server.Implementations/Sorting/DateCreatedComparer.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
using System;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
/// <summary>
/// Class DateCreatedComparer
diff --git a/MediaBrowser.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs b/Emby.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs
index 5080edffd5..fc92505ac4 100644
--- a/MediaBrowser.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs
+++ b/Emby.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs
@@ -4,7 +4,7 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
using System;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
public class DateLastMediaAddedComparer : IUserBaseItemComparer
{
diff --git a/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs b/Emby.Server.Implementations/Sorting/DatePlayedComparer.cs
index 3edf23020a..388d2772e8 100644
--- a/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs
+++ b/Emby.Server.Implementations/Sorting/DatePlayedComparer.cs
@@ -4,7 +4,7 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
using System;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
/// <summary>
/// Class DatePlayedComparer
diff --git a/MediaBrowser.Server.Implementations/Sorting/GameSystemComparer.cs b/Emby.Server.Implementations/Sorting/GameSystemComparer.cs
index eb83b98e97..4ee30397dc 100644
--- a/MediaBrowser.Server.Implementations/Sorting/GameSystemComparer.cs
+++ b/Emby.Server.Implementations/Sorting/GameSystemComparer.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
using System;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
public class GameSystemComparer : IBaseItemComparer
{
diff --git a/MediaBrowser.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs b/Emby.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs
index 658708dba5..27485f09e2 100644
--- a/MediaBrowser.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs
+++ b/Emby.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
public class IsFavoriteOrLikeComparer : IUserBaseItemComparer
{
diff --git a/MediaBrowser.Server.Implementations/Sorting/IsFolderComparer.cs b/Emby.Server.Implementations/Sorting/IsFolderComparer.cs
index d2341d0651..756d13bd87 100644
--- a/MediaBrowser.Server.Implementations/Sorting/IsFolderComparer.cs
+++ b/Emby.Server.Implementations/Sorting/IsFolderComparer.cs
@@ -2,7 +2,7 @@
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
public class IsFolderComparer : IBaseItemComparer
{
diff --git a/MediaBrowser.Server.Implementations/Sorting/IsPlayedComparer.cs b/Emby.Server.Implementations/Sorting/IsPlayedComparer.cs
index aebfbdb1c4..987dc54a51 100644
--- a/MediaBrowser.Server.Implementations/Sorting/IsPlayedComparer.cs
+++ b/Emby.Server.Implementations/Sorting/IsPlayedComparer.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
public class IsPlayedComparer : IUserBaseItemComparer
{
diff --git a/MediaBrowser.Server.Implementations/Sorting/IsUnplayedComparer.cs b/Emby.Server.Implementations/Sorting/IsUnplayedComparer.cs
index f1c6a5a4eb..0f4e4c37e2 100644
--- a/MediaBrowser.Server.Implementations/Sorting/IsUnplayedComparer.cs
+++ b/Emby.Server.Implementations/Sorting/IsUnplayedComparer.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
public class IsUnplayedComparer : IUserBaseItemComparer
{
diff --git a/MediaBrowser.Server.Implementations/Sorting/MetascoreComparer.cs b/Emby.Server.Implementations/Sorting/MetascoreComparer.cs
index bfd1626615..9759e02284 100644
--- a/MediaBrowser.Server.Implementations/Sorting/MetascoreComparer.cs
+++ b/Emby.Server.Implementations/Sorting/MetascoreComparer.cs
@@ -2,7 +2,7 @@
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
public class MetascoreComparer : IBaseItemComparer
{
diff --git a/MediaBrowser.Server.Implementations/Sorting/NameComparer.cs b/Emby.Server.Implementations/Sorting/NameComparer.cs
index 49f86c485a..8ab5e5172c 100644
--- a/MediaBrowser.Server.Implementations/Sorting/NameComparer.cs
+++ b/Emby.Server.Implementations/Sorting/NameComparer.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
using System;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
/// <summary>
/// Class NameComparer
diff --git a/MediaBrowser.Server.Implementations/Sorting/OfficialRatingComparer.cs b/Emby.Server.Implementations/Sorting/OfficialRatingComparer.cs
index dd31109daf..3eab4fccc9 100644
--- a/MediaBrowser.Server.Implementations/Sorting/OfficialRatingComparer.cs
+++ b/Emby.Server.Implementations/Sorting/OfficialRatingComparer.cs
@@ -1,9 +1,9 @@
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Sorting;
+using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Querying;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
public class OfficialRatingComparer : IBaseItemComparer
{
diff --git a/MediaBrowser.Server.Implementations/Sorting/PlayCountComparer.cs b/Emby.Server.Implementations/Sorting/PlayCountComparer.cs
index 8b14efffcf..aecad7c580 100644
--- a/MediaBrowser.Server.Implementations/Sorting/PlayCountComparer.cs
+++ b/Emby.Server.Implementations/Sorting/PlayCountComparer.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
/// <summary>
/// Class PlayCountComparer
diff --git a/MediaBrowser.Server.Implementations/Sorting/PlayersComparer.cs b/Emby.Server.Implementations/Sorting/PlayersComparer.cs
index 5bcd080d7b..3b54517c3e 100644
--- a/MediaBrowser.Server.Implementations/Sorting/PlayersComparer.cs
+++ b/Emby.Server.Implementations/Sorting/PlayersComparer.cs
@@ -2,7 +2,7 @@
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
public class PlayersComparer : IBaseItemComparer
{
diff --git a/MediaBrowser.Server.Implementations/Sorting/PremiereDateComparer.cs b/Emby.Server.Implementations/Sorting/PremiereDateComparer.cs
index ffe1fc24a1..d7219c86f4 100644
--- a/MediaBrowser.Server.Implementations/Sorting/PremiereDateComparer.cs
+++ b/Emby.Server.Implementations/Sorting/PremiereDateComparer.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
using System;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
/// <summary>
/// Class PremiereDateComparer
diff --git a/MediaBrowser.Server.Implementations/Sorting/ProductionYearComparer.cs b/Emby.Server.Implementations/Sorting/ProductionYearComparer.cs
index 16d5313347..ea479419ac 100644
--- a/MediaBrowser.Server.Implementations/Sorting/ProductionYearComparer.cs
+++ b/Emby.Server.Implementations/Sorting/ProductionYearComparer.cs
@@ -2,7 +2,7 @@
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
/// <summary>
/// Class ProductionYearComparer
diff --git a/MediaBrowser.Server.Implementations/Sorting/RandomComparer.cs b/Emby.Server.Implementations/Sorting/RandomComparer.cs
index b1677331ac..1fbecde566 100644
--- a/MediaBrowser.Server.Implementations/Sorting/RandomComparer.cs
+++ b/Emby.Server.Implementations/Sorting/RandomComparer.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
using System;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
/// <summary>
/// Class RandomComparer
diff --git a/MediaBrowser.Server.Implementations/Sorting/RevenueComparer.cs b/Emby.Server.Implementations/Sorting/RevenueComparer.cs
index 6caa27ac39..62e43eac1e 100644
--- a/MediaBrowser.Server.Implementations/Sorting/RevenueComparer.cs
+++ b/Emby.Server.Implementations/Sorting/RevenueComparer.cs
@@ -2,7 +2,7 @@
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
public class RevenueComparer : IBaseItemComparer
{
diff --git a/MediaBrowser.Server.Implementations/Sorting/RuntimeComparer.cs b/Emby.Server.Implementations/Sorting/RuntimeComparer.cs
index 793cb265e8..63c4758cb1 100644
--- a/MediaBrowser.Server.Implementations/Sorting/RuntimeComparer.cs
+++ b/Emby.Server.Implementations/Sorting/RuntimeComparer.cs
@@ -2,7 +2,7 @@
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
/// <summary>
/// Class RuntimeComparer
diff --git a/MediaBrowser.Server.Implementations/Sorting/SeriesSortNameComparer.cs b/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs
index 6bc1264a48..b315d33c30 100644
--- a/MediaBrowser.Server.Implementations/Sorting/SeriesSortNameComparer.cs
+++ b/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
using System;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
class SeriesSortNameComparer : IBaseItemComparer
{
diff --git a/MediaBrowser.Server.Implementations/Sorting/SortNameComparer.cs b/Emby.Server.Implementations/Sorting/SortNameComparer.cs
index 873753a2b2..f2a7648407 100644
--- a/MediaBrowser.Server.Implementations/Sorting/SortNameComparer.cs
+++ b/Emby.Server.Implementations/Sorting/SortNameComparer.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
using System;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
/// <summary>
/// Class SortNameComparer
diff --git a/MediaBrowser.Server.Implementations/Sorting/StartDateComparer.cs b/Emby.Server.Implementations/Sorting/StartDateComparer.cs
index 7e6f24ec1c..6be5f48830 100644
--- a/MediaBrowser.Server.Implementations/Sorting/StartDateComparer.cs
+++ b/Emby.Server.Implementations/Sorting/StartDateComparer.cs
@@ -4,7 +4,7 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
using System;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
public class StartDateComparer : IBaseItemComparer
{
diff --git a/MediaBrowser.Server.Implementations/Sorting/StudioComparer.cs b/Emby.Server.Implementations/Sorting/StudioComparer.cs
index 83ab4dfc26..6735022afd 100644
--- a/MediaBrowser.Server.Implementations/Sorting/StudioComparer.cs
+++ b/Emby.Server.Implementations/Sorting/StudioComparer.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
using System.Linq;
-namespace MediaBrowser.Server.Implementations.Sorting
+namespace Emby.Server.Implementations.Sorting
{
public class StudioComparer : IBaseItemComparer
{
diff --git a/MediaBrowser.Server.Startup.Common/StartupOptions.cs b/Emby.Server.Implementations/StartupOptions.cs
index 1acaf63486..159c36248c 100644
--- a/MediaBrowser.Server.Startup.Common/StartupOptions.cs
+++ b/Emby.Server.Implementations/StartupOptions.cs
@@ -2,11 +2,16 @@
using System.Collections.Generic;
using System.Linq;
-namespace MediaBrowser.Server.Startup.Common
+namespace Emby.Server.Implementations
{
public class StartupOptions
{
- private readonly List<string> _options = Environment.GetCommandLineArgs().ToList();
+ private readonly List<string> _options;
+
+ public StartupOptions(string[] commandLineArgs)
+ {
+ _options = commandLineArgs.ToList();
+ }
public bool ContainsOption(string option)
{
diff --git a/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs b/Emby.Server.Implementations/Sync/AppSyncProvider.cs
index 408ec717eb..d405a0ff94 100644
--- a/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs
+++ b/Emby.Server.Implementations/Sync/AppSyncProvider.cs
@@ -7,7 +7,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
-namespace MediaBrowser.Server.Implementations.Sync
+namespace Emby.Server.Implementations.Sync
{
public class AppSyncProvider : ISyncProvider, IHasUniqueTargetIds, IHasSyncQuality, IHasDuplicateCheck
{
diff --git a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs b/Emby.Server.Implementations/Sync/CloudSyncProfile.cs
index f40b644989..1a78c8ae66 100644
--- a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs
+++ b/Emby.Server.Implementations/Sync/CloudSyncProfile.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Dlna;
using System.Collections.Generic;
-namespace MediaBrowser.Server.Implementations.Sync
+namespace Emby.Server.Implementations.Sync
{
public class CloudSyncProfile : DeviceProfile
{
diff --git a/MediaBrowser.Server.Implementations/Sync/IHasSyncQuality.cs b/Emby.Server.Implementations/Sync/IHasSyncQuality.cs
index e7eee09232..bec8b37a77 100644
--- a/MediaBrowser.Server.Implementations/Sync/IHasSyncQuality.cs
+++ b/Emby.Server.Implementations/Sync/IHasSyncQuality.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Sync;
using System.Collections.Generic;
-namespace MediaBrowser.Server.Implementations.Sync
+namespace Emby.Server.Implementations.Sync
{
public interface IHasSyncQuality
{
diff --git a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs b/Emby.Server.Implementations/Sync/MediaSync.cs
index 3218ac5e76..fa8388b6cd 100644
--- a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs
+++ b/Emby.Server.Implementations/Sync/MediaSync.cs
@@ -13,14 +13,14 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
-using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
-using Interfaces.IO;
+using Emby.Server.Implementations.IO;
+using MediaBrowser.Model.Cryptography;
+using MediaBrowser.Model.IO;
-namespace MediaBrowser.Server.Implementations.Sync
+namespace Emby.Server.Implementations.Sync
{
public class MediaSync
{
@@ -29,17 +29,19 @@ namespace MediaBrowser.Server.Implementations.Sync
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
private readonly IConfigurationManager _config;
+ private readonly ICryptoProvider _cryptographyProvider;
public const string PathSeparatorString = "/";
public const char PathSeparatorChar = '/';
- public MediaSync(ILogger logger, ISyncManager syncManager, IServerApplicationHost appHost, IFileSystem fileSystem, IConfigurationManager config)
+ public MediaSync(ILogger logger, ISyncManager syncManager, IServerApplicationHost appHost, IFileSystem fileSystem, IConfigurationManager config, ICryptoProvider cryptographyProvider)
{
_logger = logger;
_syncManager = syncManager;
_appHost = appHost;
_fileSystem = fileSystem;
_config = config;
+ _cryptographyProvider = cryptographyProvider;
}
public async Task Sync(IServerSyncProvider provider,
@@ -76,8 +78,8 @@ namespace MediaBrowser.Server.Implementations.Sync
CancellationToken cancellationToken)
{
var localItems = await dataProvider.GetLocalItems(target, serverId).ConfigureAwait(false);
- var remoteFiles = await provider.GetFiles(new FileQuery(), target, cancellationToken).ConfigureAwait(false);
- var remoteIds = remoteFiles.Items.Select(i => i.Id).ToList();
+ var remoteFiles = await provider.GetFiles(target, cancellationToken).ConfigureAwait(false);
+ var remoteIds = remoteFiles.Items.Select(i => i.FullName).ToList();
var jobItemIds = new List<string>();
@@ -346,7 +348,7 @@ namespace MediaBrowser.Server.Implementations.Sync
return await supportsDirectCopy.SendFile(inputPath, pathParts, target, progress, cancellationToken).ConfigureAwait(false);
}
- using (var fileStream = _fileSystem.GetFileStream(inputPath, FileMode.Open, FileAccess.Read, FileShare.Read, true))
+ using (var fileStream = _fileSystem.GetFileStream(inputPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true))
{
Stream stream = fileStream;
@@ -359,19 +361,16 @@ namespace MediaBrowser.Server.Implementations.Sync
}
}
- private static string GetLocalId(string jobItemId, string itemId)
+ private string GetLocalId(string jobItemId, string itemId)
{
var bytes = Encoding.UTF8.GetBytes(jobItemId + itemId);
bytes = CreateMd5(bytes);
return BitConverter.ToString(bytes, 0, bytes.Length).Replace("-", string.Empty);
}
- private static byte[] CreateMd5(byte[] value)
+ private byte[] CreateMd5(byte[] value)
{
- using (var provider = MD5.Create())
- {
- return provider.ComputeHash(value);
- }
+ return _cryptographyProvider.ComputeMD5(value);
}
public LocalItem CreateLocalItem(IServerSyncProvider provider, SyncedItem syncedItem, SyncJob job, SyncTarget target, BaseItemDto libraryItem, string serverId, string serverName, string originalFileName)
diff --git a/MediaBrowser.Server.Implementations/Sync/MultiProviderSync.cs b/Emby.Server.Implementations/Sync/MultiProviderSync.cs
index 97b2b1eb8b..8189b8550f 100644
--- a/MediaBrowser.Server.Implementations/Sync/MultiProviderSync.cs
+++ b/Emby.Server.Implementations/Sync/MultiProviderSync.cs
@@ -9,9 +9,12 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.Cryptography;
+using MediaBrowser.Model.IO;
-namespace MediaBrowser.Server.Implementations.Sync
+namespace Emby.Server.Implementations.Sync
{
public class MultiProviderSync
{
@@ -20,14 +23,16 @@ namespace MediaBrowser.Server.Implementations.Sync
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
private readonly IConfigurationManager _config;
+ private readonly ICryptoProvider _cryptographyProvider;
- public MultiProviderSync(SyncManager syncManager, IServerApplicationHost appHost, ILogger logger, IFileSystem fileSystem, IConfigurationManager config)
+ public MultiProviderSync(SyncManager syncManager, IServerApplicationHost appHost, ILogger logger, IFileSystem fileSystem, IConfigurationManager config, ICryptoProvider cryptographyProvider)
{
_syncManager = syncManager;
_appHost = appHost;
_logger = logger;
_fileSystem = fileSystem;
_config = config;
+ _cryptographyProvider = cryptographyProvider;
}
public async Task Sync(IEnumerable<IServerSyncProvider> providers, IProgress<double> progress, CancellationToken cancellationToken)
@@ -59,7 +64,7 @@ namespace MediaBrowser.Server.Implementations.Sync
var dataProvider = _syncManager.GetDataProvider(target.Item1, target.Item2);
- await new MediaSync(_logger, _syncManager, _appHost, _fileSystem, _config)
+ await new MediaSync(_logger, _syncManager, _appHost, _fileSystem, _config, _cryptographyProvider)
.Sync(target.Item1, dataProvider, target.Item2, innerProgress, cancellationToken)
.ConfigureAwait(false);
diff --git a/MediaBrowser.Server.Implementations/Sync/ServerSyncScheduledTask.cs b/Emby.Server.Implementations/Sync/ServerSyncScheduledTask.cs
index 28813c715d..09a0bfde4c 100644
--- a/MediaBrowser.Server.Implementations/Sync/ServerSyncScheduledTask.cs
+++ b/Emby.Server.Implementations/Sync/ServerSyncScheduledTask.cs
@@ -1,5 +1,4 @@
using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Sync;
using MediaBrowser.Model.Logging;
@@ -8,25 +7,29 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.Cryptography;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Server.Implementations.Sync
+namespace Emby.Server.Implementations.Sync
{
- class ServerSyncScheduledTask : IScheduledTask, IConfigurableScheduledTask, IHasKey
+ class ServerSyncScheduledTask : IScheduledTask, IConfigurableScheduledTask
{
private readonly ISyncManager _syncManager;
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
private readonly IServerApplicationHost _appHost;
private readonly IConfigurationManager _config;
+ private readonly ICryptoProvider _cryptographyProvider;
- public ServerSyncScheduledTask(ISyncManager syncManager, ILogger logger, IFileSystem fileSystem, IServerApplicationHost appHost, IConfigurationManager config)
+ public ServerSyncScheduledTask(ISyncManager syncManager, ILogger logger, IFileSystem fileSystem, IServerApplicationHost appHost, IConfigurationManager config, ICryptoProvider cryptographyProvider)
{
_syncManager = syncManager;
_logger = logger;
_fileSystem = fileSystem;
_appHost = appHost;
_config = config;
+ _cryptographyProvider = cryptographyProvider;
}
public string Name
@@ -49,7 +52,7 @@ namespace MediaBrowser.Server.Implementations.Sync
public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
{
- return new MultiProviderSync((SyncManager)_syncManager, _appHost, _logger, _fileSystem, _config)
+ return new MultiProviderSync((SyncManager)_syncManager, _appHost, _logger, _fileSystem, _config, _cryptographyProvider)
.Sync(ServerSyncProviders, progress, cancellationToken);
}
@@ -58,14 +61,17 @@ namespace MediaBrowser.Server.Implementations.Sync
get { return ((SyncManager)_syncManager).ServerSyncProviders; }
}
- public IEnumerable<ITaskTrigger> GetDefaultTriggers()
+ /// <summary>
+ /// Creates the triggers that define when the task will run
+ /// </summary>
+ public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- return new ITaskTrigger[]
- {
- new IntervalTrigger { Interval = TimeSpan.FromHours(3) }
- };
+ return new[] {
+
+ // Every so often
+ new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(3).Ticks}
+ };
}
-
public bool IsHidden
{
get { return !IsEnabled; }
@@ -76,6 +82,11 @@ namespace MediaBrowser.Server.Implementations.Sync
get { return ServerSyncProviders.Any(); }
}
+ public bool IsLogged
+ {
+ get { return true; }
+ }
+
public string Key
{
get { return "ServerSync"; }
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncConfig.cs b/Emby.Server.Implementations/Sync/SyncConfig.cs
index 52c7743307..8a97326bda 100644
--- a/MediaBrowser.Server.Implementations/Sync/SyncConfig.cs
+++ b/Emby.Server.Implementations/Sync/SyncConfig.cs
@@ -2,7 +2,7 @@
using MediaBrowser.Model.Sync;
using System.Collections.Generic;
-namespace MediaBrowser.Server.Implementations.Sync
+namespace Emby.Server.Implementations.Sync
{
public class SyncConfigurationFactory : IConfigurationFactory
{
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncConvertScheduledTask.cs b/Emby.Server.Implementations/Sync/SyncConvertScheduledTask.cs
index 3f9eb76918..8dafac7e10 100644
--- a/MediaBrowser.Server.Implementations/Sync/SyncConvertScheduledTask.cs
+++ b/Emby.Server.Implementations/Sync/SyncConvertScheduledTask.cs
@@ -1,5 +1,4 @@
using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Sync;
@@ -9,11 +8,14 @@ using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Server.Implementations.Sync
+namespace Emby.Server.Implementations.Sync
{
- public class SyncConvertScheduledTask : IScheduledTask, IConfigurableScheduledTask, IHasKey
+ public class SyncConvertScheduledTask : IScheduledTask
{
private readonly ILibraryManager _libraryManager;
private readonly ISyncRepository _syncRepo;
@@ -66,22 +68,17 @@ namespace MediaBrowser.Server.Implementations.Sync
.Sync(progress, cancellationToken);
}
- public IEnumerable<ITaskTrigger> GetDefaultTriggers()
+ /// <summary>
+ /// Creates the triggers that define when the task will run
+ /// </summary>
+ /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
+ public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
- return new ITaskTrigger[]
- {
- new IntervalTrigger { Interval = TimeSpan.FromHours(3) }
- };
- }
-
- public bool IsHidden
- {
- get { return false; }
- }
-
- public bool IsEnabled
- {
- get { return true; }
+ return new[] {
+
+ // Every so often
+ new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(3).Ticks}
+ };
}
public string Key
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncHelper.cs b/Emby.Server.Implementations/Sync/SyncHelper.cs
index fb4e0c6be0..da475f0038 100644
--- a/MediaBrowser.Server.Implementations/Sync/SyncHelper.cs
+++ b/Emby.Server.Implementations/Sync/SyncHelper.cs
@@ -1,6 +1,6 @@
using System;
-namespace MediaBrowser.Server.Implementations.Sync
+namespace Emby.Server.Implementations.Sync
{
public class SyncHelper
{
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncJobOptions.cs b/Emby.Server.Implementations/Sync/SyncJobOptions.cs
index cb8141c895..8e4d8e2edc 100644
--- a/MediaBrowser.Server.Implementations/Sync/SyncJobOptions.cs
+++ b/Emby.Server.Implementations/Sync/SyncJobOptions.cs
@@ -1,6 +1,6 @@
using MediaBrowser.Model.Dlna;
-namespace MediaBrowser.Server.Implementations.Sync
+namespace Emby.Server.Implementations.Sync
{
public class SyncJobOptions
{
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs b/Emby.Server.Implementations/Sync/SyncJobProcessor.cs
index d5dfd38569..415757609d 100644
--- a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs
+++ b/Emby.Server.Implementations/Sync/SyncJobProcessor.cs
@@ -16,7 +16,6 @@ using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Session;
using MediaBrowser.Model.Sync;
-using MoreLinq;
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -24,9 +23,12 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.IO;
-namespace MediaBrowser.Server.Implementations.Sync
+namespace Emby.Server.Implementations.Sync
{
public class SyncJobProcessor
{
@@ -769,7 +771,7 @@ namespace MediaBrowser.Server.Implementations.Sync
using (var stream = await _subtitleEncoder.GetSubtitles(streamInfo.ItemId, streamInfo.MediaSourceId, subtitleStreamIndex, subtitleStreamInfo.Format, 0, null, false, cancellationToken).ConfigureAwait(false))
{
- using (var fs = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true))
+ using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
{
await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/Emby.Server.Implementations/Sync/SyncManager.cs
index c523ec7bde..13f60f5eea 100644
--- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs
+++ b/Emby.Server.Implementations/Sync/SyncManager.cs
@@ -1,7 +1,6 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
@@ -21,18 +20,18 @@ using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Sync;
using MediaBrowser.Model.Users;
-using MoreLinq;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
-using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Tasks;
-namespace MediaBrowser.Server.Implementations.Sync
+namespace Emby.Server.Implementations.Sync
{
public class SyncManager : ISyncManager
{
@@ -52,7 +51,7 @@ namespace MediaBrowser.Server.Implementations.Sync
private readonly Func<IMediaSourceManager> _mediaSourceManager;
private readonly IJsonSerializer _json;
private readonly ITaskManager _taskManager;
- private readonly IMemoryStreamProvider _memoryStreamProvider;
+ private readonly IMemoryStreamFactory _memoryStreamProvider;
private ISyncProvider[] _providers = { };
@@ -62,7 +61,7 @@ namespace MediaBrowser.Server.Implementations.Sync
public event EventHandler<GenericEventArgs<SyncJobItem>> SyncJobItemUpdated;
public event EventHandler<GenericEventArgs<SyncJobItem>> SyncJobItemCreated;
- public SyncManager(ILibraryManager libraryManager, ISyncRepository repo, IImageProcessor imageProcessor, ILogger logger, IUserManager userManager, Func<IDtoService> dtoService, IServerApplicationHost appHost, ITVSeriesManager tvSeriesManager, Func<IMediaEncoder> mediaEncoder, IFileSystem fileSystem, Func<ISubtitleEncoder> subtitleEncoder, IConfigurationManager config, IUserDataManager userDataManager, Func<IMediaSourceManager> mediaSourceManager, IJsonSerializer json, ITaskManager taskManager, IMemoryStreamProvider memoryStreamProvider)
+ public SyncManager(ILibraryManager libraryManager, ISyncRepository repo, IImageProcessor imageProcessor, ILogger logger, IUserManager userManager, Func<IDtoService> dtoService, IServerApplicationHost appHost, ITVSeriesManager tvSeriesManager, Func<IMediaEncoder> mediaEncoder, IFileSystem fileSystem, Func<ISubtitleEncoder> subtitleEncoder, IConfigurationManager config, IUserDataManager userDataManager, Func<IMediaSourceManager> mediaSourceManager, IJsonSerializer json, ITaskManager taskManager, IMemoryStreamFactory memoryStreamProvider)
{
_libraryManager = libraryManager;
_repo = repo;
@@ -126,7 +125,7 @@ namespace MediaBrowser.Server.Implementations.Sync
if (string.IsNullOrWhiteSpace(request.Name))
{
- request.Name = DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString();
+ request.Name = DateTime.Now.ToString("f1", CultureInfo.CurrentCulture);
}
var target = GetSyncTargets(request.UserId)
@@ -421,7 +420,7 @@ namespace MediaBrowser.Server.Implementations.Sync
{
_fileSystem.DeleteDirectory(path, true);
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
}
@@ -576,7 +575,7 @@ namespace MediaBrowser.Server.Implementations.Sync
{
_fileSystem.DeleteDirectory(jobItem.TemporaryPath, true);
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
}
catch (Exception ex)
@@ -602,7 +601,10 @@ namespace MediaBrowser.Server.Implementations.Sync
if (query.AddMetadata)
{
- result.Items.ForEach(FillMetadata);
+ foreach (var item in result.Items)
+ {
+ FillMetadata(item);
+ }
}
return result;
@@ -1060,7 +1062,7 @@ namespace MediaBrowser.Server.Implementations.Sync
{
_fileSystem.DeleteDirectory(path, true);
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
}
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncNotificationEntryPoint.cs b/Emby.Server.Implementations/Sync/SyncNotificationEntryPoint.cs
index 7017b422ee..46cdb28a4d 100644
--- a/MediaBrowser.Server.Implementations/Sync/SyncNotificationEntryPoint.cs
+++ b/Emby.Server.Implementations/Sync/SyncNotificationEntryPoint.cs
@@ -5,7 +5,7 @@ using MediaBrowser.Controller.Sync;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Sync;
-namespace MediaBrowser.Server.Implementations.Sync
+namespace Emby.Server.Implementations.Sync
{
public class SyncNotificationEntryPoint : IServerEntryPoint
{
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncRegistrationInfo.cs b/Emby.Server.Implementations/Sync/SyncRegistrationInfo.cs
index 40b84b1c21..c2658c5c54 100644
--- a/MediaBrowser.Server.Implementations/Sync/SyncRegistrationInfo.cs
+++ b/Emby.Server.Implementations/Sync/SyncRegistrationInfo.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Common.Security;
using System.Threading.Tasks;
-namespace MediaBrowser.Server.Implementations.Sync
+namespace Emby.Server.Implementations.Sync
{
public class SyncRegistrationInfo : IRequiresRegistration
{
diff --git a/Emby.Server.Implementations/Sync/SyncRepository.cs b/Emby.Server.Implementations/Sync/SyncRepository.cs
new file mode 100644
index 0000000000..885f8e64a6
--- /dev/null
+++ b/Emby.Server.Implementations/Sync/SyncRepository.cs
@@ -0,0 +1,820 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Emby.Server.Implementations.Data;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Sync;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Sync;
+using SQLitePCL.pretty;
+
+namespace Emby.Server.Implementations.Sync
+{
+ public class SyncRepository : BaseSqliteRepository, ISyncRepository
+ {
+ private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+
+ private readonly IJsonSerializer _json;
+
+ public SyncRepository(ILogger logger, IJsonSerializer json, IServerApplicationPaths appPaths)
+ : base(logger)
+ {
+ _json = json;
+ DbFilePath = Path.Combine(appPaths.DataPath, "sync14.db");
+ }
+
+ private class SyncSummary
+ {
+ public Dictionary<string, int> Items { get; set; }
+
+ public SyncSummary()
+ {
+ Items = new Dictionary<string, int>();
+ }
+ }
+
+ public void Initialize()
+ {
+ using (var connection = CreateConnection())
+ {
+ RunDefaultInitialization(connection);
+
+ string[] queries = {
+
+ "create table if not exists SyncJobs (Id GUID PRIMARY KEY, TargetId TEXT NOT NULL, Name TEXT NOT NULL, Profile TEXT, Quality TEXT, Bitrate INT, Status TEXT NOT NULL, Progress FLOAT, UserId TEXT NOT NULL, ItemIds TEXT NOT NULL, Category TEXT, ParentId TEXT, UnwatchedOnly BIT, ItemLimit INT, SyncNewContent BIT, DateCreated DateTime, DateLastModified DateTime, ItemCount int)",
+
+ "create table if not exists SyncJobItems (Id GUID PRIMARY KEY, ItemId TEXT, ItemName TEXT, MediaSourceId TEXT, JobId TEXT, TemporaryPath TEXT, OutputPath TEXT, Status TEXT, TargetId TEXT, DateCreated DateTime, Progress FLOAT, AdditionalFiles TEXT, MediaSource TEXT, IsMarkedForRemoval BIT, JobItemIndex INT, ItemDateModifiedTicks BIGINT)",
+
+ "drop index if exists idx_SyncJobItems2",
+ "drop index if exists idx_SyncJobItems3",
+ "drop index if exists idx_SyncJobs1",
+ "drop index if exists idx_SyncJobs",
+ "drop index if exists idx_SyncJobItems1",
+ "create index if not exists idx_SyncJobItems4 on SyncJobItems(TargetId,ItemId,Status,Progress,DateCreated)",
+ "create index if not exists idx_SyncJobItems5 on SyncJobItems(TargetId,Status,ItemId,Progress)",
+
+ "create index if not exists idx_SyncJobs2 on SyncJobs(TargetId,Status,ItemIds,Progress)",
+
+ "pragma shrink_memory"
+ };
+
+ connection.RunQueries(queries);
+
+ connection.RunInTransaction(db =>
+ {
+ var existingColumnNames = GetColumnNames(db, "SyncJobs");
+ AddColumn(db, "SyncJobs", "Profile", "TEXT", existingColumnNames);
+ AddColumn(db, "SyncJobs", "Bitrate", "INT", existingColumnNames);
+
+ existingColumnNames = GetColumnNames(db, "SyncJobItems");
+ AddColumn(db, "SyncJobItems", "ItemDateModifiedTicks", "BIGINT", existingColumnNames);
+ }, TransactionMode);
+ }
+ }
+
+ protected override bool EnableTempStoreMemory
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ private const string BaseJobSelectText = "select Id, TargetId, Name, Profile, Quality, Bitrate, Status, Progress, UserId, ItemIds, Category, ParentId, UnwatchedOnly, ItemLimit, SyncNewContent, DateCreated, DateLastModified, ItemCount from SyncJobs";
+ private const string BaseJobItemSelectText = "select Id, ItemId, ItemName, MediaSourceId, JobId, TemporaryPath, OutputPath, Status, TargetId, DateCreated, Progress, AdditionalFiles, MediaSource, IsMarkedForRemoval, JobItemIndex, ItemDateModifiedTicks from SyncJobItems";
+
+ public SyncJob GetJob(string id)
+ {
+ if (string.IsNullOrEmpty(id))
+ {
+ throw new ArgumentNullException("id");
+ }
+
+ CheckDisposed();
+
+ var guid = new Guid(id);
+
+ if (guid == Guid.Empty)
+ {
+ throw new ArgumentNullException("id");
+ }
+
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
+ {
+ var commandText = BaseJobSelectText + " where Id=?";
+ var paramList = new List<object>();
+
+ paramList.Add(guid.ToGuidParamValue());
+
+ foreach (var row in connection.Query(commandText, paramList.ToArray()))
+ {
+ return GetJob(row);
+ }
+
+ return null;
+ }
+ }
+ }
+
+ private SyncJob GetJob(IReadOnlyList<IResultSetValue> reader)
+ {
+ var info = new SyncJob
+ {
+ Id = reader[0].ReadGuid().ToString("N"),
+ TargetId = reader[1].ToString(),
+ Name = reader[2].ToString()
+ };
+
+ if (reader[3].SQLiteType != SQLiteType.Null)
+ {
+ info.Profile = reader[3].ToString();
+ }
+
+ if (reader[4].SQLiteType != SQLiteType.Null)
+ {
+ info.Quality = reader[4].ToString();
+ }
+
+ if (reader[5].SQLiteType != SQLiteType.Null)
+ {
+ info.Bitrate = reader[5].ToInt();
+ }
+
+ if (reader[6].SQLiteType != SQLiteType.Null)
+ {
+ info.Status = (SyncJobStatus)Enum.Parse(typeof(SyncJobStatus), reader[6].ToString(), true);
+ }
+
+ if (reader[7].SQLiteType != SQLiteType.Null)
+ {
+ info.Progress = reader[7].ToDouble();
+ }
+
+ if (reader[8].SQLiteType != SQLiteType.Null)
+ {
+ info.UserId = reader[8].ToString();
+ }
+
+ if (reader[9].SQLiteType != SQLiteType.Null)
+ {
+ info.RequestedItemIds = reader[9].ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
+ }
+
+ if (reader[10].SQLiteType != SQLiteType.Null)
+ {
+ info.Category = (SyncCategory)Enum.Parse(typeof(SyncCategory), reader[10].ToString(), true);
+ }
+
+ if (reader[11].SQLiteType != SQLiteType.Null)
+ {
+ info.ParentId = reader[11].ToString();
+ }
+
+ if (reader[12].SQLiteType != SQLiteType.Null)
+ {
+ info.UnwatchedOnly = reader[12].ToBool();
+ }
+
+ if (reader[13].SQLiteType != SQLiteType.Null)
+ {
+ info.ItemLimit = reader[13].ToInt();
+ }
+
+ info.SyncNewContent = reader[14].ToBool();
+
+ info.DateCreated = reader[15].ReadDateTime();
+ info.DateLastModified = reader[16].ReadDateTime();
+ info.ItemCount = reader[17].ToInt();
+
+ return info;
+ }
+
+ public Task Create(SyncJob job)
+ {
+ return InsertOrUpdate(job, true);
+ }
+
+ public Task Update(SyncJob job)
+ {
+ return InsertOrUpdate(job, false);
+ }
+
+ private async Task InsertOrUpdate(SyncJob job, bool insert)
+ {
+ if (job == null)
+ {
+ throw new ArgumentNullException("job");
+ }
+
+ CheckDisposed();
+
+ using (WriteLock.Write())
+ {
+ using (var connection = CreateConnection())
+ {
+ string commandText;
+ var paramList = new List<object>();
+
+ if (insert)
+ {
+ commandText = "insert into SyncJobs (Id, TargetId, Name, Profile, Quality, Bitrate, Status, Progress, UserId, ItemIds, Category, ParentId, UnwatchedOnly, ItemLimit, SyncNewContent, DateCreated, DateLastModified, ItemCount) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+ }
+ else
+ {
+ commandText = "update SyncJobs set TargetId=?,Name=?,Profile=?,Quality=?,Bitrate=?,Status=?,Progress=?,UserId=?,ItemIds=?,Category=?,ParentId=?,UnwatchedOnly=?,ItemLimit=?,SyncNewContent=?,DateCreated=?,DateLastModified=?,ItemCount=? where Id=?";
+ }
+
+ paramList.Add(job.TargetId);
+ paramList.Add(job.Name);
+ paramList.Add(job.Profile);
+ paramList.Add(job.Quality);
+ paramList.Add(job.Bitrate);
+ paramList.Add(job.Status.ToString());
+ paramList.Add(job.Progress);
+ paramList.Add(job.UserId);
+
+ paramList.Add(string.Join(",", job.RequestedItemIds.ToArray()));
+ paramList.Add(job.Category);
+ paramList.Add(job.ParentId);
+ paramList.Add(job.UnwatchedOnly);
+ paramList.Add(job.ItemLimit);
+ paramList.Add(job.SyncNewContent);
+ paramList.Add(job.DateCreated.ToDateTimeParamValue());
+ paramList.Add(job.DateLastModified.ToDateTimeParamValue());
+ paramList.Add(job.ItemCount);
+
+ if (insert)
+ {
+ paramList.Insert(0, job.Id.ToGuidParamValue());
+ }
+ else
+ {
+ paramList.Add(job.Id.ToGuidParamValue());
+ }
+
+ connection.RunInTransaction(conn =>
+ {
+ conn.Execute(commandText, paramList.ToArray());
+ }, TransactionMode);
+ }
+ }
+ }
+
+ public async Task DeleteJob(string id)
+ {
+ if (string.IsNullOrWhiteSpace(id))
+ {
+ throw new ArgumentNullException("id");
+ }
+
+ CheckDisposed();
+
+ using (WriteLock.Write())
+ {
+ using (var connection = CreateConnection())
+ {
+ connection.RunInTransaction(conn =>
+ {
+ conn.Execute("delete from SyncJobs where Id=?", id.ToGuidParamValue());
+ conn.Execute("delete from SyncJobItems where JobId=?", id);
+ }, TransactionMode);
+ }
+ }
+ }
+
+ public QueryResult<SyncJob> GetJobs(SyncJobQuery query)
+ {
+ if (query == null)
+ {
+ throw new ArgumentNullException("query");
+ }
+
+ CheckDisposed();
+
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
+ {
+ var commandText = BaseJobSelectText;
+ var paramList = new List<object>();
+
+ var whereClauses = new List<string>();
+
+ if (query.Statuses.Length > 0)
+ {
+ var statuses = string.Join(",", query.Statuses.Select(i => "'" + i.ToString() + "'").ToArray());
+
+ whereClauses.Add(string.Format("Status in ({0})", statuses));
+ }
+ if (!string.IsNullOrWhiteSpace(query.TargetId))
+ {
+ whereClauses.Add("TargetId=?");
+ paramList.Add(query.TargetId);
+ }
+ if (!string.IsNullOrWhiteSpace(query.ExcludeTargetIds))
+ {
+ var excludeIds = (query.ExcludeTargetIds ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+ if (excludeIds.Length == 1)
+ {
+ whereClauses.Add("TargetId<>?");
+ paramList.Add(excludeIds[0]);
+ }
+ else if (excludeIds.Length > 1)
+ {
+ whereClauses.Add("TargetId<>?");
+ paramList.Add(excludeIds[0]);
+ }
+ }
+ if (!string.IsNullOrWhiteSpace(query.UserId))
+ {
+ whereClauses.Add("UserId=?");
+ paramList.Add(query.UserId);
+ }
+ if (query.SyncNewContent.HasValue)
+ {
+ whereClauses.Add("SyncNewContent=?");
+ paramList.Add(query.SyncNewContent.Value);
+ }
+
+ commandText += " mainTable";
+
+ var whereTextWithoutPaging = whereClauses.Count == 0 ?
+ string.Empty :
+ " where " + string.Join(" AND ", whereClauses.ToArray());
+
+ var startIndex = query.StartIndex ?? 0;
+ if (startIndex > 0)
+ {
+ whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM SyncJobs ORDER BY (Select Max(DateLastModified) from SyncJobs where TargetId=mainTable.TargetId) DESC, DateLastModified DESC LIMIT {0})",
+ startIndex.ToString(_usCulture)));
+ }
+
+ if (whereClauses.Count > 0)
+ {
+ commandText += " where " + string.Join(" AND ", whereClauses.ToArray());
+ }
+
+ commandText += " ORDER BY (Select Max(DateLastModified) from SyncJobs where TargetId=mainTable.TargetId) DESC, DateLastModified DESC";
+
+ if (query.Limit.HasValue)
+ {
+ commandText += " LIMIT " + query.Limit.Value.ToString(_usCulture);
+ }
+
+ var list = new List<SyncJob>();
+ var count = connection.Query("select count (Id) from SyncJobs" + whereTextWithoutPaging, paramList.ToArray())
+ .SelectScalarInt()
+ .First();
+
+ foreach (var row in connection.Query(commandText, paramList.ToArray()))
+ {
+ list.Add(GetJob(row));
+ }
+
+ return new QueryResult<SyncJob>()
+ {
+ Items = list.ToArray(),
+ TotalRecordCount = count
+ };
+ }
+ }
+ }
+
+ public SyncJobItem GetJobItem(string id)
+ {
+ if (string.IsNullOrEmpty(id))
+ {
+ throw new ArgumentNullException("id");
+ }
+
+ CheckDisposed();
+
+ var guid = new Guid(id);
+
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
+ {
+ var commandText = BaseJobItemSelectText + " where Id=?";
+ var paramList = new List<object>();
+
+ paramList.Add(guid.ToGuidParamValue());
+
+ foreach (var row in connection.Query(commandText, paramList.ToArray()))
+ {
+ return GetJobItem(row);
+ }
+
+ return null;
+ }
+ }
+ }
+
+ private QueryResult<T> GetJobItemReader<T>(SyncJobItemQuery query, string baseSelectText, Func<IReadOnlyList<IResultSetValue>, T> itemFactory)
+ {
+ if (query == null)
+ {
+ throw new ArgumentNullException("query");
+ }
+
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
+ {
+ var commandText = baseSelectText;
+ var paramList = new List<object>();
+
+ var whereClauses = new List<string>();
+
+ if (!string.IsNullOrWhiteSpace(query.JobId))
+ {
+ whereClauses.Add("JobId=?");
+ paramList.Add(query.JobId);
+ }
+ if (!string.IsNullOrWhiteSpace(query.ItemId))
+ {
+ whereClauses.Add("ItemId=?");
+ paramList.Add(query.ItemId);
+ }
+ if (!string.IsNullOrWhiteSpace(query.TargetId))
+ {
+ whereClauses.Add("TargetId=?");
+ paramList.Add(query.TargetId);
+ }
+
+ if (query.Statuses.Length > 0)
+ {
+ var statuses = string.Join(",", query.Statuses.Select(i => "'" + i.ToString() + "'").ToArray());
+
+ whereClauses.Add(string.Format("Status in ({0})", statuses));
+ }
+
+ var whereTextWithoutPaging = whereClauses.Count == 0 ?
+ string.Empty :
+ " where " + string.Join(" AND ", whereClauses.ToArray());
+
+ var startIndex = query.StartIndex ?? 0;
+ if (startIndex > 0)
+ {
+ whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM SyncJobItems ORDER BY JobItemIndex, DateCreated LIMIT {0})",
+ startIndex.ToString(_usCulture)));
+ }
+
+ if (whereClauses.Count > 0)
+ {
+ commandText += " where " + string.Join(" AND ", whereClauses.ToArray());
+ }
+
+ commandText += " ORDER BY JobItemIndex, DateCreated";
+
+ if (query.Limit.HasValue)
+ {
+ commandText += " LIMIT " + query.Limit.Value.ToString(_usCulture);
+ }
+
+ var list = new List<T>();
+ var count = connection.Query("select count (Id) from SyncJobItems" + whereTextWithoutPaging, paramList.ToArray())
+ .SelectScalarInt()
+ .First();
+
+ foreach (var row in connection.Query(commandText, paramList.ToArray()))
+ {
+ list.Add(itemFactory(row));
+ }
+
+ return new QueryResult<T>()
+ {
+ Items = list.ToArray(),
+ TotalRecordCount = count
+ };
+ }
+ }
+ }
+
+ public Dictionary<string, SyncedItemProgress> GetSyncedItemProgresses(SyncJobItemQuery query)
+ {
+ var result = new Dictionary<string, SyncedItemProgress>();
+
+ var now = DateTime.UtcNow;
+
+ using (WriteLock.Read())
+ {
+ using (var connection = CreateConnection(true))
+ {
+ var commandText = "select ItemId,Status,Progress from SyncJobItems";
+ var whereClauses = new List<string>();
+
+ if (!string.IsNullOrWhiteSpace(query.TargetId))
+ {
+ whereClauses.Add("TargetId=@TargetId");
+ }
+
+ if (query.Statuses.Length > 0)
+ {
+ var statuses = string.Join(",", query.Statuses.Select(i => "'" + i.ToString() + "'").ToArray());
+
+ whereClauses.Add(string.Format("Status in ({0})", statuses));
+ }
+
+ if (whereClauses.Count > 0)
+ {
+ commandText += " where " + string.Join(" AND ", whereClauses.ToArray());
+ }
+
+ var statementTexts = new List<string>
+ {
+ commandText
+ };
+
+ commandText = commandText
+ .Replace("select ItemId,Status,Progress from SyncJobItems", "select ItemIds,Status,Progress from SyncJobs")
+ .Replace("'Synced'", "'Completed','CompletedWithError'");
+
+ statementTexts.Add(commandText);
+
+ var statements = connection.PrepareAll(string.Join(";", statementTexts.ToArray()))
+ .ToList();
+
+ using (var statement = statements[0])
+ {
+ if (!string.IsNullOrWhiteSpace(query.TargetId))
+ {
+ statement.TryBind("@TargetId", query.TargetId);
+ }
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ AddStatusResult(row, result, false);
+ }
+ LogQueryTime("GetSyncedItemProgresses", commandText, now);
+ }
+
+ now = DateTime.UtcNow;
+
+ using (var statement = statements[1])
+ {
+ if (!string.IsNullOrWhiteSpace(query.TargetId))
+ {
+ statement.TryBind("@TargetId", query.TargetId);
+ }
+
+ foreach (var row in statement.ExecuteQuery())
+ {
+ AddStatusResult(row, result, true);
+ }
+ LogQueryTime("GetSyncedItemProgresses", commandText, now);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private void LogQueryTime(string methodName, string commandText, DateTime startDate)
+ {
+ var elapsed = (DateTime.UtcNow - startDate).TotalMilliseconds;
+
+ var slowThreshold = 1000;
+
+#if DEBUG
+ slowThreshold = 50;
+#endif
+
+ if (elapsed >= slowThreshold)
+ {
+ Logger.Debug("{2} query time (slow): {0}ms. Query: {1}",
+ Convert.ToInt32(elapsed),
+ commandText,
+ methodName);
+ }
+ else
+ {
+ //Logger.Debug("{2} query time: {0}ms. Query: {1}",
+ // Convert.ToInt32(elapsed),
+ // cmd.CommandText,
+ // methodName);
+ }
+ }
+
+ private void AddStatusResult(IReadOnlyList<IResultSetValue> reader, Dictionary<string, SyncedItemProgress> result, bool multipleIds)
+ {
+ if (reader[0].SQLiteType == SQLiteType.Null)
+ {
+ return;
+ }
+
+ var itemIds = new List<string>();
+
+ var ids = reader[0].ToString();
+
+ if (multipleIds)
+ {
+ itemIds = ids.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
+ }
+ else
+ {
+ itemIds.Add(ids);
+ }
+
+ if (reader[1].SQLiteType != SQLiteType.Null)
+ {
+ SyncJobItemStatus status;
+ var statusString = reader[1].ToString();
+ if (string.Equals(statusString, "Completed", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(statusString, "CompletedWithError", StringComparison.OrdinalIgnoreCase))
+ {
+ status = SyncJobItemStatus.Synced;
+ }
+ else
+ {
+ status = (SyncJobItemStatus)Enum.Parse(typeof(SyncJobItemStatus), statusString, true);
+ }
+
+ if (status == SyncJobItemStatus.Synced)
+ {
+ foreach (var itemId in itemIds)
+ {
+ result[itemId] = new SyncedItemProgress
+ {
+ Status = SyncJobItemStatus.Synced
+ };
+ }
+ }
+ else
+ {
+ double progress = reader[2].SQLiteType == SQLiteType.Null ? 0.0 : reader[2].ToDouble();
+
+ foreach (var itemId in itemIds)
+ {
+ SyncedItemProgress currentStatus;
+ if (!result.TryGetValue(itemId, out currentStatus) || (currentStatus.Status != SyncJobItemStatus.Synced && progress >= currentStatus.Progress))
+ {
+ result[itemId] = new SyncedItemProgress
+ {
+ Status = status,
+ Progress = progress
+ };
+ }
+ }
+ }
+ }
+ }
+
+ public QueryResult<SyncJobItem> GetJobItems(SyncJobItemQuery query)
+ {
+ return GetJobItemReader(query, BaseJobItemSelectText, GetJobItem);
+ }
+
+ public Task Create(SyncJobItem jobItem)
+ {
+ return InsertOrUpdate(jobItem, true);
+ }
+
+ public Task Update(SyncJobItem jobItem)
+ {
+ return InsertOrUpdate(jobItem, false);
+ }
+
+ private async Task InsertOrUpdate(SyncJobItem jobItem, bool insert)
+ {
+ if (jobItem == null)
+ {
+ throw new ArgumentNullException("jobItem");
+ }
+
+ CheckDisposed();
+
+ using (WriteLock.Write())
+ {
+ using (var connection = CreateConnection())
+ {
+ string commandText;
+
+ if (insert)
+ {
+ commandText = "insert into SyncJobItems (Id, ItemId, ItemName, MediaSourceId, JobId, TemporaryPath, OutputPath, Status, TargetId, DateCreated, Progress, AdditionalFiles, MediaSource, IsMarkedForRemoval, JobItemIndex, ItemDateModifiedTicks) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+ }
+ else
+ {
+ // cmd
+ commandText = "update SyncJobItems set ItemId=?,ItemName=?,MediaSourceId=?,JobId=?,TemporaryPath=?,OutputPath=?,Status=?,TargetId=?,DateCreated=?,Progress=?,AdditionalFiles=?,MediaSource=?,IsMarkedForRemoval=?,JobItemIndex=?,ItemDateModifiedTicks=? where Id=?";
+ }
+
+ var paramList = new List<object>();
+ paramList.Add(jobItem.ItemId);
+ paramList.Add(jobItem.ItemName);
+ paramList.Add(jobItem.MediaSourceId);
+ paramList.Add(jobItem.JobId);
+ paramList.Add(jobItem.TemporaryPath);
+ paramList.Add(jobItem.OutputPath);
+ paramList.Add(jobItem.Status.ToString());
+
+ paramList.Add(jobItem.TargetId);
+ paramList.Add(jobItem.DateCreated.ToDateTimeParamValue());
+ paramList.Add(jobItem.Progress);
+ paramList.Add(_json.SerializeToString(jobItem.AdditionalFiles));
+ paramList.Add(jobItem.MediaSource == null ? null : _json.SerializeToString(jobItem.MediaSource));
+ paramList.Add(jobItem.IsMarkedForRemoval);
+ paramList.Add(jobItem.JobItemIndex);
+ paramList.Add(jobItem.ItemDateModifiedTicks);
+
+ if (insert)
+ {
+ paramList.Insert(0, jobItem.Id.ToGuidParamValue());
+ }
+ else
+ {
+ paramList.Add(jobItem.Id.ToGuidParamValue());
+ }
+
+ connection.RunInTransaction(conn =>
+ {
+ conn.Execute(commandText, paramList.ToArray());
+ }, TransactionMode);
+ }
+ }
+ }
+
+ private SyncJobItem GetJobItem(IReadOnlyList<IResultSetValue> reader)
+ {
+ var info = new SyncJobItem
+ {
+ Id = reader[0].ReadGuid().ToString("N"),
+ ItemId = reader[1].ToString()
+ };
+
+ if (reader[2].SQLiteType != SQLiteType.Null)
+ {
+ info.ItemName = reader[2].ToString();
+ }
+
+ if (reader[3].SQLiteType != SQLiteType.Null)
+ {
+ info.MediaSourceId = reader[3].ToString();
+ }
+
+ info.JobId = reader[4].ToString();
+
+ if (reader[5].SQLiteType != SQLiteType.Null)
+ {
+ info.TemporaryPath = reader[5].ToString();
+ }
+ if (reader[6].SQLiteType != SQLiteType.Null)
+ {
+ info.OutputPath = reader[6].ToString();
+ }
+
+ if (reader[7].SQLiteType != SQLiteType.Null)
+ {
+ info.Status = (SyncJobItemStatus)Enum.Parse(typeof(SyncJobItemStatus), reader[7].ToString(), true);
+ }
+
+ info.TargetId = reader[8].ToString();
+
+ info.DateCreated = reader[9].ReadDateTime();
+
+ if (reader[10].SQLiteType != SQLiteType.Null)
+ {
+ info.Progress = reader[10].ToDouble();
+ }
+
+ if (reader[11].SQLiteType != SQLiteType.Null)
+ {
+ var json = reader[11].ToString();
+
+ if (!string.IsNullOrWhiteSpace(json))
+ {
+ info.AdditionalFiles = _json.DeserializeFromString<List<ItemFileInfo>>(json);
+ }
+ }
+
+ if (reader[12].SQLiteType != SQLiteType.Null)
+ {
+ var json = reader[12].ToString();
+
+ if (!string.IsNullOrWhiteSpace(json))
+ {
+ info.MediaSource = _json.DeserializeFromString<MediaSourceInfo>(json);
+ }
+ }
+
+ info.IsMarkedForRemoval = reader[13].ToBool();
+ info.JobItemIndex = reader[14].ToInt();
+
+ if (reader[15].SQLiteType != SQLiteType.Null)
+ {
+ info.ItemDateModifiedTicks = reader[15].ToInt64();
+ }
+
+ return info;
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs b/Emby.Server.Implementations/Sync/SyncedMediaSourceProvider.cs
index e0553b1b16..1e54885e67 100644
--- a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs
+++ b/Emby.Server.Implementations/Sync/SyncedMediaSourceProvider.cs
@@ -12,7 +12,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-namespace MediaBrowser.Server.Implementations.Sync
+namespace Emby.Server.Implementations.Sync
{
public class SyncedMediaSourceProvider : IMediaSourceProvider
{
@@ -144,7 +144,7 @@ namespace MediaBrowser.Server.Implementations.Sync
{
mediaSource.Id = item.Id;
mediaSource.SupportsTranscoding = false;
- if (mediaSource.Protocol == Model.MediaInfo.MediaProtocol.File)
+ if (mediaSource.Protocol == MediaBrowser.Model.MediaInfo.MediaProtocol.File)
{
mediaSource.ETag = item.Id;
}
diff --git a/MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs b/Emby.Server.Implementations/Sync/TargetDataProvider.cs
index 32a6003719..fbd82aa7af 100644
--- a/MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs
+++ b/Emby.Server.Implementations/Sync/TargetDataProvider.cs
@@ -6,15 +6,12 @@ using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Sync;
using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
-using Interfaces.IO;
-using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
-namespace MediaBrowser.Server.Implementations.Sync
+namespace Emby.Server.Implementations.Sync
{
public class TargetDataProvider : ISyncDataProvider
{
@@ -29,9 +26,9 @@ namespace MediaBrowser.Server.Implementations.Sync
private readonly IFileSystem _fileSystem;
private readonly IApplicationPaths _appPaths;
private readonly IServerApplicationHost _appHost;
- private readonly IMemoryStreamProvider _memoryStreamProvider;
+ private readonly IMemoryStreamFactory _memoryStreamProvider;
- public TargetDataProvider(IServerSyncProvider provider, SyncTarget target, IServerApplicationHost appHost, ILogger logger, IJsonSerializer json, IFileSystem fileSystem, IApplicationPaths appPaths, IMemoryStreamProvider memoryStreamProvider)
+ public TargetDataProvider(IServerSyncProvider provider, SyncTarget target, IServerApplicationHost appHost, ILogger logger, IJsonSerializer json, IFileSystem fileSystem, IApplicationPaths appPaths, IMemoryStreamFactory memoryStreamProvider)
{
_logger = logger;
_json = json;
@@ -66,15 +63,11 @@ namespace MediaBrowser.Server.Implementations.Sync
{
_logger.Debug("Getting {0} from {1}", string.Join(MediaSync.PathSeparatorString, GetRemotePath().ToArray()), _provider.Name);
- var fileResult = await _provider.GetFiles(new FileQuery
- {
- FullPath = GetRemotePath().ToArray()
-
- }, _target, cancellationToken).ConfigureAwait(false);
+ var fileResult = await _provider.GetFiles(GetRemotePath().ToArray(), _target, cancellationToken).ConfigureAwait(false);
if (fileResult.Items.Length > 0)
{
- using (var stream = await _provider.GetFile(fileResult.Items[0].Id, _target, new Progress<double>(), cancellationToken))
+ using (var stream = await _provider.GetFile(fileResult.Items[0].FullName, _target, new Progress<double>(), cancellationToken))
{
return _json.DeserializeFromStream<List<LocalItem>>(stream);
}
diff --git a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs b/Emby.Server.Implementations/TV/SeriesPostScanTask.cs
index e038a3d286..3d93561f17 100644
--- a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs
+++ b/Emby.Server.Implementations/TV/SeriesPostScanTask.cs
@@ -1,21 +1,24 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Localization;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
-using MediaBrowser.Common.ScheduledTasks;
+using MediaBrowser.Controller.Configuration;
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 MediaBrowser.Providers.TV
+namespace Emby.Server.Implementations.TV
{
class SeriesGroup : List<Series>, IGrouping<string, Series>
{
@@ -32,14 +35,16 @@ namespace MediaBrowser.Providers.TV
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)
+ 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)
@@ -59,7 +64,7 @@ namespace MediaBrowser.Providers.TV
var seriesGroups = FindSeriesGroups(seriesList).Where(g => !string.IsNullOrEmpty(g.Key)).ToList();
- return new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization, _fileSystem).Run(seriesGroups, true, cancellationToken);
+ return new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization, _fileSystem, _xmlSettings).Run(seriesGroups, true, cancellationToken);
}
internal static IEnumerable<IGrouping<string, Series>> FindSeriesGroups(List<Series> seriesList)
@@ -75,7 +80,7 @@ namespace MediaBrowser.Providers.TV
var group = new SeriesGroup();
FindAllLinked(series, visited, links, group);
- group.Key = group.Select(s => s.GetProviderId(MetadataProviders.Tvdb)).FirstOrDefault(id => !string.IsNullOrEmpty(id));
+ group.Key = group.Select(s => s.PresentationUniqueKey).FirstOrDefault(id => !string.IsNullOrEmpty(id));
yield return group;
}
@@ -100,11 +105,7 @@ namespace MediaBrowser.Providers.TV
private static bool ShareProviderId(Series a, Series b)
{
- return a.ProviderIds.Any(id =>
- {
- string value;
- return b.ProviderIds.TryGetValue(id.Key, out value) && id.Value == value;
- });
+ return string.Equals(a.PresentationUniqueKey, b.PresentationUniqueKey, StringComparison.Ordinal);
}
public int Order
@@ -127,8 +128,10 @@ namespace MediaBrowser.Providers.TV
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)
+ public CleanMissingEpisodesEntryPoint(ILibraryManager libraryManager, IServerConfigurationManager config, ILogger logger, ILocalizationManager localization, IFileSystem fileSystem, ITaskManager taskManager, IXmlReaderSettingsFactory xmlSettings, ITimerFactory timerFactory)
{
_libraryManager = libraryManager;
_config = config;
@@ -136,9 +139,11 @@ namespace MediaBrowser.Providers.TV
_localization = localization;
_fileSystem = fileSystem;
_taskManager = taskManager;
+ _xmlSettings = xmlSettings;
+ _timerFactory = timerFactory;
}
- private Timer LibraryUpdateTimer { get; set; }
+ private ITimer LibraryUpdateTimer { get; set; }
public void Run()
{
@@ -156,7 +161,7 @@ namespace MediaBrowser.Providers.TV
{
if (LibraryUpdateTimer == null)
{
- LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration, Timeout.Infinite);
+ LibraryUpdateTimer = _timerFactory.Create(LibraryUpdateTimerCallback, null, LibraryUpdateDuration, Timeout.Infinite);
}
else
{
@@ -189,7 +194,7 @@ namespace MediaBrowser.Providers.TV
var seriesGroups = SeriesPostScanTask.FindSeriesGroups(seriesList).Where(g => !string.IsNullOrEmpty(g.Key)).ToList();
- await new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization, _fileSystem)
+ await new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization, _fileSystem, _xmlSettings)
.Run(seriesGroups, false, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
diff --git a/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs
index 03e8a9178e..6bf4125253 100644
--- a/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs
+++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs
@@ -9,7 +9,7 @@ using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Controller.Configuration;
-namespace MediaBrowser.Server.Implementations.TV
+namespace Emby.Server.Implementations.TV
{
public class TVSeriesManager : ITVSeriesManager
{
@@ -58,13 +58,21 @@ namespace MediaBrowser.Server.Implementations.TV
var items = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
IncludeItemTypes = new[] { typeof(Series).Name },
- SortOrder = SortOrder.Ascending,
+ SortBy = new[] { ItemSortBy.SeriesDatePlayed },
+ SortOrder = SortOrder.Descending,
PresentationUniqueKey = presentationUniqueKey,
Limit = limit,
ParentId = parentIdGuid,
- Recursive = true
+ Recursive = true,
+ DtoOptions = new MediaBrowser.Controller.Dto.DtoOptions
+ {
+ Fields = new List<ItemFields>
+ {
+
+ }
+ }
- }).Cast<Series>();
+ }).Cast<Series>().Select(GetUniqueSeriesKey);
// Avoid implicitly captured closure
var episodes = GetNextUpEpisodes(request, user, items);
@@ -72,7 +80,7 @@ namespace MediaBrowser.Server.Implementations.TV
return GetResult(episodes, null, request);
}
- public QueryResult<BaseItem> GetNextUp(NextUpQuery request, IEnumerable<Folder> parentsFolders)
+ public QueryResult<BaseItem> GetNextUp(NextUpQuery request, List<Folder> parentsFolders)
{
var user = _userManager.GetUserById(request.UserId);
@@ -102,11 +110,20 @@ namespace MediaBrowser.Server.Implementations.TV
var items = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
IncludeItemTypes = new[] { typeof(Series).Name },
- SortOrder = SortOrder.Ascending,
+ SortBy = new[] { ItemSortBy.SeriesDatePlayed },
+ SortOrder = SortOrder.Descending,
PresentationUniqueKey = presentationUniqueKey,
- Limit = limit
+ Limit = limit,
+ DtoOptions = new MediaBrowser.Controller.Dto.DtoOptions
+ {
+ Fields = new List<ItemFields>
+ {
- }, parentsFolders.Select(i => i.Id.ToString("N"))).Cast<Series>();
+ },
+ EnableImages = false
+ }
+
+ }, parentsFolders.Cast<BaseItem>().ToList()).Cast<Series>().Select(GetUniqueSeriesKey);
// Avoid implicitly captured closure
var episodes = GetNextUpEpisodes(request, user, items);
@@ -114,92 +131,98 @@ namespace MediaBrowser.Server.Implementations.TV
return GetResult(episodes, null, request);
}
- public IEnumerable<Episode> GetNextUpEpisodes(NextUpQuery request, User user, IEnumerable<Series> series)
+ public IEnumerable<Episode> GetNextUpEpisodes(NextUpQuery request, User user, IEnumerable<string> seriesKeys)
{
// Avoid implicitly captured closure
var currentUser = user;
- var allNextUp = series
- .Select(i => GetNextUp(i, currentUser))
- .Where(i => i.Item1 != null)
- // Include if an episode was found, and either the series is not unwatched or the specific series was requested
- .OrderByDescending(i => i.Item2)
- .ThenByDescending(i => i.Item1.PremiereDate ?? DateTime.MinValue)
- .ToList();
+ var allNextUp = seriesKeys
+ .Select(i => GetNextUp(i, currentUser));
- // If viewing all next up for all series, remove first episodes
- if (string.IsNullOrWhiteSpace(request.SeriesId))
- {
- var withoutFirstEpisode = allNextUp
- .Where(i => !i.Item3)
- .ToList();
+ //allNextUp = allNextUp.OrderByDescending(i => i.Item1);
- // But if that returns empty, keep those first episodes (avoid completely empty view)
- if (withoutFirstEpisode.Count > 0)
- {
- allNextUp = withoutFirstEpisode;
- }
- }
+ // 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);
return allNextUp
- .Select(i => i.Item1)
+ .Where(i =>
+ {
+ if (alwaysEnableFirstEpisode || i.Item1 != DateTime.MinValue)
+ {
+ return true;
+ }
+
+ return false;
+ })
+ .Select(i => i.Item2())
+ .Where(i => i != null)
.Take(request.Limit ?? int.MaxValue);
}
private string GetUniqueSeriesKey(BaseItem series)
{
- if (_config.Configuration.SchemaVersion < 97)
- {
- return series.Id.ToString("N");
- }
return series.GetPresentationUniqueKey();
}
/// <summary>
/// Gets the next up.
/// </summary>
- /// <param name="series">The series.</param>
- /// <param name="user">The user.</param>
/// <returns>Task{Episode}.</returns>
- private Tuple<Episode, DateTime, bool> GetNextUp(Series series, User user)
+ private Tuple<DateTime, Func<Episode>> GetNextUp(string seriesKey, User user)
{
+ var enableSeriesPresentationKey = _config.Configuration.EnableSeriesPresentationUniqueKey;
+
var lastWatchedEpisode = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
- AncestorWithPresentationUniqueKey = GetUniqueSeriesKey(series),
+ AncestorWithPresentationUniqueKey = enableSeriesPresentationKey ? null : seriesKey,
+ SeriesPresentationUniqueKey = enableSeriesPresentationKey ? seriesKey : null,
IncludeItemTypes = new[] { typeof(Episode).Name },
SortBy = new[] { ItemSortBy.SortName },
SortOrder = SortOrder.Descending,
IsPlayed = true,
Limit = 1,
- ParentIndexNumberNotEquals = 0
+ ParentIndexNumberNotEquals = 0,
+ DtoOptions = new MediaBrowser.Controller.Dto.DtoOptions
+ {
+ Fields = new List<ItemFields>
+ {
+
+ },
+ EnableImages = false
+ }
}).FirstOrDefault();
- var firstUnwatchedEpisode = _libraryManager.GetItemList(new InternalItemsQuery(user)
+ Func<Episode> getEpisode = () =>
{
- AncestorWithPresentationUniqueKey = GetUniqueSeriesKey(series),
- IncludeItemTypes = new[] { typeof(Episode).Name },
- SortBy = new[] { ItemSortBy.SortName },
- SortOrder = SortOrder.Ascending,
- Limit = 1,
- IsPlayed = false,
- IsVirtualItem = false,
- ParentIndexNumberNotEquals = 0,
- MinSortName = lastWatchedEpisode == null ? null : lastWatchedEpisode.SortName
-
- }).Cast<Episode>().FirstOrDefault();
+ return _libraryManager.GetItemList(new InternalItemsQuery(user)
+ {
+ AncestorWithPresentationUniqueKey = enableSeriesPresentationKey ? null : seriesKey,
+ SeriesPresentationUniqueKey = enableSeriesPresentationKey ? seriesKey : null,
+ IncludeItemTypes = new[] { typeof(Episode).Name },
+ SortBy = new[] { ItemSortBy.SortName },
+ SortOrder = SortOrder.Ascending,
+ Limit = 1,
+ IsPlayed = false,
+ IsVirtualItem = false,
+ ParentIndexNumberNotEquals = 0,
+ MinSortName = lastWatchedEpisode == null ? null : lastWatchedEpisode.SortName
+
+ }).Cast<Episode>().FirstOrDefault();
+ };
- if (lastWatchedEpisode != null && firstUnwatchedEpisode != null)
+ if (lastWatchedEpisode != null)
{
var userData = _userDataManager.GetUserData(user, lastWatchedEpisode);
var lastWatchedDate = userData.LastPlayedDate ?? DateTime.MinValue.AddDays(1);
- return new Tuple<Episode, DateTime, bool>(firstUnwatchedEpisode, lastWatchedDate, false);
+ return new Tuple<DateTime, Func<Episode>>(lastWatchedDate, getEpisode);
}
// Return the first episode
- return new Tuple<Episode, DateTime, bool>(firstUnwatchedEpisode, DateTime.MinValue, true);
+ return new Tuple<DateTime, Func<Episode>>(DateTime.MinValue, getEpisode);
}
private QueryResult<BaseItem> GetResult(IEnumerable<BaseItem> items, int? totalRecordLimit, NextUpQuery query)
diff --git a/MediaBrowser.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs
index 6dd5de5480..c15e0ee41b 100644
--- a/MediaBrowser.Server.Implementations/Udp/UdpServer.cs
+++ b/Emby.Server.Implementations/Udp/UdpServer.cs
@@ -1,17 +1,16 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller;
+using MediaBrowser.Controller;
using MediaBrowser.Model.ApiClient;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Net;
-using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
+using MediaBrowser.Model.Events;
+using MediaBrowser.Model.Net;
-namespace MediaBrowser.Server.Implementations.Udp
+namespace Emby.Server.Implementations.Udp
{
/// <summary>
/// Provides a Udp Server
@@ -23,14 +22,9 @@ namespace MediaBrowser.Server.Implementations.Udp
/// </summary>
private readonly ILogger _logger;
- /// <summary>
- /// The _network manager
- /// </summary>
- private readonly INetworkManager _networkManager;
-
private bool _isDisposed;
- private readonly List<Tuple<string, bool, Func<string, string, Encoding, Task>>> _responders = new List<Tuple<string, bool, Func<string, string, Encoding, Task>>>();
+ private readonly List<Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, Task>>> _responders = new List<Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, Task>>>();
private readonly IServerApplicationHost _appHost;
private readonly IJsonSerializer _json;
@@ -38,46 +32,43 @@ namespace MediaBrowser.Server.Implementations.Udp
/// <summary>
/// Initializes a new instance of the <see cref="UdpServer" /> class.
/// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="networkManager">The network manager.</param>
- /// <param name="appHost">The application host.</param>
- /// <param name="json">The json.</param>
- public UdpServer(ILogger logger, INetworkManager networkManager, IServerApplicationHost appHost, IJsonSerializer json)
+ public UdpServer(ILogger logger, IServerApplicationHost appHost, IJsonSerializer json, ISocketFactory socketFactory)
{
_logger = logger;
- _networkManager = networkManager;
_appHost = appHost;
_json = json;
+ _socketFactory = socketFactory;
AddMessageResponder("who is EmbyServer?", true, RespondToV2Message);
AddMessageResponder("who is MediaBrowserServer_v2?", false, RespondToV2Message);
}
- private void AddMessageResponder(string message, bool isSubstring, Func<string, string, Encoding, Task> responder)
+ private void AddMessageResponder(string message, bool isSubstring, Func<string, IpEndPointInfo, Encoding, Task> responder)
{
- _responders.Add(new Tuple<string, bool, Func<string, string, Encoding, Task>>(message, isSubstring, responder));
+ _responders.Add(new Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, Task>>(message, isSubstring, responder));
}
/// <summary>
/// Raises the <see cref="E:MessageReceived" /> event.
/// </summary>
- /// <param name="e">The <see cref="UdpMessageReceivedEventArgs"/> instance containing the event data.</param>
- private async void OnMessageReceived(UdpMessageReceivedEventArgs e)
+ private async void OnMessageReceived(GenericEventArgs<SocketReceiveResult> e)
{
+ var message = e.Argument;
+
var encoding = Encoding.UTF8;
- var responder = GetResponder(e.Bytes, encoding);
+ var responder = GetResponder(message.Buffer, message.ReceivedBytes, encoding);
if (responder == null)
{
encoding = Encoding.Unicode;
- responder = GetResponder(e.Bytes, encoding);
+ responder = GetResponder(message.Buffer, message.ReceivedBytes, encoding);
}
if (responder != null)
{
try
{
- await responder.Item2.Item3(responder.Item1, e.RemoteEndPoint, encoding).ConfigureAwait(false);
+ await responder.Item2.Item3(responder.Item1, message.RemoteEndPoint, encoding).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -86,9 +77,9 @@ namespace MediaBrowser.Server.Implementations.Udp
}
}
- private Tuple<string, Tuple<string, bool, Func<string, string, Encoding, Task>>> GetResponder(byte[] bytes, Encoding encoding)
+ private Tuple<string, Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, Task>>> GetResponder(byte[] buffer, int bytesReceived, Encoding encoding)
{
- var text = encoding.GetString(bytes);
+ var text = encoding.GetString(buffer, 0, bytesReceived);
var responder = _responders.FirstOrDefault(i =>
{
if (i.Item2)
@@ -102,10 +93,10 @@ namespace MediaBrowser.Server.Implementations.Udp
{
return null;
}
- return new Tuple<string, Tuple<string, bool, Func<string, string, Encoding, Task>>>(text, responder);
+ return new Tuple<string, Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, Task>>>(text, responder);
}
- private async Task RespondToV2Message(string messageText, string endpoint, Encoding encoding)
+ private async Task RespondToV2Message(string messageText, IpEndPointInfo endpoint, Encoding encoding)
{
var parts = messageText.Split('|');
@@ -121,7 +112,7 @@ namespace MediaBrowser.Server.Implementations.Udp
};
await SendAsync(encoding.GetBytes(_json.SerializeToString(response)), endpoint).ConfigureAwait(false);
-
+
if (parts.Length > 1)
{
_appHost.EnableLoopback(parts[1]);
@@ -136,7 +127,8 @@ namespace MediaBrowser.Server.Implementations.Udp
/// <summary>
/// The _udp client
/// </summary>
- private UdpClient _udpClient;
+ private IUdpSocket _udpClient;
+ private readonly ISocketFactory _socketFactory;
/// <summary>
/// Starts the specified port.
@@ -144,9 +136,7 @@ namespace MediaBrowser.Server.Implementations.Udp
/// <param name="port">The port.</param>
public void Start(int port)
{
- _udpClient = new UdpClient(new IPEndPoint(IPAddress.Any, port));
-
- _udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
+ _udpClient = _socketFactory.CreateUdpSocket(port);
Task.Run(() => StartListening());
}
@@ -157,56 +147,36 @@ namespace MediaBrowser.Server.Implementations.Udp
{
try
{
- var result = await GetResult().ConfigureAwait(false);
+ var result = await _udpClient.ReceiveAsync().ConfigureAwait(false);
OnMessageReceived(result);
}
catch (ObjectDisposedException)
{
- break;
}
catch (Exception ex)
{
- _logger.ErrorException("Error in StartListening", ex);
+ _logger.ErrorException("Error receiving udp message", ex);
}
}
}
- private Task<UdpReceiveResult> GetResult()
- {
- try
- {
- return _udpClient.ReceiveAsync();
- }
- catch (ObjectDisposedException)
- {
- return Task.FromResult(new UdpReceiveResult(new byte[] { }, new IPEndPoint(IPAddress.Any, 0)));
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error receiving udp message", ex);
- return Task.FromResult(new UdpReceiveResult(new byte[] { }, new IPEndPoint(IPAddress.Any, 0)));
- }
- }
-
/// <summary>
/// Called when [message received].
/// </summary>
/// <param name="message">The message.</param>
- private void OnMessageReceived(UdpReceiveResult message)
+ private void OnMessageReceived(SocketReceiveResult message)
{
if (message.RemoteEndPoint.Port == 0)
{
return;
}
- var bytes = message.Buffer;
try
{
- OnMessageReceived(new UdpMessageReceivedEventArgs
+ OnMessageReceived(new GenericEventArgs<SocketReceiveResult>
{
- Bytes = bytes,
- RemoteEndPoint = message.RemoteEndPoint.ToString()
+ Argument = message
});
}
catch (Exception ex)
@@ -233,7 +203,7 @@ namespace MediaBrowser.Server.Implementations.Udp
if (_udpClient != null)
{
- _udpClient.Close();
+ _udpClient.Dispose();
}
}
@@ -249,68 +219,21 @@ namespace MediaBrowser.Server.Implementations.Udp
}
}
- /// <summary>
- /// Sends the async.
- /// </summary>
- /// <param name="data">The data.</param>
- /// <param name="ipAddress">The ip address.</param>
- /// <param name="port">The port.</param>
- /// <returns>Task{System.Int32}.</returns>
- /// <exception cref="System.ArgumentNullException">data</exception>
- public Task SendAsync(string data, string ipAddress, int port)
- {
- return SendAsync(Encoding.UTF8.GetBytes(data), ipAddress, port);
- }
-
- /// <summary>
- /// Sends the async.
- /// </summary>
- /// <param name="bytes">The bytes.</param>
- /// <param name="ipAddress">The ip address.</param>
- /// <param name="port">The port.</param>
- /// <returns>Task{System.Int32}.</returns>
- /// <exception cref="System.ArgumentNullException">bytes</exception>
- public Task SendAsync(byte[] bytes, string ipAddress, int port)
- {
- if (bytes == null)
- {
- throw new ArgumentNullException("bytes");
- }
-
- if (string.IsNullOrEmpty(ipAddress))
- {
- throw new ArgumentNullException("ipAddress");
- }
-
- return _udpClient.SendAsync(bytes, bytes.Length, ipAddress, port);
- }
-
- /// <summary>
- /// Sends the async.
- /// </summary>
- /// <param name="bytes">The bytes.</param>
- /// <param name="remoteEndPoint">The remote end point.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">
- /// bytes
- /// or
- /// remoteEndPoint
- /// </exception>
- public async Task SendAsync(byte[] bytes, string remoteEndPoint)
+ public async Task SendAsync(byte[] bytes, IpEndPointInfo remoteEndPoint)
{
if (bytes == null)
{
throw new ArgumentNullException("bytes");
}
- if (string.IsNullOrEmpty(remoteEndPoint))
+ if (remoteEndPoint == null)
{
throw new ArgumentNullException("remoteEndPoint");
}
try
{
- await _udpClient.SendAsync(bytes, bytes.Length, _networkManager.Parse(remoteEndPoint)).ConfigureAwait(false);
+ await _udpClient.SendAsync(bytes, bytes.Length, remoteEndPoint).ConfigureAwait(false);
_logger.Info("Udp message sent to {0}", remoteEndPoint);
}
diff --git a/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs
index 9674199fe4..0420900c55 100644
--- a/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs
+++ b/Emby.Server.Implementations/Updates/InstallationManager.cs
@@ -1,26 +1,26 @@
-using MediaBrowser.Common.Configuration;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Common;
+using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events;
-using MediaBrowser.Common.Implementations.Security;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Progress;
using MediaBrowser.Common.Security;
using MediaBrowser.Common.Updates;
+using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.Events;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Updates;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Security.Cryptography;
-using System.Threading;
-using System.Threading.Tasks;
-using CommonIO;
-namespace MediaBrowser.Common.Implementations.Updates
+namespace Emby.Server.Implementations.Updates
{
/// <summary>
/// Manages all install, uninstall and update operations (both plugins and system)
@@ -40,7 +40,12 @@ namespace MediaBrowser.Common.Implementations.Updates
/// <summary>
/// The completed installations
/// </summary>
- public ConcurrentBag<InstallationInfo> CompletedInstallations { get; set; }
+ private ConcurrentBag<InstallationInfo> CompletedInstallationsInternal { get; set; }
+
+ public IEnumerable<InstallationInfo> CompletedInstallations
+ {
+ get { return CompletedInstallationsInternal; }
+ }
#region PluginUninstalled Event
/// <summary>
@@ -115,7 +120,9 @@ namespace MediaBrowser.Common.Implementations.Updates
/// <value>The application host.</value>
private readonly IApplicationHost _applicationHost;
- public InstallationManager(ILogger logger, IApplicationHost appHost, IApplicationPaths appPaths, IHttpClient httpClient, IJsonSerializer jsonSerializer, ISecurityManager securityManager, IConfigurationManager config, IFileSystem fileSystem)
+ private readonly ICryptoProvider _cryptographyProvider;
+
+ public InstallationManager(ILogger logger, IApplicationHost appHost, IApplicationPaths appPaths, IHttpClient httpClient, IJsonSerializer jsonSerializer, ISecurityManager securityManager, IConfigurationManager config, IFileSystem fileSystem, ICryptoProvider cryptographyProvider)
{
if (logger == null)
{
@@ -123,7 +130,7 @@ namespace MediaBrowser.Common.Implementations.Updates
}
CurrentInstallations = new List<Tuple<InstallationInfo, CancellationTokenSource>>();
- CompletedInstallations = new ConcurrentBag<InstallationInfo>();
+ CompletedInstallationsInternal = new ConcurrentBag<InstallationInfo>();
_applicationHost = appHost;
_appPaths = appPaths;
@@ -132,6 +139,7 @@ namespace MediaBrowser.Common.Implementations.Updates
_securityManager = securityManager;
_config = config;
_fileSystem = fileSystem;
+ _cryptographyProvider = cryptographyProvider;
_logger = logger;
}
@@ -156,14 +164,14 @@ namespace MediaBrowser.Common.Implementations.Updates
{
var data = new Dictionary<string, string>
{
- { "key", _securityManager.SupporterKey },
- { "mac", _applicationHost.SystemId },
+ { "key", _securityManager.SupporterKey },
+ { "mac", _applicationHost.SystemId },
{ "systemid", _applicationHost.SystemId }
};
if (withRegistration)
{
- using (var json = await _httpClient.Post(MbAdmin.HttpsUrl + "service/package/retrieveall", data, cancellationToken).ConfigureAwait(false))
+ using (var json = await _httpClient.Post("https://www.mb3admin.com/admin/service/package/retrieveall", data, cancellationToken).ConfigureAwait(false))
{
cancellationToken.ThrowIfCancellationRequested();
@@ -236,7 +244,7 @@ namespace MediaBrowser.Common.Implementations.Updates
var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
{
- Url = MbAdmin.HttpUrl + "service/MB3Packages.json",
+ Url = "https://www.mb3admin.com/admin/service/MB3Packages.json",
CancellationToken = cancellationToken,
Progress = new Progress<Double>()
@@ -500,7 +508,7 @@ namespace MediaBrowser.Common.Implementations.Updates
progress.Report(100);
- CompletedInstallations.Add(installationInfo);
+ CompletedInstallationsInternal.Add(installationInfo);
EventHelper.FireEventIfNotNull(PackageInstallationCompleted, this, installationEventArgs, _logger);
}
@@ -597,13 +605,12 @@ namespace MediaBrowser.Common.Implementations.Updates
var packageChecksum = string.IsNullOrWhiteSpace(package.checksum) ? Guid.Empty : new Guid(package.checksum);
if (packageChecksum != Guid.Empty) // support for legacy uploads for now
{
- using (var crypto = new MD5CryptoServiceProvider())
- using (var stream = new BufferedStream(_fileSystem.OpenRead(tempFile), 100000))
+ using (var stream = _fileSystem.OpenRead(tempFile))
{
- var check = Guid.Parse(BitConverter.ToString(crypto.ComputeHash(stream)).Replace("-", String.Empty));
+ var check = Guid.Parse(BitConverter.ToString(_cryptographyProvider.ComputeMD5(stream)).Replace("-", String.Empty));
if (check != packageChecksum)
{
- throw new ApplicationException(string.Format("Download validation failed for {0}. Probably corrupted during transfer.", package.name));
+ throw new Exception(string.Format("Download validation failed for {0}. Probably corrupted during transfer.", package.name));
}
}
}
@@ -618,7 +625,7 @@ namespace MediaBrowser.Common.Implementations.Updates
//If it is an archive - write out a version file so we know what it is
if (isArchive)
{
- File.WriteAllText(target + ".ver", package.versionStr);
+ _fileSystem.WriteAllText(target + ".ver", package.versionStr);
}
}
catch (IOException e)
@@ -650,6 +657,8 @@ namespace MediaBrowser.Common.Implementations.Updates
// Remove it the quick way for now
_applicationHost.RemovePlugin(plugin);
+ _logger.Info("Deleting plugin file {0}", plugin.AssemblyFilePath);
+
_fileSystem.DeleteFile(plugin.AssemblyFilePath);
OnPluginUninstalled(plugin);
diff --git a/MediaBrowser.Server.Implementations/UserViews/CollectionFolderImageProvider.cs b/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs
index 2cff4a14f0..ab63072386 100644
--- a/MediaBrowser.Server.Implementations/UserViews/CollectionFolderImageProvider.cs
+++ b/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs
@@ -5,20 +5,22 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Server.Implementations.Photos;
-using MoreLinq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
-using CommonIO;
+using Emby.Server.Implementations.Images;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Collections;
using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Querying;
-namespace MediaBrowser.Server.Implementations.UserViews
+namespace Emby.Server.Implementations.UserViews
{
public class CollectionFolderImageProvider : BaseDynamicImageProvider<CollectionFolder>
{
diff --git a/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs b/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs
index f400728971..bef964c6fa 100644
--- a/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs
+++ b/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs
@@ -6,17 +6,17 @@ using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Server.Implementations.Photos;
-using MoreLinq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
-using CommonIO;
+using Emby.Server.Implementations.Images;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Model.Extensions;
-namespace MediaBrowser.Server.Implementations.UserViews
+namespace Emby.Server.Implementations.UserViews
{
public class DynamicImageProvider : BaseDynamicImageProvider<UserView>
{
@@ -151,17 +151,7 @@ namespace MediaBrowser.Server.Implementations.UserViews
string[] collectionStripViewTypes =
{
CollectionType.Movies,
- CollectionType.TvShows,
- CollectionType.Music,
- CollectionType.Games,
- CollectionType.Books,
- CollectionType.MusicVideos,
- CollectionType.HomeVideos,
- CollectionType.BoxSets,
- CollectionType.LiveTv,
- CollectionType.Playlists,
- CollectionType.Photos,
- string.Empty
+ CollectionType.TvShows
};
return collectionStripViewTypes.Contains(view.ViewType ?? string.Empty);
@@ -169,20 +159,14 @@ namespace MediaBrowser.Server.Implementations.UserViews
protected override async Task<string> CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
{
- var outputPath = Path.ChangeExtension(outputPathWithoutExtension, ".png");
-
- var view = (UserView)item;
- if (imageType == ImageType.Primary && IsUsingCollectionStrip(view))
+ if (itemsWithImages.Count == 0)
{
- if (itemsWithImages.Count == 0)
- {
- return null;
- }
-
- return await CreateThumbCollage(item, itemsWithImages, outputPath, 960, 540).ConfigureAwait(false);
+ return null;
}
- return await base.CreateImage(item, itemsWithImages, outputPath, imageType, imageIndex).ConfigureAwait(false);
+ var outputPath = Path.ChangeExtension(outputPathWithoutExtension, ".png");
+
+ return await CreateThumbCollage(item, itemsWithImages, outputPath, 960, 540).ConfigureAwait(false);
}
}
}
diff --git a/Emby.Server.Implementations/packages.config b/Emby.Server.Implementations/packages.config
new file mode 100644
index 0000000000..09b0af8981
--- /dev/null
+++ b/Emby.Server.Implementations/packages.config
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="Emby.XmlTv" version="1.0.3" targetFramework="portable45-net45+win8" />
+ <package id="MediaBrowser.Naming" version="1.0.3" targetFramework="portable45-net45+win8" />
+ <package id="SQLitePCL.pretty" version="1.1.0" targetFramework="portable45-net45+win8" />
+ <package id="SQLitePCLRaw.core" version="1.1.1" targetFramework="portable45-net45+win8" />
+ <package id="UniversalDetector" version="1.0.1" targetFramework="portable45-net45+win8" />
+</packages> \ No newline at end of file
diff --git a/Emby.Server.sln b/Emby.Server.sln
new file mode 100644
index 0000000000..c6e057c424
--- /dev/null
+++ b/Emby.Server.sln
@@ -0,0 +1,291 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D93082B7-8BA0-4F64-8772-F318C78773D7}"
+ ProjectSection(SolutionItems) = preProject
+ global.json = global.json
+ EndProjectSection
+EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Emby.Server", "src\Emby.Server\Emby.Server.xproj", "{DDAFF431-0B3D-4857-8762-990A32DC8472}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Model", "MediaBrowser.Model\MediaBrowser.Model.csproj", "{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Common", "MediaBrowser.Common\MediaBrowser.Common.csproj", "{9142EEFA-7570-41E1-BFCC-468BB571AF2F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Controller", "MediaBrowser.Controller\MediaBrowser.Controller.csproj", "{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.XbmcMetadata", "MediaBrowser.XbmcMetadata\MediaBrowser.XbmcMetadata.csproj", "{23499896-B135-4527-8574-C26E926EA99E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.LocalMetadata", "MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj", "{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.WebDashboard", "MediaBrowser.WebDashboard\MediaBrowser.WebDashboard.csproj", "{5624B7B5-B5A7-41D8-9F10-CC5611109619}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DvdLib", "DvdLib\DvdLib.csproj", "{713F42B5-878E-499D-A878-E4C652B1D5E8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BDInfo", "BDInfo\BDInfo.csproj", "{88AE38DF-19D7-406F-A6A9-09527719A21E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Providers", "MediaBrowser.Providers\MediaBrowser.Providers.csproj", "{442B5058-DCAF-4263-BB6A-F21E31120A1B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenSubtitlesHandler", "OpenSubtitlesHandler\OpenSubtitlesHandler.csproj", "{4A4402D4-E910-443B-B8FC-2C18286A2CA0}"
+EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Emby.Common.Implementations", "Emby.Common.Implementations\Emby.Common.Implementations.xproj", "{5A27010A-09C6-4E86-93EA-437484C10917}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Photos", "Emby.Photos\Emby.Photos.csproj", "{89AB4548-770D-41FD-A891-8DAFF44F452C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Api", "MediaBrowser.Api\MediaBrowser.Api.csproj", "{4FD51AC5-2C16-4308-A993-C3A84F3B4582}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.MediaEncoding", "MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj", "{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RSSDP", "RSSDP\RSSDP.csproj", "{21002819-C39A-4D3E-BE83-2A276A77FB1F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Dlna", "Emby.Dlna\Emby.Dlna.csproj", "{805844AB-E92F-45E6-9D99-4F6D48D129A5}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Server.Implementations", "MediaBrowser.Server.Implementations\MediaBrowser.Server.Implementations.csproj", "{2E781478-814D-4A48-9D80-BFF206441A65}"
+EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Mono.Nat", "Mono.Nat\Mono.Nat.xproj", "{4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Server.Implementations", "Emby.Server.Implementations\Emby.Server.Implementations.csproj", "{E383961B-9356-4D5D-8233-9A1079D03055}"
+EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Emby.Server.Core", "Emby.Server.Core\Emby.Server.Core.xproj", "{65AA7D67-8059-40CD-91F1-16D02687226C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing", "Emby.Drawing\Emby.Drawing.csproj", "{08FFF49B-F175-4807-A2B5-73B0EBD9F716}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceStack", "ServiceStack\ServiceStack.csproj", "{680A1709-25EB-4D52-A87F-EE03FFD94BAA}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SocketHttpListener.Portable", "SocketHttpListener.Portable\SocketHttpListener.Portable.csproj", "{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release Mono|Any CPU = Release Mono|Any CPU
+ Release|Any CPU = Release|Any CPU
+ Signed|Any CPU = Signed|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {DDAFF431-0B3D-4857-8762-990A32DC8472}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DDAFF431-0B3D-4857-8762-990A32DC8472}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DDAFF431-0B3D-4857-8762-990A32DC8472}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
+ {DDAFF431-0B3D-4857-8762-990A32DC8472}.Release Mono|Any CPU.Build.0 = Release|Any CPU
+ {DDAFF431-0B3D-4857-8762-990A32DC8472}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DDAFF431-0B3D-4857-8762-990A32DC8472}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DDAFF431-0B3D-4857-8762-990A32DC8472}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {DDAFF431-0B3D-4857-8762-990A32DC8472}.Signed|Any CPU.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}.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|Any CPU.ActiveCfg = Release|Any CPU
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release|Any CPU.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
+ {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}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release Mono|Any CPU.Build.0 = Release Mono|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}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|Any CPU.Build.0 = Release|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release Mono|Any CPU.Build.0 = Release Mono|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}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Signed|Any CPU.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}.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|Any CPU.ActiveCfg = Release|Any CPU
+ {23499896-B135-4527-8574-C26E926EA99E}.Release|Any CPU.Build.0 = 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
+ {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}.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|Any CPU.ActiveCfg = Release|Any CPU
+ {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release|Any CPU.Build.0 = 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
+ {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}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release Mono|Any CPU.Build.0 = Release Mono|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}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|Any CPU.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}.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|Any CPU.ActiveCfg = Release|Any CPU
+ {713F42B5-878E-499D-A878-E4C652B1D5E8}.Release|Any CPU.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
+ {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}.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|Any CPU.ActiveCfg = Release|Any CPU
+ {88AE38DF-19D7-406F-A6A9-09527719A21E}.Release|Any CPU.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
+ {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}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release Mono|Any CPU.Build.0 = Release Mono|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}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|Any CPU.Build.0 = Release|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release Mono|Any CPU.Build.0 = Release Mono|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}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|Any CPU.Build.0 = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|Any CPU.Build.0 = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Signed|Any CPU.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}.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|Any CPU.ActiveCfg = Release|Any CPU
+ {89AB4548-770D-41FD-A891-8DAFF44F452C}.Release|Any CPU.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
+ {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}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release Mono|Any CPU.Build.0 = Release Mono|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}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|Any CPU.Build.0 = Release|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|Any CPU.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}.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|Any CPU.ActiveCfg = Release|Any CPU
+ {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|Any CPU.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
+ {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}.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|Any CPU.ActiveCfg = Release|Any CPU
+ {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|Any CPU.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
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Signed|Any CPU.Build.0 = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release Mono|Any CPU.Build.0 = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Signed|Any CPU.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}.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|Any CPU.ActiveCfg = Release|Any CPU
+ {E383961B-9356-4D5D-8233-9A1079D03055}.Release|Any CPU.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
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release Mono|Any CPU.Build.0 = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Signed|Any CPU.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}.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|Any CPU.ActiveCfg = Release|Any CPU
+ {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release|Any CPU.Build.0 = 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
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Any CPU.Build.0 = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Any CPU.ActiveCfg = Signed|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Any CPU.Build.0 = Signed|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release Mono|Any CPU.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {DDAFF431-0B3D-4857-8762-990A32DC8472} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ {23499896-B135-4527-8574-C26E926EA99E} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ {7EF9F3E0-697D-42F3-A08F-19DEB5F84392} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ {713F42B5-878E-499D-A878-E4C652B1D5E8} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ {88AE38DF-19D7-406F-A6A9-09527719A21E} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ {5A27010A-09C6-4E86-93EA-437484C10917} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ {89AB4548-770D-41FD-A891-8DAFF44F452C} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ {21002819-C39A-4D3E-BE83-2A276A77FB1F} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ {805844AB-E92F-45E6-9D99-4F6D48D129A5} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ {2E781478-814D-4A48-9D80-BFF206441A65} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ {E383961B-9356-4D5D-8233-9A1079D03055} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ {65AA7D67-8059-40CD-91F1-16D02687226C} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ {08FFF49B-F175-4807-A2B5-73B0EBD9F716} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E} = {8ADD772F-F0A4-4A53-9B2F-AF4A4C585839}
+ EndGlobalSection
+EndGlobal
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index c6e45c61ab..37ab123667 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -15,8 +15,12 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.Diagnostics;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Threading;
namespace MediaBrowser.Api
{
@@ -34,7 +38,8 @@ namespace MediaBrowser.Api
/// Gets or sets the logger.
/// </summary>
/// <value>The logger.</value>
- private ILogger Logger { get; set; }
+ internal ILogger Logger { get; private set; }
+ internal IHttpResultFactory ResultFactory { get; private set; }
/// <summary>
/// The application paths
@@ -44,6 +49,8 @@ namespace MediaBrowser.Api
private readonly ISessionManager _sessionManager;
private readonly IFileSystem _fileSystem;
private readonly IMediaSourceManager _mediaSourceManager;
+ public readonly ITimerFactory TimerFactory;
+ public readonly IProcessFactory ProcessFactory;
/// <summary>
/// The active transcoding jobs
@@ -61,13 +68,16 @@ namespace MediaBrowser.Api
/// <param name="config">The configuration.</param>
/// <param name="fileSystem">The file system.</param>
/// <param name="mediaSourceManager">The media source manager.</param>
- public ApiEntryPoint(ILogger logger, ISessionManager sessionManager, IServerConfigurationManager config, IFileSystem fileSystem, IMediaSourceManager mediaSourceManager)
+ public ApiEntryPoint(ILogger logger, ISessionManager sessionManager, IServerConfigurationManager config, IFileSystem fileSystem, IMediaSourceManager mediaSourceManager, ITimerFactory timerFactory, IProcessFactory processFactory, IHttpResultFactory resultFactory)
{
Logger = logger;
_sessionManager = sessionManager;
_config = config;
_fileSystem = fileSystem;
_mediaSourceManager = mediaSourceManager;
+ TimerFactory = timerFactory;
+ ProcessFactory = processFactory;
+ ResultFactory = resultFactory;
Instance = this;
_sessionManager.PlaybackProgress += _sessionManager_PlaybackProgress;
@@ -114,11 +124,15 @@ namespace MediaBrowser.Api
{
DeleteEncodedMediaCache();
}
- catch (DirectoryNotFoundException)
+ catch (FileNotFoundException)
+ {
+ // Don't clutter the log
+ }
+ catch (IOException)
{
// Don't clutter the log
}
- catch (IOException ex)
+ catch (Exception ex)
{
Logger.ErrorException("Error deleting encoded media cache", ex);
}
@@ -166,7 +180,8 @@ namespace MediaBrowser.Api
// Try to allow for some time to kill the ffmpeg processes and delete the partial stream files
if (jobCount > 0)
{
- Thread.Sleep(1000);
+ var task = Task.Delay(1000);
+ Task.WaitAll(task);
}
}
@@ -188,14 +203,14 @@ namespace MediaBrowser.Api
string liveStreamId,
string transcodingJobId,
TranscodingJobType type,
- Process process,
+ IProcess process,
string deviceId,
StreamState state,
CancellationTokenSource cancellationTokenSource)
{
lock (_activeTranscodingJobs)
{
- var job = new TranscodingJob(Logger)
+ var job = new TranscodingJob(Logger, TimerFactory)
{
Type = type,
Path = path,
@@ -598,10 +613,6 @@ namespace MediaBrowser.Api
DeleteHlsPartialStreamFiles(path);
}
}
- catch (DirectoryNotFoundException)
- {
-
- }
catch (FileNotFoundException)
{
@@ -649,10 +660,6 @@ namespace MediaBrowser.Api
//Logger.Debug("Deleting HLS file {0}", file);
_fileSystem.DeleteFile(file);
}
- catch (DirectoryNotFoundException)
- {
-
- }
catch (FileNotFoundException)
{
@@ -704,7 +711,7 @@ namespace MediaBrowser.Api
/// Gets or sets the process.
/// </summary>
/// <value>The process.</value>
- public Process Process { get; set; }
+ public IProcess Process { get; set; }
public ILogger Logger { get; private set; }
/// <summary>
/// Gets or sets the active request count.
@@ -715,7 +722,9 @@ namespace MediaBrowser.Api
/// Gets or sets the kill timer.
/// </summary>
/// <value>The kill timer.</value>
- private Timer KillTimer { get; set; }
+ private ITimer KillTimer { get; set; }
+
+ private readonly ITimerFactory _timerFactory;
public string DeviceId { get; set; }
@@ -745,9 +754,10 @@ namespace MediaBrowser.Api
public DateTime LastPingDate { get; set; }
public int PingTimeout { get; set; }
- public TranscodingJob(ILogger logger)
+ public TranscodingJob(ILogger logger, ITimerFactory timerFactory)
{
Logger = logger;
+ _timerFactory = timerFactory;
}
public void StopKillTimer()
@@ -773,12 +783,12 @@ namespace MediaBrowser.Api
}
}
- public void StartKillTimer(TimerCallback callback)
+ public void StartKillTimer(Action<object> callback)
{
StartKillTimer(callback, PingTimeout);
}
- public void StartKillTimer(TimerCallback callback, int intervalMs)
+ public void StartKillTimer(Action<object> callback, int intervalMs)
{
if (HasExited)
{
@@ -790,7 +800,7 @@ namespace MediaBrowser.Api
if (KillTimer == null)
{
//Logger.Debug("Starting kill timer at {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId);
- KillTimer = new Timer(callback, this, intervalMs, Timeout.Infinite);
+ KillTimer = _timerFactory.Create(callback, this, intervalMs, Timeout.Infinite);
}
else
{
diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs
index 3ff432d741..1f21a1dd15 100644
--- a/MediaBrowser.Api/BaseApiService.cs
+++ b/MediaBrowser.Api/BaseApiService.cs
@@ -6,30 +6,42 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
-using ServiceStack.Text.Controller;
-using ServiceStack.Web;
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
/// <summary>
/// Class BaseApiService
/// </summary>
- public class BaseApiService : IHasResultFactory, IRestfulService, IHasSession
+ public class BaseApiService : IService, IRequiresRequest
{
/// <summary>
/// Gets or sets the logger.
/// </summary>
/// <value>The logger.</value>
- public ILogger Logger { get; set; }
+ public ILogger Logger
+ {
+ get
+ {
+ return ApiEntryPoint.Instance.Logger;
+ }
+ }
/// <summary>
/// Gets or sets the HTTP result factory.
/// </summary>
/// <value>The HTTP result factory.</value>
- public IHttpResultFactory ResultFactory { get; set; }
+ public IHttpResultFactory ResultFactory
+ {
+ get
+ {
+ return ApiEntryPoint.Instance.ResultFactory;
+ }
+ }
/// <summary>
/// Gets or sets the request context.
@@ -37,9 +49,6 @@ namespace MediaBrowser.Api
/// <value>The request context.</value>
public IRequest Request { get; set; }
- public ISessionContext SessionContext { get; set; }
- public IAuthorizationContext AuthorizationContext { get; set; }
-
public string GetHeader(string name)
{
return Request.Headers[name];
@@ -57,9 +66,9 @@ namespace MediaBrowser.Api
return ResultFactory.GetOptimizedResult(Request, result);
}
- protected void AssertCanUpdateUser(IUserManager userManager, string userId)
+ protected void AssertCanUpdateUser(IAuthorizationContext authContext, IUserManager userManager, string userId)
{
- var auth = AuthorizationContext.GetAuthorizationInfo(Request);
+ var auth = authContext.GetAuthorizationInfo(Request);
var authenticatedUser = userManager.GetUserById(auth.UserId);
@@ -96,9 +105,9 @@ namespace MediaBrowser.Api
/// Gets the session.
/// </summary>
/// <returns>SessionInfo.</returns>
- protected async Task<SessionInfo> GetSession()
+ protected async Task<SessionInfo> GetSession(ISessionContext sessionContext)
{
- var session = await SessionContext.GetSession(Request).ConfigureAwait(false);
+ var session = await sessionContext.GetSession(Request).ConfigureAwait(false);
if (session == null)
{
@@ -108,21 +117,13 @@ namespace MediaBrowser.Api
return session;
}
- /// <summary>
- /// To the static file result.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>System.Object.</returns>
- protected object ToStaticFileResult(string path)
- {
- return ResultFactory.GetStaticFileResult(Request, path).Result;
- }
-
- protected DtoOptions GetDtoOptions(object request)
+ protected DtoOptions GetDtoOptions(IAuthorizationContext authContext, object request)
{
var options = new DtoOptions();
- options.DeviceId = AuthorizationContext.GetAuthorizationInfo(Request).DeviceId;
+ var authInfo = authContext.GetAuthorizationInfo(Request);
+
+ options.DeviceId = authInfo.DeviceId;
var hasFields = request as IHasItemFields;
if (hasFields != null)
@@ -130,6 +131,36 @@ namespace MediaBrowser.Api
options.Fields = hasFields.GetItemFields().ToList();
}
+ 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)
+ {
+ options.Fields.Add(Model.Querying.ItemFields.RecursiveItemCount);
+ }
+
+ 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)
+ {
+ options.Fields.Add(Model.Querying.ItemFields.ChildCount);
+ }
+
+ if (client.IndexOf("web", StringComparison.OrdinalIgnoreCase) == -1 &&
+
+ // covers both emby mobile and emby for android mobile
+ client.IndexOf("mobile", StringComparison.OrdinalIgnoreCase) == -1 &&
+ client.IndexOf("ios", StringComparison.OrdinalIgnoreCase) == -1 &&
+ client.IndexOf("theater", StringComparison.OrdinalIgnoreCase) == -1)
+ {
+ options.Fields.Add(Model.Querying.ItemFields.ChildCount);
+ }
+
var hasDtoOptions = request as IHasDtoOptions;
if (hasDtoOptions != null)
{
@@ -275,8 +306,8 @@ namespace MediaBrowser.Api
protected string GetPathValue(int index)
{
- var pathInfo = PathInfo.Parse(Request.PathInfo);
- var first = pathInfo.GetArgumentValue<string>(0);
+ var pathInfo = Parse(Request.PathInfo);
+ var first = pathInfo[0];
// backwards compatibility
if (string.Equals(first, "mediabrowser", StringComparison.OrdinalIgnoreCase) ||
@@ -285,7 +316,24 @@ namespace MediaBrowser.Api
index++;
}
- return pathInfo.GetArgumentValue<string>(index);
+ return pathInfo[index];
+ }
+
+ private static 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();
}
/// <summary>
diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Api/BasePeriodicWebSocketListener.cs
index be177cb02c..fe7de387f8 100644
--- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
+++ b/MediaBrowser.Api/BasePeriodicWebSocketListener.cs
@@ -1,13 +1,15 @@
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
-using System;
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Threading;
-namespace MediaBrowser.Controller.Net
+namespace MediaBrowser.Api
{
/// <summary>
/// Starts sending data over a web socket periodically when a message is received, and then stops when a corresponding stop message is received
@@ -21,8 +23,8 @@ namespace MediaBrowser.Controller.Net
/// <summary>
/// The _active connections
/// </summary>
- protected readonly List<Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>> ActiveConnections =
- new List<Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>>();
+ protected readonly List<Tuple<IWebSocketConnection, CancellationTokenSource, ITimer, TStateType, SemaphoreSlim>> ActiveConnections =
+ new List<Tuple<IWebSocketConnection, CancellationTokenSource, ITimer, TStateType, SemaphoreSlim>>();
/// <summary>
/// Gets the name.
@@ -42,12 +44,9 @@ namespace MediaBrowser.Controller.Net
/// </summary>
protected ILogger Logger;
- /// <summary>
- /// Initializes a new instance of the <see cref="BasePeriodicWebSocketListener{TStateType}" /> class.
- /// </summary>
- /// <param name="logger">The logger.</param>
- /// <exception cref="System.ArgumentNullException">logger</exception>
- protected BasePeriodicWebSocketListener(ILogger logger)
+ protected ITimerFactory TimerFactory { get; private set; }
+
+ protected BasePeriodicWebSocketListener(ILogger logger, ITimerFactory timerFactory)
{
if (logger == null)
{
@@ -55,6 +54,7 @@ namespace MediaBrowser.Controller.Net
}
Logger = logger;
+ TimerFactory = timerFactory;
}
/// <summary>
@@ -123,7 +123,7 @@ namespace MediaBrowser.Controller.Net
Logger.Debug("{1} Begin transmitting over websocket to {0}", message.Connection.RemoteEndPoint, GetType().Name);
var timer = SendOnTimer ?
- new Timer(TimerCallback, message.Connection, Timeout.Infinite, Timeout.Infinite) :
+ TimerFactory.Create(TimerCallback, message.Connection, Timeout.Infinite, Timeout.Infinite) :
null;
var state = new TStateType
@@ -136,7 +136,7 @@ namespace MediaBrowser.Controller.Net
lock (ActiveConnections)
{
- ActiveConnections.Add(new Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>(message.Connection, cancellationTokenSource, timer, state, semaphore));
+ ActiveConnections.Add(new Tuple<IWebSocketConnection, CancellationTokenSource, ITimer, TStateType, SemaphoreSlim>(message.Connection, cancellationTokenSource, timer, state, semaphore));
}
if (timer != null)
@@ -153,7 +153,7 @@ namespace MediaBrowser.Controller.Net
{
var connection = (IWebSocketConnection)state;
- Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> tuple;
+ Tuple<IWebSocketConnection, CancellationTokenSource, ITimer, TStateType, SemaphoreSlim> tuple;
lock (ActiveConnections)
{
@@ -176,7 +176,7 @@ namespace MediaBrowser.Controller.Net
protected void SendData(bool force)
{
- List<Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>> tuples;
+ List<Tuple<IWebSocketConnection, CancellationTokenSource, ITimer, TStateType, SemaphoreSlim>> tuples;
lock (ActiveConnections)
{
@@ -204,7 +204,7 @@ namespace MediaBrowser.Controller.Net
}
}
- private async void SendData(Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> tuple)
+ private async void SendData(Tuple<IWebSocketConnection, CancellationTokenSource, ITimer, TStateType, SemaphoreSlim> tuple)
{
var connection = tuple.Item1;
@@ -265,7 +265,7 @@ namespace MediaBrowser.Controller.Net
/// Disposes the connection.
/// </summary>
/// <param name="connection">The connection.</param>
- private void DisposeConnection(Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> connection)
+ private void DisposeConnection(Tuple<IWebSocketConnection, CancellationTokenSource, ITimer, TStateType, SemaphoreSlim> connection)
{
Logger.Debug("{1} stop transmitting over websocket to {0}", connection.Item1.RemoteEndPoint, GetType().Name);
diff --git a/MediaBrowser.Api/BrandingService.cs b/MediaBrowser.Api/BrandingService.cs
index e991565ad2..4602f56ed9 100644
--- a/MediaBrowser.Api/BrandingService.cs
+++ b/MediaBrowser.Api/BrandingService.cs
@@ -1,6 +1,6 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Branding;
-using ServiceStack;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
diff --git a/MediaBrowser.Api/ChannelService.cs b/MediaBrowser.Api/ChannelService.cs
index 2dfa0918d6..1eeb92530d 100644
--- a/MediaBrowser.Api/ChannelService.cs
+++ b/MediaBrowser.Api/ChannelService.cs
@@ -4,12 +4,12 @@ using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
diff --git a/MediaBrowser.Api/ConfigurationService.cs b/MediaBrowser.Api/ConfigurationService.cs
index 2e5c252d63..5dbb4b42aa 100644
--- a/MediaBrowser.Api/ConfigurationService.cs
+++ b/MediaBrowser.Api/ConfigurationService.cs
@@ -4,14 +4,15 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Serialization;
-using ServiceStack;
-using ServiceStack.Web;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
diff --git a/MediaBrowser.Api/ConnectService.cs b/MediaBrowser.Api/ConnectService.cs
index 494a6e756c..304dc366b1 100644
--- a/MediaBrowser.Api/ConnectService.cs
+++ b/MediaBrowser.Api/ConnectService.cs
@@ -3,11 +3,11 @@ using MediaBrowser.Controller.Connect;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Connect;
-using ServiceStack;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
@@ -78,11 +78,13 @@ namespace MediaBrowser.Api
{
private readonly IConnectManager _connectManager;
private readonly ISessionManager _sessionManager;
+ private readonly IAuthorizationContext _authContext;
- public ConnectService(IConnectManager connectManager, ISessionManager sessionManager)
+ public ConnectService(IConnectManager connectManager, ISessionManager sessionManager, IAuthorizationContext authContext)
{
_connectManager = connectManager;
_sessionManager = sessionManager;
+ _authContext = authContext;
}
public object Post(CreateConnectLink request)
@@ -142,7 +144,7 @@ namespace MediaBrowser.Api
throw new ResourceNotFoundException();
}
- var auth = AuthorizationContext.GetAuthorizationInfo(Request);
+ var auth = _authContext.GetAuthorizationInfo(Request);
if (string.IsNullOrWhiteSpace(auth.Client))
{
diff --git a/MediaBrowser.Api/Devices/DeviceService.cs b/MediaBrowser.Api/Devices/DeviceService.cs
index 759eea3537..544960f5f5 100644
--- a/MediaBrowser.Api/Devices/DeviceService.cs
+++ b/MediaBrowser.Api/Devices/DeviceService.cs
@@ -5,10 +5,9 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Devices;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Session;
-using ServiceStack;
-using ServiceStack.Web;
using System.IO;
using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Devices
{
diff --git a/MediaBrowser.Api/DisplayPreferencesService.cs b/MediaBrowser.Api/DisplayPreferencesService.cs
index 69c4c89eeb..5a21fc9f46 100644
--- a/MediaBrowser.Api/DisplayPreferencesService.cs
+++ b/MediaBrowser.Api/DisplayPreferencesService.cs
@@ -2,9 +2,9 @@
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Serialization;
-using ServiceStack;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
diff --git a/MediaBrowser.Api/Dlna/DlnaServerService.cs b/MediaBrowser.Api/Dlna/DlnaServerService.cs
index b2c7e5532a..8125951b5d 100644
--- a/MediaBrowser.Api/Dlna/DlnaServerService.cs
+++ b/MediaBrowser.Api/Dlna/DlnaServerService.cs
@@ -1,6 +1,4 @@
using MediaBrowser.Controller.Dlna;
-using ServiceStack;
-using ServiceStack.Web;
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -9,6 +7,8 @@ using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Dlna
{
@@ -98,7 +98,7 @@ namespace MediaBrowser.Api.Dlna
{
[ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", 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; }
}
@@ -111,9 +111,9 @@ namespace MediaBrowser.Api.Dlna
private readonly IMediaReceiverRegistrar _mediaReceiverRegistrar;
private const string XMLContentType = "text/xml; charset=UTF-8";
- private readonly IMemoryStreamProvider _memoryStreamProvider;
+ private readonly IMemoryStreamFactory _memoryStreamProvider;
- public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager, IMediaReceiverRegistrar mediaReceiverRegistrar, IMemoryStreamProvider memoryStreamProvider)
+ public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager, IMediaReceiverRegistrar mediaReceiverRegistrar, IMemoryStreamFactory memoryStreamProvider)
{
_dlnaManager = dlnaManager;
_contentDirectory = contentDirectory;
@@ -126,79 +126,64 @@ namespace MediaBrowser.Api.Dlna
{
var url = Request.AbsoluteUri;
var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase));
- var xml = _dlnaManager.GetServerDescriptionXml(GetRequestHeaders(), request.UuId, serverAddress);
+ var xml = _dlnaManager.GetServerDescriptionXml(Request.Headers.ToDictionary(), request.UuId, serverAddress);
return ResultFactory.GetResult(xml, XMLContentType);
}
public object Get(GetContentDirectory request)
{
- var xml = _contentDirectory.GetServiceXml(GetRequestHeaders());
+ var xml = _contentDirectory.GetServiceXml(Request.Headers.ToDictionary());
return ResultFactory.GetResult(xml, XMLContentType);
}
public object Get(GetMediaReceiverRegistrar request)
{
- var xml = _mediaReceiverRegistrar.GetServiceXml(GetRequestHeaders());
+ var xml = _mediaReceiverRegistrar.GetServiceXml(Request.Headers.ToDictionary());
return ResultFactory.GetResult(xml, XMLContentType);
}
public object Get(GetConnnectionManager request)
{
- var xml = _connectionManager.GetServiceXml(GetRequestHeaders());
+ var xml = _connectionManager.GetServiceXml(Request.Headers.ToDictionary());
return ResultFactory.GetResult(xml, XMLContentType);
}
- public async Task<object> Post(ProcessMediaReceiverRegistrarControlRequest request)
+ public object Post(ProcessMediaReceiverRegistrarControlRequest request)
{
- var response = await PostAsync(request.RequestStream, _mediaReceiverRegistrar).ConfigureAwait(false);
+ var response = PostAsync(request.RequestStream, _mediaReceiverRegistrar);
return ResultFactory.GetResult(response.Xml, XMLContentType);
}
- public async Task<object> Post(ProcessContentDirectoryControlRequest request)
+ public object Post(ProcessContentDirectoryControlRequest request)
{
- var response = await PostAsync(request.RequestStream, _contentDirectory).ConfigureAwait(false);
+ var response = PostAsync(request.RequestStream, _contentDirectory);
return ResultFactory.GetResult(response.Xml, XMLContentType);
}
- public async Task<object> Post(ProcessConnectionManagerControlRequest request)
+ public object Post(ProcessConnectionManagerControlRequest request)
{
- var response = await PostAsync(request.RequestStream, _connectionManager).ConfigureAwait(false);
+ var response = PostAsync(request.RequestStream, _connectionManager);
return ResultFactory.GetResult(response.Xml, XMLContentType);
}
- private async Task<ControlResponse> PostAsync(Stream requestStream, IUpnpService service)
+ private ControlResponse PostAsync(Stream requestStream, IUpnpService service)
{
var id = GetPathValue(2);
- using (var reader = new StreamReader(requestStream))
- {
- return service.ProcessControlRequest(new ControlRequest
- {
- Headers = GetRequestHeaders(),
- InputXml = await reader.ReadToEndAsync().ConfigureAwait(false),
- TargetServerUuId = id,
- RequestedUrl = Request.AbsoluteUri
- });
- }
- }
-
- private IDictionary<string, string> GetRequestHeaders()
- {
- var headers = new Dictionary<string, string>();
-
- foreach (var key in Request.Headers.AllKeys)
+ return service.ProcessControlRequest(new ControlRequest
{
- headers[key] = Request.Headers[key];
- }
-
- return headers;
+ Headers = Request.Headers.ToDictionary(),
+ InputXml = requestStream,
+ TargetServerUuId = id,
+ RequestedUrl = Request.AbsoluteUri
+ });
}
public object Get(GetIcon request)
diff --git a/MediaBrowser.Api/Dlna/DlnaService.cs b/MediaBrowser.Api/Dlna/DlnaService.cs
index dd4b59b920..ecb54bf5cb 100644
--- a/MediaBrowser.Api/Dlna/DlnaService.cs
+++ b/MediaBrowser.Api/Dlna/DlnaService.cs
@@ -1,9 +1,9 @@
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Dlna;
-using ServiceStack;
using System.Collections.Generic;
using System.Linq;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Dlna
{
diff --git a/MediaBrowser.Api/EnvironmentService.cs b/MediaBrowser.Api/EnvironmentService.cs
index b354cb26c2..bab538c91f 100644
--- a/MediaBrowser.Api/EnvironmentService.cs
+++ b/MediaBrowser.Api/EnvironmentService.cs
@@ -2,13 +2,11 @@
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
-using ServiceStack;
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.IO;
using System.Linq;
-using CommonIO;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
@@ -108,6 +106,7 @@ namespace MediaBrowser.Api
public class EnvironmentService : BaseApiService
{
const char UncSeparator = '\\';
+ const string UncSeparatorString = "\\";
/// <summary>
/// The _network manager
@@ -134,20 +133,17 @@ namespace MediaBrowser.Api
{
var result = new DefaultDirectoryBrowserInfo();
- if (Environment.OSVersion.Platform == PlatformID.Unix)
+ try
{
- try
+ var qnap = "/share/CACHEDEV1_DATA";
+ if (_fileSystem.DirectoryExists(qnap))
{
- var qnap = "/share/CACHEDEV1_DATA";
- if (Directory.Exists(qnap))
- {
- result.Path = qnap;
- }
+ result.Path = qnap;
}
- catch
- {
+ }
+ catch
+ {
- }
}
return ToOptimizedResult(result);
@@ -167,7 +163,7 @@ namespace MediaBrowser.Api
throw new ArgumentNullException("Path");
}
- var networkPrefix = UncSeparator.ToString(CultureInfo.InvariantCulture) + UncSeparator.ToString(CultureInfo.InvariantCulture);
+ var networkPrefix = UncSeparatorString + UncSeparatorString;
if (path.StartsWith(networkPrefix, StringComparison.OrdinalIgnoreCase) && path.LastIndexOf(UncSeparator) == 1)
{
@@ -204,13 +200,11 @@ namespace MediaBrowser.Api
/// <returns>IEnumerable{FileSystemEntryInfo}.</returns>
private IEnumerable<FileSystemEntryInfo> GetDrives()
{
- // Only include drives in the ready state or this method could end up being very slow, waiting for drives to timeout
- return DriveInfo.GetDrives().Where(d => d.IsReady).Select(d => new FileSystemEntryInfo
+ return _fileSystem.GetDrives().Select(d => new FileSystemEntryInfo
{
- Name = GetName(d),
- Path = d.RootDirectory.FullName,
+ Name = d.Name,
+ Path = d.FullName,
Type = FileSystemEntryType.Directory
-
});
}
@@ -229,16 +223,6 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Gets the name.
- /// </summary>
- /// <param name="drive">The drive.</param>
- /// <returns>System.String.</returns>
- private string GetName(DriveInfo drive)
- {
- return drive.Name;
- }
-
- /// <summary>
/// Gets the network shares.
/// </summary>
/// <param name="path">The path.</param>
@@ -263,7 +247,7 @@ namespace MediaBrowser.Api
// using EnumerateFileSystemInfos doesn't handle reparse points (symlinks)
var entries = _fileSystem.GetFileSystemEntries(request.Path).Where(i =>
{
- if (!request.IncludeHidden && i.Attributes.HasFlag(FileAttributes.Hidden))
+ if (!request.IncludeHidden && i.IsHidden)
{
return false;
}
diff --git a/MediaBrowser.Api/FilterService.cs b/MediaBrowser.Api/FilterService.cs
index 57aa5575f7..dc2f8e1364 100644
--- a/MediaBrowser.Api/FilterService.cs
+++ b/MediaBrowser.Api/FilterService.cs
@@ -2,11 +2,11 @@
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Querying;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
diff --git a/MediaBrowser.Api/GamesService.cs b/MediaBrowser.Api/GamesService.cs
index efa69d3336..a9394b52e4 100644
--- a/MediaBrowser.Api/GamesService.cs
+++ b/MediaBrowser.Api/GamesService.cs
@@ -4,7 +4,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -12,6 +11,7 @@ using System.IO;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
@@ -80,6 +80,8 @@ namespace MediaBrowser.Api
/// </summary>
private readonly IDtoService _dtoService;
+ private readonly IAuthorizationContext _authContext;
+
/// <summary>
/// Initializes a new instance of the <see cref="GamesService" /> class.
/// </summary>
@@ -88,13 +90,14 @@ namespace MediaBrowser.Api
/// <param name="libraryManager">The library manager.</param>
/// <param name="itemRepo">The item repo.</param>
/// <param name="dtoService">The dto service.</param>
- public GamesService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService)
+ public GamesService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService, IAuthorizationContext authContext)
{
_userManager = userManager;
_userDataRepository = userDataRepository;
_libraryManager = libraryManager;
_itemRepo = itemRepo;
_dtoService = dtoService;
+ _authContext = authContext;
}
/// <summary>
@@ -200,7 +203,7 @@ namespace MediaBrowser.Api
(!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder :
_libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id);
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
diff --git a/MediaBrowser.Api/Images/ImageByNameService.cs b/MediaBrowser.Api/Images/ImageByNameService.cs
index 7bcb39b4de..189e0f84b8 100644
--- a/MediaBrowser.Api/Images/ImageByNameService.cs
+++ b/MediaBrowser.Api/Images/ImageByNameService.cs
@@ -3,12 +3,15 @@ using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Dto;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using CommonIO;
+using System.Threading.Tasks;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Images
{
@@ -100,15 +103,17 @@ namespace MediaBrowser.Api.Images
private readonly IServerApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem;
+ private readonly IHttpResultFactory _resultFactory;
/// <summary>
/// Initializes a new instance of the <see cref="ImageByNameService" /> class.
/// </summary>
/// <param name="appPaths">The app paths.</param>
- public ImageByNameService(IServerApplicationPaths appPaths, IFileSystem fileSystem)
+ public ImageByNameService(IServerApplicationPaths appPaths, IFileSystem fileSystem, IHttpResultFactory resultFactory)
{
_appPaths = appPaths;
_fileSystem = fileSystem;
+ _resultFactory = resultFactory;
}
public object Get(GetMediaInfoImages request)
@@ -148,7 +153,7 @@ namespace MediaBrowser.Api.Images
.OrderBy(i => i.Name)
.ToList();
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
return new List<ImageByNameInfo>();
}
@@ -175,7 +180,7 @@ namespace MediaBrowser.Api.Images
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
- public object Get(GetGeneralImage request)
+ public Task<object> Get(GetGeneralImage request)
{
var filename = string.Equals(request.Type, "primary", StringComparison.OrdinalIgnoreCase)
? "folder"
@@ -185,7 +190,7 @@ namespace MediaBrowser.Api.Images
var path = paths.FirstOrDefault(_fileSystem.FileExists) ?? paths.FirstOrDefault();
- return ToStaticFileResult(path);
+ return _resultFactory.GetStaticFileResult(Request, path);
}
/// <summary>
@@ -205,7 +210,7 @@ namespace MediaBrowser.Api.Images
if (!string.IsNullOrEmpty(path))
{
- return ToStaticFileResult(path);
+ return _resultFactory.GetStaticFileResult(Request, path);
}
}
@@ -222,7 +227,7 @@ namespace MediaBrowser.Api.Images
if (!string.IsNullOrEmpty(path))
{
- return ToStaticFileResult(path);
+ return _resultFactory.GetStaticFileResult(Request, path);
}
}
@@ -234,7 +239,7 @@ namespace MediaBrowser.Api.Images
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
- public object Get(GetMediaInfoImage request)
+ public Task<object> Get(GetMediaInfoImage request)
{
var themeFolder = Path.Combine(_appPaths.MediaInfoImagesPath, request.Theme);
@@ -245,7 +250,7 @@ namespace MediaBrowser.Api.Images
if (!string.IsNullOrEmpty(path))
{
- return ToStaticFileResult(path);
+ return _resultFactory.GetStaticFileResult(Request, path);
}
}
@@ -261,7 +266,7 @@ namespace MediaBrowser.Api.Images
if (!string.IsNullOrEmpty(path))
{
- return ToStaticFileResult(path);
+ return _resultFactory.GetStaticFileResult(Request, path);
}
}
diff --git a/MediaBrowser.Api/Images/ImageRequest.cs b/MediaBrowser.Api/Images/ImageRequest.cs
index 8b86ee7e0f..b61c81972c 100644
--- a/MediaBrowser.Api/Images/ImageRequest.cs
+++ b/MediaBrowser.Api/Images/ImageRequest.cs
@@ -1,5 +1,5 @@
using MediaBrowser.Model.Entities;
-using ServiceStack;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Images
{
@@ -64,6 +64,8 @@ namespace MediaBrowser.Api.Images
[ApiMember(Name = "UnplayedCount", Description = "Optional unplayed count overlay to render", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? UnplayedCount { get; set; }
+ public int? Blur { get; set; }
+
[ApiMember(Name = "BackgroundColor", Description = "Optional. Apply a background color for transparent images.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string BackgroundColor { get; set; }
diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs
index 3280358dfa..392654aa25 100644
--- a/MediaBrowser.Api/Images/ImageService.cs
+++ b/MediaBrowser.Api/Images/ImageService.cs
@@ -8,15 +8,16 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using ServiceStack;
-using ServiceStack.Web;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Images
{
@@ -204,7 +205,6 @@ namespace MediaBrowser.Api.Images
/// </summary>
[Route("/Items/{Id}/Images/{Type}", "POST")]
[Route("/Items/{Id}/Images/{Type}/{Index}", "POST")]
- [Api(Description = "Posts an item image")]
[Authenticated(Roles = "admin")]
public class PostItemImage : DeleteImageRequest, IRequiresRequestStream, IReturnVoid
{
@@ -236,11 +236,12 @@ namespace MediaBrowser.Api.Images
private readonly IItemRepository _itemRepo;
private readonly IImageProcessor _imageProcessor;
private readonly IFileSystem _fileSystem;
+ private readonly IAuthorizationContext _authContext;
/// <summary>
/// Initializes a new instance of the <see cref="ImageService" /> class.
/// </summary>
- public ImageService(IUserManager userManager, ILibraryManager libraryManager, IProviderManager providerManager, IItemRepository itemRepo, IImageProcessor imageProcessor, IFileSystem fileSystem)
+ public ImageService(IUserManager userManager, ILibraryManager libraryManager, IProviderManager providerManager, IItemRepository itemRepo, IImageProcessor imageProcessor, IFileSystem fileSystem, IAuthorizationContext authContext)
{
_userManager = userManager;
_libraryManager = libraryManager;
@@ -248,6 +249,7 @@ namespace MediaBrowser.Api.Images
_itemRepo = itemRepo;
_imageProcessor = imageProcessor;
_fileSystem = fileSystem;
+ _authContext = authContext;
}
/// <summary>
@@ -320,7 +322,7 @@ namespace MediaBrowser.Api.Images
{
if (info.IsLocalFile)
{
- var fileInfo = new FileInfo(info.Path);
+ var fileInfo = _fileSystem.GetFileInfo(info.Path);
length = fileInfo.Length;
var size = _imageProcessor.GetImageSize(info);
@@ -425,7 +427,7 @@ namespace MediaBrowser.Api.Images
public void Post(PostUserImage request)
{
var userId = GetPathValue(1);
- AssertCanUpdateUser(_userManager, userId);
+ AssertCanUpdateUser(_authContext, _userManager, userId);
request.Type = (ImageType)Enum.Parse(typeof(ImageType), GetPathValue(3), true);
@@ -460,7 +462,7 @@ namespace MediaBrowser.Api.Images
public void Delete(DeleteUserImage request)
{
var userId = request.Id;
- AssertCanUpdateUser(_userManager, userId);
+ AssertCanUpdateUser(_authContext, _userManager, userId);
var item = _userManager.GetUserById(userId);
@@ -622,6 +624,7 @@ namespace MediaBrowser.Api.Images
AddPlayedIndicator = request.AddPlayedIndicator,
PercentPlayed = request.PercentPlayed ?? 0,
UnplayedCount = request.UnplayedCount,
+ Blur = request.Blur,
BackgroundColor = request.BackgroundColor,
ForegroundLayer = request.ForegroundLayer,
SupportedOutputFormats = supportedFormats
@@ -642,7 +645,7 @@ namespace MediaBrowser.Api.Images
// Sometimes imagemagick keeps a hold on the file briefly even after it's done writing to it.
// I'd rather do this than add a delay after saving the file
- FileShare = FileShare.ReadWrite
+ FileShare = FileShareMode.ReadWrite
}).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Api/Images/RemoteImageService.cs b/MediaBrowser.Api/Images/RemoteImageService.cs
index 25d7639c64..eb871746d7 100644
--- a/MediaBrowser.Api/Images/RemoteImageService.cs
+++ b/MediaBrowser.Api/Images/RemoteImageService.cs
@@ -8,14 +8,16 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Images
{
@@ -232,21 +234,18 @@ namespace MediaBrowser.Api.Images
try
{
- using (var reader = new StreamReader(pointerCachePath))
- {
- contentPath = await reader.ReadToEndAsync().ConfigureAwait(false);
- }
+ contentPath = _fileSystem.ReadAllText(pointerCachePath);
- if (_fileSystem.FileExists(contentPath))
+ if (_fileSystem.FileExists(contentPath))
{
return await ResultFactory.GetStaticFileResult(Request, contentPath).ConfigureAwait(false);
}
}
- catch (DirectoryNotFoundException)
+ catch (FileNotFoundException)
{
// Means the file isn't cached yet
}
- catch (FileNotFoundException)
+ catch (IOException)
{
// Means the file isn't cached yet
}
@@ -254,10 +253,7 @@ namespace MediaBrowser.Api.Images
await DownloadImage(request.ImageUrl, urlHash, pointerCachePath).ConfigureAwait(false);
// Read the pointer file again
- using (var reader = new StreamReader(pointerCachePath))
- {
- contentPath = await reader.ReadToEndAsync().ConfigureAwait(false);
- }
+ contentPath = _fileSystem.ReadAllText(pointerCachePath);
return await ResultFactory.GetStaticFileResult(Request, contentPath).ConfigureAwait(false);
}
@@ -285,17 +281,14 @@ namespace MediaBrowser.Api.Images
_fileSystem.CreateDirectory(Path.GetDirectoryName(fullCachePath));
using (var stream = result.Content)
{
- using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
+ using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
{
await stream.CopyToAsync(filestream).ConfigureAwait(false);
}
}
_fileSystem.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
- using (var writer = new StreamWriter(pointerCachePath))
- {
- await writer.WriteAsync(fullCachePath).ConfigureAwait(false);
- }
+ _fileSystem.WriteAllText(pointerCachePath, fullCachePath);
}
/// <summary>
diff --git a/MediaBrowser.Api/ItemLookupService.cs b/MediaBrowser.Api/ItemLookupService.cs
index 357ff43949..b5c51bfef0 100644
--- a/MediaBrowser.Api/ItemLookupService.cs
+++ b/MediaBrowser.Api/ItemLookupService.cs
@@ -8,15 +8,15 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Providers;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
@@ -86,6 +86,12 @@ namespace MediaBrowser.Api
{
}
+ [Route("/Items/RemoteSearch/Book", "POST")]
+ [Authenticated]
+ public class GetBookRemoteSearchResults : RemoteSearchQuery<BookInfo>, IReturn<List<RemoteSearchResult>>
+ {
+ }
+
[Route("/Items/RemoteSearch/Image", "GET", Summary = "Gets a remote image")]
public class GetRemoteSearchImage
{
@@ -145,6 +151,13 @@ namespace MediaBrowser.Api
return ToOptimizedResult(result);
}
+ public async Task<object> Post(GetBookRemoteSearchResults request)
+ {
+ var result = await _providerManager.GetRemoteSearchResults<Book, BookInfo>(request, CancellationToken.None).ConfigureAwait(false);
+
+ return ToOptimizedResult(result);
+ }
+
public async Task<object> Post(GetMovieRemoteSearchResults request)
{
var result = await _providerManager.GetRemoteSearchResults<Movie, MovieInfo>(request, CancellationToken.None).ConfigureAwait(false);
@@ -225,7 +238,8 @@ namespace MediaBrowser.Api
ImageRefreshMode = ImageRefreshMode.FullRefresh,
ReplaceAllMetadata = true,
ReplaceAllImages = request.ReplaceAllImages,
- SearchResult = request
+ SearchResult = request,
+ ForceEnableInternetMetadata = true
}, CancellationToken.None);
Task.WaitAll(task);
@@ -245,21 +259,18 @@ namespace MediaBrowser.Api
try
{
- using (var reader = new StreamReader(pointerCachePath))
- {
- contentPath = await reader.ReadToEndAsync().ConfigureAwait(false);
- }
+ contentPath = _fileSystem.ReadAllText(pointerCachePath);
if (_fileSystem.FileExists(contentPath))
{
return await ResultFactory.GetStaticFileResult(Request, contentPath).ConfigureAwait(false);
}
}
- catch (DirectoryNotFoundException)
+ catch (FileNotFoundException)
{
// Means the file isn't cached yet
}
- catch (FileNotFoundException)
+ catch (IOException)
{
// Means the file isn't cached yet
}
@@ -267,10 +278,7 @@ namespace MediaBrowser.Api
await DownloadImage(request.ProviderName, request.ImageUrl, urlHash, pointerCachePath).ConfigureAwait(false);
// Read the pointer file again
- using (var reader = new StreamReader(pointerCachePath))
- {
- contentPath = await reader.ReadToEndAsync().ConfigureAwait(false);
- }
+ contentPath = _fileSystem.ReadAllText(pointerCachePath);
return await ResultFactory.GetStaticFileResult(Request, contentPath).ConfigureAwait(false);
}
@@ -294,17 +302,14 @@ namespace MediaBrowser.Api
_fileSystem.CreateDirectory(Path.GetDirectoryName(fullCachePath));
using (var stream = result.Content)
{
- using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
+ using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
{
await stream.CopyToAsync(filestream).ConfigureAwait(false);
}
}
_fileSystem.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
- using (var writer = new StreamWriter(pointerCachePath))
- {
- await writer.WriteAsync(fullCachePath).ConfigureAwait(false);
- }
+ _fileSystem.WriteAllText(pointerCachePath, fullCachePath);
}
/// <summary>
diff --git a/MediaBrowser.Api/ItemRefreshService.cs b/MediaBrowser.Api/ItemRefreshService.cs
index a918e841fb..81547637ef 100644
--- a/MediaBrowser.Api/ItemRefreshService.cs
+++ b/MediaBrowser.Api/ItemRefreshService.cs
@@ -2,10 +2,12 @@
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Providers;
-using ServiceStack;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs
index 2c2e599d4d..b0a0e79aee 100644
--- a/MediaBrowser.Api/ItemUpdateService.cs
+++ b/MediaBrowser.Api/ItemUpdateService.cs
@@ -4,17 +4,17 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
diff --git a/MediaBrowser.Api/Library/FileOrganizationService.cs b/MediaBrowser.Api/Library/FileOrganizationService.cs
index ca391bef08..ea610ac5c1 100644
--- a/MediaBrowser.Api/Library/FileOrganizationService.cs
+++ b/MediaBrowser.Api/Library/FileOrganizationService.cs
@@ -3,10 +3,10 @@ using MediaBrowser.Controller.FileOrganization;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.FileOrganization;
using MediaBrowser.Model.Querying;
-using ServiceStack;
using System.Threading.Tasks;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Library
{
@@ -204,10 +204,10 @@ namespace MediaBrowser.Api.Library
public void Post(DeleteSmartMatchEntry request)
{
- request.Entries.ForEach(entry =>
+ foreach (var entry in request.Entries)
{
_iFileOrganizationService.DeleteSmartMatchEntry(entry.Name, entry.Value);
- });
+ }
}
}
}
diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs
index e0dfcb9ad2..15c1cbe827 100644
--- a/MediaBrowser.Api/Library/LibraryService.cs
+++ b/MediaBrowser.Api/Library/LibraryService.cs
@@ -1,6 +1,5 @@
using MediaBrowser.Api.Movies;
using MediaBrowser.Api.Music;
-using MediaBrowser.Controller.Activity;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@@ -8,7 +7,6 @@ using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.TV;
@@ -16,7 +14,6 @@ using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -24,8 +21,12 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Library
{
@@ -322,13 +323,9 @@ namespace MediaBrowser.Api.Library
if (item is Game)
{
- return new GamesService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService)
+ return new GamesService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _authContext)
{
- AuthorizationContext = AuthorizationContext,
- Logger = Logger,
Request = Request,
- SessionContext = SessionContext,
- ResultFactory = ResultFactory
}.Get(new GetSimilarGames
{
@@ -340,13 +337,9 @@ namespace MediaBrowser.Api.Library
}
if (item is MusicAlbum)
{
- return new AlbumsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService)
+ return new AlbumsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _authContext)
{
- AuthorizationContext = AuthorizationContext,
- Logger = Logger,
Request = Request,
- SessionContext = SessionContext,
- ResultFactory = ResultFactory
}.Get(new GetSimilarAlbums
{
@@ -359,13 +352,9 @@ namespace MediaBrowser.Api.Library
}
if (item is MusicArtist)
{
- return new AlbumsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService)
+ return new AlbumsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _authContext)
{
- AuthorizationContext = AuthorizationContext,
- Logger = Logger,
Request = Request,
- SessionContext = SessionContext,
- ResultFactory = ResultFactory
}.Get(new GetSimilarArtists
{
@@ -380,13 +369,9 @@ namespace MediaBrowser.Api.Library
if (item is Movie || (program != null && program.IsMovie) || item is Trailer)
{
- return new MoviesService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _config)
+ return new MoviesService(_userManager, _libraryManager, _dtoService, _config, _authContext)
{
- AuthorizationContext = AuthorizationContext,
- Logger = Logger,
Request = Request,
- SessionContext = SessionContext,
- ResultFactory = ResultFactory
}.Get(new GetSimilarMovies
{
@@ -399,13 +384,9 @@ namespace MediaBrowser.Api.Library
if (item is Series || (program != null && program.IsSeries))
{
- return new TvShowsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _tvManager)
+ return new TvShowsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _tvManager, _authContext)
{
- AuthorizationContext = AuthorizationContext,
- Logger = Logger,
Request = Request,
- SessionContext = SessionContext,
- ResultFactory = ResultFactory
}.Get(new GetSimilarShows
{
@@ -430,7 +411,7 @@ namespace MediaBrowser.Api.Library
items = items.Where(i => i.IsHidden == val).ToList();
}
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
var result = new ItemsResult
{
@@ -611,7 +592,7 @@ namespace MediaBrowser.Api.Library
var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
BaseItem parent = item.GetParent();
@@ -699,14 +680,17 @@ namespace MediaBrowser.Api.Library
/// <param name="request">The request.</param>
public void Post(RefreshLibrary request)
{
- try
- {
- _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None);
- }
- catch (Exception ex)
+ Task.Run(() =>
{
- Logger.ErrorException("Error refreshing library", ex);
- }
+ try
+ {
+ _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None);
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error refreshing library", ex);
+ }
+ });
}
/// <summary>
@@ -840,7 +824,7 @@ namespace MediaBrowser.Api.Library
item = item.GetParent();
}
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
var dtos = item.ThemeSongIds.Select(_libraryManager.GetItemById)
.Where(i => i != null)
@@ -884,7 +868,7 @@ namespace MediaBrowser.Api.Library
item = item.GetParent();
}
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
var dtos = item.ThemeVideoIds.Select(_libraryManager.GetItemById)
.Where(i => i != null)
diff --git a/MediaBrowser.Api/Library/LibraryStructureService.cs b/MediaBrowser.Api/Library/LibraryStructureService.cs
index e872061a74..c3bb80dcb5 100644
--- a/MediaBrowser.Api/Library/LibraryStructureService.cs
+++ b/MediaBrowser.Api/Library/LibraryStructureService.cs
@@ -2,17 +2,19 @@
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Entities;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Library
{
@@ -257,7 +259,7 @@ namespace MediaBrowser.Api.Library
if (!_fileSystem.DirectoryExists(currentPath))
{
- throw new DirectoryNotFoundException("The media collection does not exist");
+ throw new FileNotFoundException("The media collection does not exist");
}
if (!string.Equals(currentPath, newPath, StringComparison.OrdinalIgnoreCase) && _fileSystem.DirectoryExists(newPath))
diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs
index 90767b135b..c829ad2abb 100644
--- a/MediaBrowser.Api/LiveTv/LiveTvService.cs
+++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs
@@ -8,7 +8,6 @@ using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Querying;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -16,11 +15,13 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Api.Playback.Progressive;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Server.Implementations.LiveTv.EmbyTV;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.LiveTv
{
@@ -694,8 +695,10 @@ namespace MediaBrowser.Api.LiveTv
private readonly ILibraryManager _libraryManager;
private readonly IDtoService _dtoService;
private readonly IFileSystem _fileSystem;
+ private readonly IAuthorizationContext _authContext;
+ private readonly ISessionContext _sessionContext;
- public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IServerConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService, IFileSystem fileSystem)
+ public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IServerConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService, IFileSystem fileSystem, IAuthorizationContext authContext, ISessionContext sessionContext)
{
_liveTvManager = liveTvManager;
_userManager = userManager;
@@ -704,13 +707,15 @@ namespace MediaBrowser.Api.LiveTv
_libraryManager = libraryManager;
_dtoService = dtoService;
_fileSystem = fileSystem;
+ _authContext = authContext;
+ _sessionContext = sessionContext;
}
- public async Task<object> Get(GetLiveRecordingFile request)
+ public object Get(GetLiveRecordingFile request)
{
- var path = EmbyTV.Current.GetActiveRecordingPath(request.Id);
+ var path = _liveTvManager.GetEmbyTvActiveRecordingPath(request.Id);
- if (path == null)
+ if (string.IsNullOrWhiteSpace(path))
{
throw new FileNotFoundException();
}
@@ -719,25 +724,23 @@ namespace MediaBrowser.Api.LiveTv
outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType(path);
- var streamSource = new ProgressiveFileCopier(_fileSystem, path, outputHeaders, null, Logger, CancellationToken.None)
+ return new ProgressiveFileCopier(_fileSystem, path, outputHeaders, null, Logger, CancellationToken.None)
{
AllowEndOfFile = false
};
- return ResultFactory.GetAsyncStreamWriter(streamSource);
}
public async Task<object> Get(GetLiveStreamFile request)
{
- var directStreamProvider = (await EmbyTV.Current.GetLiveStream(request.Id).ConfigureAwait(false)) as IDirectStreamProvider;
+ var directStreamProvider = (await _liveTvManager.GetEmbyTvLiveStream(request.Id).ConfigureAwait(false)) as IDirectStreamProvider;
var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType("file." + request.Container);
- var streamSource = new ProgressiveFileCopier(directStreamProvider, outputHeaders, null, Logger, CancellationToken.None)
+ return new ProgressiveFileCopier(directStreamProvider, outputHeaders, null, Logger, CancellationToken.None)
{
AllowEndOfFile = false
};
- return ResultFactory.GetAsyncStreamWriter(streamSource);
}
public object Get(GetDefaultListingProvider request)
@@ -820,7 +823,7 @@ namespace MediaBrowser.Api.LiveTv
private void AssertUserCanManageLiveTv()
{
- var user = SessionContext.GetUser(Request).Result;
+ var user = _sessionContext.GetUser(Request).Result;
if (user == null)
{
@@ -908,7 +911,7 @@ namespace MediaBrowser.Api.LiveTv
var user = string.IsNullOrEmpty(request.UserId) ? null : _userManager.GetUserById(request.UserId);
- var options = GetDtoOptions(request);
+ var options = GetDtoOptions(_authContext, request);
RemoveFields(options);
options.AddCurrentProgram = request.AddCurrentProgram;
@@ -938,7 +941,7 @@ namespace MediaBrowser.Api.LiveTv
var item = _libraryManager.GetItemById(request.Id);
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
var result = _dtoService.GetBaseItemDto(item, dtoOptions, user);
@@ -1003,7 +1006,7 @@ namespace MediaBrowser.Api.LiveTv
}
}
- var result = await _liveTvManager.GetPrograms(query, GetDtoOptions(request), CancellationToken.None).ConfigureAwait(false);
+ var result = await _liveTvManager.GetPrograms(query, GetDtoOptions(_authContext, request), CancellationToken.None).ConfigureAwait(false);
return ToOptimizedResult(result);
}
@@ -1024,7 +1027,7 @@ namespace MediaBrowser.Api.LiveTv
EnableTotalRecordCount = request.EnableTotalRecordCount
};
- var result = await _liveTvManager.GetRecommendedPrograms(query, GetDtoOptions(request), CancellationToken.None).ConfigureAwait(false);
+ var result = await _liveTvManager.GetRecommendedPrograms(query, GetDtoOptions(_authContext, request), CancellationToken.None).ConfigureAwait(false);
return ToOptimizedResult(result);
}
@@ -1036,8 +1039,8 @@ namespace MediaBrowser.Api.LiveTv
public async Task<object> Get(GetRecordings request)
{
- var options = GetDtoOptions(request);
- options.DeviceId = AuthorizationContext.GetAuthorizationInfo(Request).DeviceId;
+ var options = GetDtoOptions(_authContext, request);
+ options.DeviceId = _authContext.GetAuthorizationInfo(Request).DeviceId;
var result = await _liveTvManager.GetRecordings(new RecordingQuery
{
@@ -1063,8 +1066,8 @@ namespace MediaBrowser.Api.LiveTv
public async Task<object> Get(GetRecordingSeries request)
{
- var options = GetDtoOptions(request);
- options.DeviceId = AuthorizationContext.GetAuthorizationInfo(Request).DeviceId;
+ var options = GetDtoOptions(_authContext, request);
+ options.DeviceId = _authContext.GetAuthorizationInfo(Request).DeviceId;
var result = await _liveTvManager.GetRecordingSeries(new RecordingQuery
{
@@ -1088,7 +1091,7 @@ namespace MediaBrowser.Api.LiveTv
var user = string.IsNullOrEmpty(request.UserId) ? null : _userManager.GetUserById(request.UserId);
var options = new DtoOptions();
- options.DeviceId = AuthorizationContext.GetAuthorizationInfo(Request).DeviceId;
+ options.DeviceId = _authContext.GetAuthorizationInfo(Request).DeviceId;
var result = await _liveTvManager.GetRecording(request.Id, options, CancellationToken.None, user).ConfigureAwait(false);
diff --git a/MediaBrowser.Api/LocalizationService.cs b/MediaBrowser.Api/LocalizationService.cs
index a8bb61b737..90b963149b 100644
--- a/MediaBrowser.Api/LocalizationService.cs
+++ b/MediaBrowser.Api/LocalizationService.cs
@@ -1,10 +1,9 @@
-using MediaBrowser.Controller.Localization;
-using MediaBrowser.Controller.Net;
+using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
-using ServiceStack;
using System.Collections.Generic;
using System.Linq;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj
index d7091df566..c1a7347b5f 100644
--- a/MediaBrowser.Api/MediaBrowser.Api.csproj
+++ b/MediaBrowser.Api/MediaBrowser.Api.csproj
@@ -11,10 +11,9 @@
<AssemblyName>MediaBrowser.Api</AssemblyName>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
- <TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
- <ReleaseVersion>
- </ReleaseVersion>
- <TargetFrameworkProfile />
+ <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>
@@ -46,32 +45,10 @@
<RunPostBuildEvent>Always</RunPostBuildEvent>
</PropertyGroup>
<ItemGroup>
- <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
- </Reference>
- <Reference Include="MoreLinq">
- <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
- </Reference>
- <Reference Include="Patterns.Logging">
- <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
- </Reference>
- <Reference Include="System" />
- <Reference Include="System.Core" />
- <Reference Include="Microsoft.CSharp" />
- <Reference Include="System.Data" />
- <Reference Include="System.Xml" />
- <Reference Include="ServiceStack.Interfaces">
- <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath>
- </Reference>
- <Reference Include="ServiceStack.Text">
- <HintPath>..\ThirdParty\ServiceStack.Text\ServiceStack.Text.dll</HintPath>
- </Reference>
- </ItemGroup>
- <ItemGroup>
<Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link>
</Compile>
+ <Compile Include="BasePeriodicWebSocketListener.cs" />
<Compile Include="BrandingService.cs" />
<Compile Include="ChannelService.cs" />
<Compile Include="ConnectService.cs" />
@@ -165,6 +142,7 @@
<Compile Include="System\ActivityLogWebSocketListener.cs" />
<Compile Include="System\SystemService.cs" />
<Compile Include="Movies\TrailersService.cs" />
+ <Compile Include="TestService.cs" />
<Compile Include="TvShowsService.cs" />
<Compile Include="UserLibrary\ArtistsService.cs" />
<Compile Include="UserLibrary\BaseItemsByNameService.cs" />
@@ -198,15 +176,8 @@
<Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project>
<Name>MediaBrowser.Model</Name>
</ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Server.Implementations\MediaBrowser.Server.Implementations.csproj">
- <Project>{2e781478-814d-4a48-9d80-bff206441a65}</Project>
- <Name>MediaBrowser.Server.Implementations</Name>
- </ProjectReference>
- </ItemGroup>
- <ItemGroup>
- <None Include="packages.config" />
</ItemGroup>
- <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>
</PostBuildEvent>
diff --git a/MediaBrowser.Api/MediaBrowser.Api.nuget.targets b/MediaBrowser.Api/MediaBrowser.Api.nuget.targets
new file mode 100644
index 0000000000..e69ce0e64f
--- /dev/null
+++ b/MediaBrowser.Api/MediaBrowser.Api.nuget.targets
@@ -0,0 +1,6 @@
+<?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 e8c33abc61..917a3bc0bc 100644
--- a/MediaBrowser.Api/Movies/CollectionService.cs
+++ b/MediaBrowser.Api/Movies/CollectionService.cs
@@ -2,11 +2,11 @@
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Collections;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Movies
{
@@ -51,16 +51,18 @@ namespace MediaBrowser.Api.Movies
{
private readonly ICollectionManager _collectionManager;
private readonly IDtoService _dtoService;
+ private readonly IAuthorizationContext _authContext;
- public CollectionService(ICollectionManager collectionManager, IDtoService dtoService)
+ public CollectionService(ICollectionManager collectionManager, IDtoService dtoService, IAuthorizationContext authContext)
{
_collectionManager = collectionManager;
_dtoService = dtoService;
+ _authContext = authContext;
}
public async Task<object> Post(CreateCollection request)
{
- var userId = AuthorizationContext.GetAuthorizationInfo(Request).UserId;
+ var userId = _authContext.GetAuthorizationInfo(Request).UserId;
var parentId = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId);
@@ -74,7 +76,7 @@ namespace MediaBrowser.Api.Movies
}).ConfigureAwait(false);
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
var dto = _dtoService.GetBaseItemDto(item, dtoOptions);
diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs
index 559bca7557..b5c6f52fc0 100644
--- a/MediaBrowser.Api/Movies/MoviesService.cs
+++ b/MediaBrowser.Api/Movies/MoviesService.cs
@@ -8,14 +8,14 @@ using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
-using MoreLinq;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Movies
{
@@ -78,35 +78,22 @@ namespace MediaBrowser.Api.Movies
/// </summary>
private readonly IUserManager _userManager;
- /// <summary>
- /// The _user data repository
- /// </summary>
- private readonly IUserDataManager _userDataRepository;
- /// <summary>
- /// The _library manager
- /// </summary>
private readonly ILibraryManager _libraryManager;
- private readonly IItemRepository _itemRepo;
private readonly IDtoService _dtoService;
private readonly IServerConfigurationManager _config;
+ private readonly IAuthorizationContext _authContext;
/// <summary>
/// Initializes a new instance of the <see cref="MoviesService" /> class.
/// </summary>
- /// <param name="userManager">The user manager.</param>
- /// <param name="userDataRepository">The user data repository.</param>
- /// <param name="libraryManager">The library manager.</param>
- /// <param name="itemRepo">The item repo.</param>
- /// <param name="dtoService">The dto service.</param>
- public MoviesService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService, IServerConfigurationManager config)
+ public MoviesService(IUserManager userManager, ILibraryManager libraryManager, IDtoService dtoService, IServerConfigurationManager config, IAuthorizationContext authContext)
{
_userManager = userManager;
- _userDataRepository = userDataRepository;
_libraryManager = libraryManager;
- _itemRepo = itemRepo;
_dtoService = dtoService;
_config = config;
+ _authContext = authContext;
}
/// <summary>
@@ -132,7 +119,7 @@ namespace MediaBrowser.Api.Movies
{
var user = _userManager.GetUserById(request.UserId);
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
dtoOptions.Fields = request.GetItemFields().ToList();
@@ -156,7 +143,7 @@ namespace MediaBrowser.Api.Movies
itemTypes.Add(typeof(LiveTvProgram).Name);
}
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
diff --git a/MediaBrowser.Api/Movies/TrailersService.cs b/MediaBrowser.Api/Movies/TrailersService.cs
index c70620cf9f..eb5365ab8f 100644
--- a/MediaBrowser.Api/Movies/TrailersService.cs
+++ b/MediaBrowser.Api/Movies/TrailersService.cs
@@ -3,10 +3,10 @@ using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Querying;
-using ServiceStack;
using MediaBrowser.Controller.Collections;
-using MediaBrowser.Controller.Localization;
+using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Movies
{
@@ -39,8 +39,9 @@ namespace MediaBrowser.Api.Movies
private readonly ICollectionManager _collectionManager;
private readonly ILocalizationManager _localizationManager;
private readonly IJsonSerializer _json;
+ private readonly IAuthorizationContext _authContext;
- public TrailersService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IDtoService dtoService, ICollectionManager collectionManager, ILocalizationManager localizationManager, IJsonSerializer json)
+ public TrailersService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IDtoService dtoService, ICollectionManager collectionManager, ILocalizationManager localizationManager, IJsonSerializer json, IAuthorizationContext authContext)
{
_userManager = userManager;
_userDataRepository = userDataRepository;
@@ -49,6 +50,7 @@ namespace MediaBrowser.Api.Movies
_collectionManager = collectionManager;
_localizationManager = localizationManager;
_json = json;
+ _authContext = authContext;
}
public object Get(Getrailers request)
@@ -58,13 +60,9 @@ namespace MediaBrowser.Api.Movies
getItems.IncludeItemTypes = "Trailer";
- return new ItemsService(_userManager, _libraryManager, _localizationManager, _dtoService)
+ return new ItemsService(_userManager, _libraryManager, _localizationManager, _dtoService, _authContext)
{
- AuthorizationContext = AuthorizationContext,
- Logger = Logger,
Request = Request,
- ResultFactory = ResultFactory,
- SessionContext = SessionContext
}.Get(getItems);
}
diff --git a/MediaBrowser.Api/Music/AlbumsService.cs b/MediaBrowser.Api/Music/AlbumsService.cs
index 2d7e909bf6..bc7ae2be27 100644
--- a/MediaBrowser.Api/Music/AlbumsService.cs
+++ b/MediaBrowser.Api/Music/AlbumsService.cs
@@ -4,11 +4,11 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Music
{
@@ -40,19 +40,21 @@ namespace MediaBrowser.Api.Music
private readonly ILibraryManager _libraryManager;
private readonly IItemRepository _itemRepo;
private readonly IDtoService _dtoService;
+ private readonly IAuthorizationContext _authContext;
- public AlbumsService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService)
+ public AlbumsService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService, IAuthorizationContext authContext)
{
_userManager = userManager;
_userDataRepository = userDataRepository;
_libraryManager = libraryManager;
_itemRepo = itemRepo;
_dtoService = dtoService;
+ _authContext = authContext;
}
public async Task<object> Get(GetSimilarArtists request)
{
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
var result = await SimilarItemsHelper.GetSimilarItemsResult(dtoOptions, _userManager,
_itemRepo,
@@ -73,7 +75,7 @@ namespace MediaBrowser.Api.Music
/// <returns>System.Object.</returns>
public async Task<object> Get(GetSimilarAlbums request)
{
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
var result = await SimilarItemsHelper.GetSimilarItemsResult(dtoOptions, _userManager,
_itemRepo,
diff --git a/MediaBrowser.Api/Music/InstantMixService.cs b/MediaBrowser.Api/Music/InstantMixService.cs
index 19265408bf..d735dd7cd0 100644
--- a/MediaBrowser.Api/Music/InstantMixService.cs
+++ b/MediaBrowser.Api/Music/InstantMixService.cs
@@ -5,10 +5,10 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Model.Querying;
-using ServiceStack;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Music
{
@@ -68,13 +68,15 @@ namespace MediaBrowser.Api.Music
private readonly IDtoService _dtoService;
private readonly ILibraryManager _libraryManager;
private readonly IMusicManager _musicManager;
+ private readonly IAuthorizationContext _authContext;
- public InstantMixService(IUserManager userManager, IDtoService dtoService, IMusicManager musicManager, ILibraryManager libraryManager)
+ public InstantMixService(IUserManager userManager, IDtoService dtoService, IMusicManager musicManager, ILibraryManager libraryManager, IAuthorizationContext authContext)
{
_userManager = userManager;
_dtoService = dtoService;
_musicManager = musicManager;
_libraryManager = libraryManager;
+ _authContext = authContext;
}
public Task<object> Get(GetInstantMixFromItem request)
@@ -171,7 +173,7 @@ namespace MediaBrowser.Api.Music
TotalRecordCount = list.Count
};
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
result.Items = (await _dtoService.GetBaseItemDtos(list.Take(request.Limit ?? list.Count), dtoOptions, user).ConfigureAwait(false)).ToArray();
diff --git a/MediaBrowser.Api/NewsService.cs b/MediaBrowser.Api/NewsService.cs
index 6243bf25f4..542103799b 100644
--- a/MediaBrowser.Api/NewsService.cs
+++ b/MediaBrowser.Api/NewsService.cs
@@ -1,7 +1,6 @@
-using MediaBrowser.Controller.News;
-using MediaBrowser.Model.News;
+using MediaBrowser.Model.News;
using MediaBrowser.Model.Querying;
-using ServiceStack;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
diff --git a/MediaBrowser.Api/NotificationsService.cs b/MediaBrowser.Api/NotificationsService.cs
index 5103d1b5cd..58e413cef2 100644
--- a/MediaBrowser.Api/NotificationsService.cs
+++ b/MediaBrowser.Api/NotificationsService.cs
@@ -2,12 +2,12 @@
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Notifications;
using MediaBrowser.Model.Notifications;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
diff --git a/MediaBrowser.Api/PackageReviewService.cs b/MediaBrowser.Api/PackageReviewService.cs
index 0a5b9bef8d..baf1adc192 100644
--- a/MediaBrowser.Api/PackageReviewService.cs
+++ b/MediaBrowser.Api/PackageReviewService.cs
@@ -3,12 +3,12 @@ using MediaBrowser.Controller;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Serialization;
-using ServiceStack;
using System.Collections.Generic;
using System.Globalization;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
diff --git a/MediaBrowser.Api/PackageService.cs b/MediaBrowser.Api/PackageService.cs
index dc7c4c6c18..125cec681b 100644
--- a/MediaBrowser.Api/PackageService.cs
+++ b/MediaBrowser.Api/PackageService.cs
@@ -3,12 +3,12 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Updates;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 2fcfa90bdf..3718edba49 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -2,7 +2,6 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dlna;
@@ -14,16 +13,16 @@ using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Serialization;
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.Diagnostics;
namespace MediaBrowser.Api.Playback
{
@@ -73,13 +72,15 @@ namespace MediaBrowser.Api.Playback
public static IServerApplicationHost AppHost;
public static IHttpClient HttpClient;
+ protected IAuthorizationContext AuthorizationContext { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="BaseStreamingService" /> class.
/// </summary>
- protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer)
+ protected BaseStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext)
{
JsonSerializer = jsonSerializer;
+ AuthorizationContext = authorizationContext;
ZipClient = zipClient;
MediaSourceManager = mediaSourceManager;
DeviceManager = deviceManager;
@@ -335,9 +336,12 @@ namespace MediaBrowser.Api.Playback
{
// 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) && videoStream.Level == -99)
+ if (string.Equals(videoStream.Codec, "mpeg4", StringComparison.OrdinalIgnoreCase))
{
- return false;
+ if (videoStream.Level == -99 || videoStream.Level == 15)
+ {
+ return false;
+ }
}
}
return true;
@@ -352,6 +356,11 @@ namespace MediaBrowser.Api.Playback
return defaultEncoder;
}
+ protected virtual string GetDefaultH264Preset()
+ {
+ return "superfast";
+ }
+
/// <summary>
/// Gets the video bitrate to specify on the command line
/// </summary>
@@ -375,7 +384,7 @@ namespace MediaBrowser.Api.Playback
}
else
{
- param += "-preset superfast";
+ param += "-preset " + GetDefaultH264Preset();
}
if (encodingOptions.H264Crf >= 0 && encodingOptions.H264Crf <= 51)
@@ -529,7 +538,6 @@ namespace MediaBrowser.Api.Playback
if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) &&
- !string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{
param = "-pix_fmt yuv420p " + param;
@@ -1152,32 +1160,24 @@ namespace MediaBrowser.Api.Playback
var transcodingId = Guid.NewGuid().ToString("N");
var commandLineArgs = GetCommandLineArguments(outputPath, state, true);
- var process = new Process
+ var process = ApiEntryPoint.Instance.ProcessFactory.Create(new ProcessOptions
{
- StartInfo = new ProcessStartInfo
- {
- CreateNoWindow = true,
- UseShellExecute = false,
-
- // Must consume both stdout and stderr or deadlocks may occur
- //RedirectStandardOutput = true,
- RedirectStandardError = true,
- RedirectStandardInput = true,
+ CreateNoWindow = true,
+ UseShellExecute = false,
- FileName = MediaEncoder.EncoderPath,
- Arguments = commandLineArgs,
+ // Must consume both stdout and stderr or deadlocks may occur
+ //RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ RedirectStandardInput = true,
- WindowStyle = ProcessWindowStyle.Hidden,
- ErrorDialog = false
- },
+ FileName = MediaEncoder.EncoderPath,
+ Arguments = commandLineArgs,
- EnableRaisingEvents = true
- };
-
- if (!string.IsNullOrWhiteSpace(workingDirectory))
- {
- process.StartInfo.WorkingDirectory = workingDirectory;
- }
+ IsHidden = true,
+ ErrorDialog = false,
+ EnableRaisingEvents = true,
+ WorkingDirectory = !string.IsNullOrWhiteSpace(workingDirectory) ? workingDirectory : null
+ });
var transcodingJob = ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath,
state.Request.PlaySessionId,
@@ -1206,7 +1206,7 @@ namespace MediaBrowser.Api.Playback
FileSystem.CreateDirectory(Path.GetDirectoryName(logFilePath));
// FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
- state.LogFileStream = FileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true);
+ state.LogFileStream = FileSystem.GetFileStream(logFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true);
var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(Request.AbsoluteUri + Environment.NewLine + Environment.NewLine + JsonSerializer.SerializeToString(state.MediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
await state.LogFileStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false);
@@ -1262,13 +1262,14 @@ namespace MediaBrowser.Api.Playback
{
if (EnableThrottling(state))
{
- transcodingJob.TranscodingThrottler = state.TranscodingThrottler = new TranscodingThrottler(transcodingJob, Logger, ServerConfigurationManager);
+ transcodingJob.TranscodingThrottler = state.TranscodingThrottler = new TranscodingThrottler(transcodingJob, Logger, ServerConfigurationManager, ApiEntryPoint.Instance.TimerFactory, FileSystem);
state.TranscodingThrottler.Start();
}
}
private bool EnableThrottling(StreamState state)
{
+ return false;
// do not use throttling with hardware encoders
return state.InputProtocol == MediaProtocol.File &&
state.RunTimeTicks.HasValue &&
@@ -1514,7 +1515,7 @@ namespace MediaBrowser.Api.Playback
/// <param name="process">The process.</param>
/// <param name="job">The job.</param>
/// <param name="state">The state.</param>
- private void OnFfMpegProcessExited(Process process, TranscodingJob job, StreamState state)
+ private void OnFfMpegProcessExited(IProcess process, TranscodingJob job, StreamState state)
{
if (job != null)
{
@@ -1758,6 +1759,13 @@ namespace MediaBrowser.Api.Playback
videoRequest.EnableSplittingOnNonKeyFrames = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
}
}
+ else if (i == 30)
+ {
+ if (videoRequest != null)
+ {
+ videoRequest.RequireAvc = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
+ }
+ }
}
}
@@ -2114,7 +2122,7 @@ namespace MediaBrowser.Api.Playback
if (string.Equals("h264", videoStream.Codec, StringComparison.OrdinalIgnoreCase))
{
- if (videoStream.IsAVC.HasValue && !videoStream.IsAVC.Value)
+ if (videoStream.IsAVC.HasValue && !videoStream.IsAVC.Value && request.RequireAvc)
{
Logger.Debug("Cannot stream copy video. Stream is marked as not AVC");
return false;
@@ -2296,11 +2304,7 @@ namespace MediaBrowser.Api.Playback
private void ApplyDeviceProfileSettings(StreamState state)
{
- var headers = new Dictionary<string, string>();
- foreach (var key in Request.Headers.AllKeys)
- {
- headers[key] = Request.Headers[key];
- }
+ var headers = Request.Headers.ToDictionary();
if (!string.IsNullOrWhiteSpace(state.Request.DeviceProfileId))
{
@@ -2406,97 +2410,98 @@ namespace MediaBrowser.Api.Playback
{
return Task.FromResult(true);
}
+ return Task.FromResult(true);
- var dict = new Dictionary<string, string>();
+ //var dict = new Dictionary<string, string>();
- var outputAudio = GetAudioEncoder(state);
- if (!string.IsNullOrWhiteSpace(outputAudio))
- {
- dict["outputAudio"] = outputAudio;
- }
+ //var outputAudio = GetAudioEncoder(state);
+ //if (!string.IsNullOrWhiteSpace(outputAudio))
+ //{
+ // dict["outputAudio"] = outputAudio;
+ //}
- var outputVideo = GetVideoEncoder(state);
- if (!string.IsNullOrWhiteSpace(outputVideo))
- {
- dict["outputVideo"] = outputVideo;
- }
+ //var outputVideo = GetVideoEncoder(state);
+ //if (!string.IsNullOrWhiteSpace(outputVideo))
+ //{
+ // dict["outputVideo"] = outputVideo;
+ //}
- if (ServerConfigurationManager.Configuration.CodecsUsed.Contains(outputAudio ?? string.Empty, StringComparer.OrdinalIgnoreCase) &&
- ServerConfigurationManager.Configuration.CodecsUsed.Contains(outputVideo ?? string.Empty, StringComparer.OrdinalIgnoreCase))
- {
- return Task.FromResult(true);
- }
+ //if (ServerConfigurationManager.Configuration.CodecsUsed.Contains(outputAudio ?? string.Empty, StringComparer.OrdinalIgnoreCase) &&
+ // ServerConfigurationManager.Configuration.CodecsUsed.Contains(outputVideo ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ //{
+ // return Task.FromResult(true);
+ //}
- dict["id"] = AppHost.SystemId;
- dict["type"] = state.VideoRequest == null ? "Audio" : "Video";
+ //dict["id"] = AppHost.SystemId;
+ //dict["type"] = state.VideoRequest == null ? "Audio" : "Video";
- var audioStream = state.AudioStream;
- if (audioStream != null && !string.IsNullOrWhiteSpace(audioStream.Codec))
- {
- dict["inputAudio"] = audioStream.Codec;
- }
-
- var videoStream = state.VideoStream;
- if (videoStream != null && !string.IsNullOrWhiteSpace(videoStream.Codec))
- {
- dict["inputVideo"] = videoStream.Codec;
- }
+ //var audioStream = state.AudioStream;
+ //if (audioStream != null && !string.IsNullOrWhiteSpace(audioStream.Codec))
+ //{
+ // dict["inputAudio"] = audioStream.Codec;
+ //}
- var cert = GetType().Assembly.GetModules().First().GetSignerCertificate();
- if (cert != null)
- {
- dict["assemblySig"] = cert.GetCertHashString();
- dict["certSubject"] = cert.Subject ?? string.Empty;
- dict["certIssuer"] = cert.Issuer ?? string.Empty;
- }
- else
- {
- return Task.FromResult(true);
- }
+ //var videoStream = state.VideoStream;
+ //if (videoStream != null && !string.IsNullOrWhiteSpace(videoStream.Codec))
+ //{
+ // dict["inputVideo"] = videoStream.Codec;
+ //}
- if (state.SupportedAudioCodecs.Count > 0)
- {
- dict["supportedAudioCodecs"] = string.Join(",", state.SupportedAudioCodecs.ToArray());
- }
+ //var cert = GetType().Assembly.GetModules().First().GetSignerCertificate();
+ //if (cert != null)
+ //{
+ // dict["assemblySig"] = cert.GetCertHashString();
+ // dict["certSubject"] = cert.Subject ?? string.Empty;
+ // dict["certIssuer"] = cert.Issuer ?? string.Empty;
+ //}
+ //else
+ //{
+ // return Task.FromResult(true);
+ //}
- var auth = AuthorizationContext.GetAuthorizationInfo(Request);
+ //if (state.SupportedAudioCodecs.Count > 0)
+ //{
+ // dict["supportedAudioCodecs"] = string.Join(",", state.SupportedAudioCodecs.ToArray());
+ //}
- dict["appName"] = auth.Client ?? string.Empty;
- dict["appVersion"] = auth.Version ?? string.Empty;
- dict["device"] = auth.Device ?? string.Empty;
- dict["deviceId"] = auth.DeviceId ?? string.Empty;
- dict["context"] = "streaming";
+ //var auth = AuthorizationContext.GetAuthorizationInfo(Request);
- //Logger.Info(JsonSerializer.SerializeToString(dict));
- if (!ServerConfigurationManager.Configuration.CodecsUsed.Contains(outputAudio ?? string.Empty, StringComparer.OrdinalIgnoreCase))
- {
- var list = ServerConfigurationManager.Configuration.CodecsUsed.ToList();
- list.Add(outputAudio);
- ServerConfigurationManager.Configuration.CodecsUsed = list.ToArray();
- }
+ //dict["appName"] = auth.Client ?? string.Empty;
+ //dict["appVersion"] = auth.Version ?? string.Empty;
+ //dict["device"] = auth.Device ?? string.Empty;
+ //dict["deviceId"] = auth.DeviceId ?? string.Empty;
+ //dict["context"] = "streaming";
- if (!ServerConfigurationManager.Configuration.CodecsUsed.Contains(outputVideo ?? string.Empty, StringComparer.OrdinalIgnoreCase))
- {
- var list = ServerConfigurationManager.Configuration.CodecsUsed.ToList();
- list.Add(outputVideo);
- ServerConfigurationManager.Configuration.CodecsUsed = list.ToArray();
- }
+ ////Logger.Info(JsonSerializer.SerializeToString(dict));
+ //if (!ServerConfigurationManager.Configuration.CodecsUsed.Contains(outputAudio ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ //{
+ // var list = ServerConfigurationManager.Configuration.CodecsUsed.ToList();
+ // list.Add(outputAudio);
+ // ServerConfigurationManager.Configuration.CodecsUsed = list.ToArray();
+ //}
- ServerConfigurationManager.SaveConfiguration();
+ //if (!ServerConfigurationManager.Configuration.CodecsUsed.Contains(outputVideo ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ //{
+ // var list = ServerConfigurationManager.Configuration.CodecsUsed.ToList();
+ // list.Add(outputVideo);
+ // ServerConfigurationManager.Configuration.CodecsUsed = list.ToArray();
+ //}
- //Logger.Info(JsonSerializer.SerializeToString(dict));
- var options = new HttpRequestOptions()
- {
- Url = "https://mb3admin.com/admin/service/transcoding/report",
- CancellationToken = CancellationToken.None,
- LogRequest = false,
- LogErrors = false,
- BufferContent = false
- };
- options.RequestContent = JsonSerializer.SerializeToString(dict);
- options.RequestContentType = "application/json";
+ //ServerConfigurationManager.SaveConfiguration();
- return HttpClient.Post(options);
+ ////Logger.Info(JsonSerializer.SerializeToString(dict));
+ //var options = new HttpRequestOptions()
+ //{
+ // Url = "https://mb3admin.com/admin/service/transcoding/report",
+ // CancellationToken = CancellationToken.None,
+ // LogRequest = false,
+ // LogErrors = false,
+ // BufferContent = false
+ //};
+ //options.RequestContent = JsonSerializer.SerializeToString(dict);
+ //options.RequestContentType = "application/json";
+
+ //return HttpClient.Post(options);
}
/// <summary>
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
index 319e4bbb68..84f3955185 100644
--- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
@@ -13,7 +13,10 @@ using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Api.Playback.Hls
{
@@ -22,11 +25,6 @@ namespace MediaBrowser.Api.Playback.Hls
/// </summary>
public abstract class BaseHlsService : BaseStreamingService
{
- protected BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer)
- : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer)
- {
- }
-
/// <summary>
/// Gets the audio arguments.
/// </summary>
@@ -145,7 +143,7 @@ namespace MediaBrowser.Api.Playback.Hls
private string GetLivePlaylistText(string path, int segmentLength)
{
- using (var stream = FileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
+ using (var stream = FileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite))
{
using (var reader = new StreamReader(stream))
{
@@ -155,7 +153,8 @@ namespace MediaBrowser.Api.Playback.Hls
var newDuration = "#EXT-X-TARGETDURATION:" + segmentLength.ToString(UsCulture);
- text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength + 1).ToString(UsCulture), newDuration, StringComparison.OrdinalIgnoreCase);
+ text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength - 1).ToString(UsCulture), newDuration, StringComparison.OrdinalIgnoreCase);
+ //text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength + 1).ToString(UsCulture), newDuration, StringComparison.OrdinalIgnoreCase);
return text;
}
@@ -187,7 +186,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
try
{
- // Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written
+ // Need to use FileShareMode.ReadWrite because we're reading the file at the same time it's being written
using (var fileStream = GetPlaylistFileStream(playlist))
{
using (var reader = new StreamReader(fileStream))
@@ -227,11 +226,11 @@ namespace MediaBrowser.Api.Playback.Hls
try
{
- return FileSystem.GetFileStream(tmpPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true);
+ return FileSystem.GetFileStream(tmpPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, true);
}
catch (IOException)
{
- return FileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true);
+ return FileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, true);
}
}
@@ -256,7 +255,10 @@ namespace MediaBrowser.Api.Playback.Hls
"hls/" + Path.GetFileNameWithoutExtension(outputPath));
}
- var args = string.Format("{0} {1} {2} -map_metadata -1 -threads {3} {4} {5} -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9}{10} -y \"{11}\"",
+ // add when stream copying?
+ // -avoid_negative_ts make_zero -fflags +genpts
+
+ var args = string.Format("{0} {1} {2} -map_metadata -1 -map_chapters -1 -threads {3} {4} {5} -fflags +genpts -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9}{10} -y \"{11}\"",
itsOffset,
inputModifier,
GetInputArgument(state),
@@ -274,6 +276,11 @@ namespace MediaBrowser.Api.Playback.Hls
return args;
}
+ protected override string GetDefaultH264Preset()
+ {
+ return "veryfast";
+ }
+
protected virtual int GetStartNumber(StreamState state)
{
return 0;
@@ -285,5 +292,9 @@ namespace MediaBrowser.Api.Playback.Hls
return isLiveStream;
}
+
+ public BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext)
+ {
+ }
}
} \ No newline at end of file
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index 97b386d73b..f1b5b4681c 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -10,7 +10,6 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -19,7 +18,10 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Services;
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
namespace MediaBrowser.Api.Playback.Hls
@@ -67,7 +69,6 @@ namespace MediaBrowser.Api.Playback.Hls
}
[Route("/Videos/{Id}/hls1/{PlaylistId}/{SegmentId}.ts", "GET")]
- [Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
public class GetHlsVideoSegment : VideoStreamRequest
{
public string PlaylistId { get; set; }
@@ -81,7 +82,6 @@ namespace MediaBrowser.Api.Playback.Hls
[Route("/Audio/{Id}/hls1/{PlaylistId}/{SegmentId}.aac", "GET")]
[Route("/Audio/{Id}/hls1/{PlaylistId}/{SegmentId}.ts", "GET")]
- [Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
public class GetHlsAudioSegment : StreamRequest
{
public string PlaylistId { get; set; }
@@ -95,8 +95,8 @@ namespace MediaBrowser.Api.Playback.Hls
public class DynamicHlsService : BaseHlsService
{
- public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, INetworkManager networkManager)
- : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer)
+
+ public DynamicHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, INetworkManager networkManager) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext)
{
NetworkManager = networkManager;
}
@@ -356,7 +356,8 @@ namespace MediaBrowser.Api.Playback.Hls
{
Logger.ErrorException("Error deleting partial stream file(s) {0}", ex, file.FullName);
- Thread.Sleep(100);
+ var task = Task.Delay(100);
+ Task.WaitAll(task);
DeleteFile(file, retryCount + 1);
}
catch (Exception ex)
@@ -378,7 +379,7 @@ namespace MediaBrowser.Api.Playback.Hls
.OrderByDescending(fileSystem.GetLastWriteTimeUtc)
.FirstOrDefault();
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
return null;
}
@@ -420,7 +421,7 @@ namespace MediaBrowser.Api.Playback.Hls
// If all transcoding has completed, just return immediately
if (transcodingJob != null && transcodingJob.HasExited && FileSystem.FileExists(segmentPath))
{
- return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
+ return await GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob).ConfigureAwait(false);
}
var segmentFilename = Path.GetFileName(segmentPath);
@@ -440,7 +441,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
if (FileSystem.FileExists(segmentPath))
{
- return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
+ return await GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob).ConfigureAwait(false);
}
//break;
}
@@ -456,17 +457,17 @@ namespace MediaBrowser.Api.Playback.Hls
}
cancellationToken.ThrowIfCancellationRequested();
- return GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob);
+ return await GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob).ConfigureAwait(false);
}
- private object GetSegmentResult(StreamState state, string segmentPath, int index, TranscodingJob transcodingJob)
+ private Task<object> GetSegmentResult(StreamState state, string segmentPath, int index, TranscodingJob transcodingJob)
{
var segmentEndingPositionTicks = GetEndPositionTicks(state, index);
return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
{
Path = segmentPath,
- FileShare = FileShare.ReadWrite,
+ FileShare = FileShareMode.ReadWrite,
OnComplete = () =>
{
if (transcodingJob != null)
@@ -475,7 +476,7 @@ namespace MediaBrowser.Api.Playback.Hls
ApiEntryPoint.Instance.OnTranscodeEndRequest(transcodingJob);
}
}
- }).Result;
+ });
}
private async Task<object> GetMasterPlaylistInternal(StreamRequest request, string method)
@@ -881,12 +882,13 @@ namespace MediaBrowser.Api.Playback.Hls
if (state.IsOutputVideo && !EnableCopyTs(state) && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) && (state.Request.StartTimeTicks ?? 0) > 0)
{
- timestampOffsetParam = " -output_ts_offset " + MediaEncoder.GetTimeParameter(state.Request.StartTimeTicks ?? 0).ToString(CultureInfo.InvariantCulture);
+ timestampOffsetParam = " -output_ts_offset " + MediaEncoder.GetTimeParameter(state.Request.StartTimeTicks ?? 0);
}
var mapArgs = state.IsOutputVideo ? GetMapArgs(state) : string.Empty;
var enableSplittingOnNonKeyFrames = state.VideoRequest.EnableSplittingOnNonKeyFrames && string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase);
enableSplittingOnNonKeyFrames = false;
+
// TODO: check libavformat version for 57 50.100 and use -hls_flags split_by_time
var hlsProtocolSupportsSplittingByTime = false;
@@ -894,7 +896,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state);
- return string.Format("{0} {10} {1} -map_metadata -1 -threads {2} {3} {4} {5} -f segment -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -segment_time {6} -break_non_keyframes 1 -segment_format mpegts -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"",
+ return string.Format("{0} {10} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f segment -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -segment_time {6} -break_non_keyframes 1 -segment_format mpegts -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"",
inputModifier,
GetInputArgument(state),
threads,
@@ -912,7 +914,7 @@ namespace MediaBrowser.Api.Playback.Hls
var splitByTime = hlsProtocolSupportsSplittingByTime && enableSplittingOnNonKeyFrames;
var splitByTimeArg = splitByTime ? " -hls_flags split_by_time" : "";
- return string.Format("{0}{12} {1} -map_metadata -1 -threads {2} {3} {4}{5} {6} -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -hls_time {7}{8} -start_number {9} -hls_list_size {10} -y \"{11}\"",
+ return string.Format("{0}{12} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4}{5} {6} -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -hls_time {7}{8} -start_number {9} -hls_list_size {10} -y \"{11}\"",
inputModifier,
GetInputArgument(state),
threads,
diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
index 976fed3f03..f3683c6cba 100644
--- a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
+++ b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
@@ -1,11 +1,13 @@
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Net;
-using ServiceStack;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Playback.Hls
{
@@ -14,7 +16,6 @@ namespace MediaBrowser.Api.Playback.Hls
/// </summary>
[Route("/Audio/{Id}/hls/{SegmentId}/stream.mp3", "GET")]
[Route("/Audio/{Id}/hls/{SegmentId}/stream.aac", "GET")]
- [Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
public class GetHlsAudioSegmentLegacy
{
// TODO: Deprecate with new iOS app
@@ -36,7 +37,6 @@ namespace MediaBrowser.Api.Playback.Hls
/// Class GetHlsVideoSegment
/// </summary>
[Route("/Videos/{Id}/hls/{PlaylistId}/stream.m3u8", "GET")]
- [Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
public class GetHlsPlaylistLegacy
{
// TODO: Deprecate with new iOS app
@@ -51,7 +51,6 @@ namespace MediaBrowser.Api.Playback.Hls
}
[Route("/Videos/ActiveEncodings", "DELETE")]
- [Api(Description = "Stops an encoding process")]
public class StopEncodingProcess
{
[ApiMember(Name = "DeviceId", Description = "The device id of the client requesting. Used to stop encoding processes when needed.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
@@ -65,7 +64,6 @@ namespace MediaBrowser.Api.Playback.Hls
/// Class GetHlsVideoSegment
/// </summary>
[Route("/Videos/{Id}/hls/{PlaylistId}/{SegmentId}.ts", "GET")]
- [Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
public class GetHlsVideoSegmentLegacy : VideoStreamRequest
{
public string PlaylistId { get; set; }
@@ -81,11 +79,13 @@ namespace MediaBrowser.Api.Playback.Hls
{
private readonly IServerApplicationPaths _appPaths;
private readonly IServerConfigurationManager _config;
+ private readonly IFileSystem _fileSystem;
- public HlsSegmentService(IServerApplicationPaths appPaths, IServerConfigurationManager config)
+ public HlsSegmentService(IServerApplicationPaths appPaths, IServerConfigurationManager config, IFileSystem fileSystem)
{
_appPaths = appPaths;
_config = config;
+ _fileSystem = fileSystem;
}
public Task<object> Get(GetHlsPlaylistLegacy request)
@@ -113,7 +113,7 @@ namespace MediaBrowser.Api.Playback.Hls
var normalizedPlaylistId = request.PlaylistId;
- var playlistPath = Directory.EnumerateFiles(_config.ApplicationPaths.TranscodingTempPath, "*")
+ var playlistPath = _fileSystem.GetFilePaths(_config.ApplicationPaths.TranscodingTempPath)
.FirstOrDefault(i => string.Equals(Path.GetExtension(i), ".m3u8", StringComparison.OrdinalIgnoreCase) && i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1);
return GetFileResult(file, playlistPath);
@@ -124,13 +124,13 @@ namespace MediaBrowser.Api.Playback.Hls
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
- public object Get(GetHlsAudioSegmentLegacy request)
+ public Task<object> Get(GetHlsAudioSegmentLegacy request)
{
// TODO: Deprecate with new iOS app
var file = request.SegmentId + Path.GetExtension(Request.PathInfo);
file = Path.Combine(_appPaths.TranscodingTempPath, file);
- return ResultFactory.GetStaticFileResult(Request, file, FileShare.ReadWrite).Result;
+ return ResultFactory.GetStaticFileResult(Request, file, FileShareMode.ReadWrite);
}
private Task<object> GetFileResult(string path, string playlistPath)
@@ -140,7 +140,7 @@ namespace MediaBrowser.Api.Playback.Hls
return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
{
Path = path,
- FileShare = FileShare.ReadWrite,
+ FileShare = FileShareMode.ReadWrite,
OnComplete = () =>
{
if (transcodingJob != null)
diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
index c7258d72f7..67edd3f003 100644
--- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
@@ -5,15 +5,17 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
-using ServiceStack;
using System;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Playback.Hls
{
[Route("/Videos/{Id}/live.m3u8", "GET")]
- [Api(Description = "Gets a video stream using HTTP live streaming.")]
public class GetLiveHlsStream : VideoStreamRequest
{
}
@@ -23,10 +25,6 @@ namespace MediaBrowser.Api.Playback.Hls
/// </summary>
public class VideoHlsService : BaseHlsService
{
- public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer)
- {
- }
-
public object Get(GetLiveHlsStream request)
{
return ProcessRequest(request, true);
@@ -91,6 +89,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
args += " -bsf:v h264_mp4toannexb";
}
+ args += " -flags -global_header";
return args;
}
@@ -127,5 +126,9 @@ namespace MediaBrowser.Api.Playback.Hls
{
return ".ts";
}
+
+ public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext)
+ {
+ }
}
}
diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs
index 0611adea5f..8fb78b6e5d 100644
--- a/MediaBrowser.Api/Playback/MediaInfoService.cs
+++ b/MediaBrowser.Api/Playback/MediaInfoService.cs
@@ -9,7 +9,6 @@ using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Session;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -18,6 +17,7 @@ using System.Threading.Tasks;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Playback
{
@@ -72,8 +72,9 @@ namespace MediaBrowser.Api.Playback
private readonly IMediaEncoder _mediaEncoder;
private readonly IUserManager _userManager;
private readonly IJsonSerializer _json;
+ private readonly IAuthorizationContext _authContext;
- public MediaInfoService(IMediaSourceManager mediaSourceManager, IDeviceManager deviceManager, ILibraryManager libraryManager, IServerConfigurationManager config, INetworkManager networkManager, IMediaEncoder mediaEncoder, IUserManager userManager, IJsonSerializer json)
+ public MediaInfoService(IMediaSourceManager mediaSourceManager, IDeviceManager deviceManager, ILibraryManager libraryManager, IServerConfigurationManager config, INetworkManager networkManager, IMediaEncoder mediaEncoder, IUserManager userManager, IJsonSerializer json, IAuthorizationContext authContext)
{
_mediaSourceManager = mediaSourceManager;
_deviceManager = deviceManager;
@@ -83,6 +84,7 @@ namespace MediaBrowser.Api.Playback
_mediaEncoder = mediaEncoder;
_userManager = userManager;
_json = json;
+ _authContext = authContext;
}
public object Get(GetBitrateTestBytes request)
@@ -105,7 +107,7 @@ namespace MediaBrowser.Api.Playback
public async Task<object> Post(OpenMediaSource request)
{
- var authInfo = AuthorizationContext.GetAuthorizationInfo(Request);
+ var authInfo = _authContext.GetAuthorizationInfo(Request);
var result = await _mediaSourceManager.OpenLiveStream(request, true, CancellationToken.None).ConfigureAwait(false);
@@ -146,7 +148,7 @@ namespace MediaBrowser.Api.Playback
public async Task<object> Post(GetPostedPlaybackInfo request)
{
- var authInfo = AuthorizationContext.GetAuthorizationInfo(Request);
+ var authInfo = _authContext.GetAuthorizationInfo(Request);
var profile = request.DeviceProfile;
diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs
index e828a53c92..082d6b2f40 100644
--- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs
@@ -7,10 +7,13 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
-using ServiceStack;
using System.Collections.Generic;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Playback.Progressive
{
@@ -32,10 +35,6 @@ namespace MediaBrowser.Api.Playback.Progressive
/// </summary>
public class AudioService : BaseProgressiveStreamingService
{
- public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, imageProcessor, httpClient)
- {
- }
-
/// <summary>
/// Gets the specified request.
/// </summary>
@@ -95,5 +94,9 @@ namespace MediaBrowser.Api.Playback.Progressive
string.Join(" ", audioTranscodeParams.ToArray()),
outputPath).Trim();
}
+
+ public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext, imageProcessor)
+ {
+ }
}
}
diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
index 809eabef8f..23a84e4809 100644
--- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
@@ -9,15 +9,16 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Serialization;
-using ServiceStack.Web;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
-using ServiceStack;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Playback.Progressive
{
@@ -28,7 +29,7 @@ namespace MediaBrowser.Api.Playback.Progressive
{
protected readonly IImageProcessor ImageProcessor;
- protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer)
+ public BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext)
{
ImageProcessor = imageProcessor;
}
@@ -132,11 +133,10 @@ namespace MediaBrowser.Api.Playback.Progressive
// TODO: Don't hardcode this
outputHeaders["Content-Type"] = MediaBrowser.Model.Net.MimeTypes.GetMimeType("file.ts");
- var streamSource = new ProgressiveFileCopier(state.DirectStreamProvider, outputHeaders, null, Logger, CancellationToken.None)
+ return new ProgressiveFileCopier(state.DirectStreamProvider, outputHeaders, null, Logger, CancellationToken.None)
{
AllowEndOfFile = false
};
- return ResultFactory.GetAsyncStreamWriter(streamSource);
}
}
@@ -177,11 +177,10 @@ namespace MediaBrowser.Api.Playback.Progressive
outputHeaders["Content-Type"] = contentType;
- var streamSource = new ProgressiveFileCopier(FileSystem, state.MediaPath, outputHeaders, null, Logger, CancellationToken.None)
+ return new ProgressiveFileCopier(FileSystem, state.MediaPath, outputHeaders, null, Logger, CancellationToken.None)
{
AllowEndOfFile = false
};
- return ResultFactory.GetAsyncStreamWriter(streamSource);
}
TimeSpan? cacheDuration = null;
@@ -221,7 +220,7 @@ namespace MediaBrowser.Api.Playback.Progressive
// ContentType = contentType,
// IsHeadRequest = isHeadRequest,
// Path = outputPath,
- // FileShare = FileShare.ReadWrite,
+ // FileShare = FileShareMode.ReadWrite,
// OnComplete = () =>
// {
// if (transcodingJob != null)
@@ -314,12 +313,12 @@ namespace MediaBrowser.Api.Playback.Progressive
var result = new StaticRemoteStreamWriter(response);
- result.Options["Content-Type"] = response.ContentType;
+ result.Headers["Content-Type"] = response.ContentType;
// Add the response headers to the result object
foreach (var header in responseHeaders)
{
- result.Options[header.Key] = header.Value;
+ result.Headers[header.Key] = header.Value;
}
return result;
@@ -356,18 +355,18 @@ namespace MediaBrowser.Api.Playback.Progressive
{
var streamResult = ResultFactory.GetResult(new byte[] { }, contentType, responseHeaders);
- var hasOptions = streamResult as IHasOptions;
- if (hasOptions != null)
+ var hasHeaders = streamResult as IHasHeaders;
+ if (hasHeaders != null)
{
if (contentLength.HasValue)
{
- hasOptions.Options["Content-Length"] = contentLength.Value.ToString(CultureInfo.InvariantCulture);
+ hasHeaders.Headers["Content-Length"] = contentLength.Value.ToString(CultureInfo.InvariantCulture);
}
else
{
- if (hasOptions.Options.ContainsKey("Content-Length"))
+ if (hasHeaders.Headers.ContainsKey("Content-Length"))
{
- hasOptions.Options.Remove("Content-Length");
+ hasHeaders.Headers.Remove("Content-Length");
}
}
}
@@ -401,9 +400,7 @@ namespace MediaBrowser.Api.Playback.Progressive
outputHeaders[item.Key] = item.Value;
}
- var streamSource = new ProgressiveFileCopier(FileSystem, outputPath, outputHeaders, job, Logger, CancellationToken.None);
-
- return ResultFactory.GetAsyncStreamWriter(streamSource);
+ return new ProgressiveFileCopier(FileSystem, outputPath, outputHeaders, job, Logger, CancellationToken.None);
}
finally
{
diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
index 3477ad57bd..a33fbcbcfd 100644
--- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
+++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
@@ -3,15 +3,17 @@ using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Net;
using System.Collections.Generic;
-using ServiceStack.Web;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Playback.Progressive
{
- public class ProgressiveFileCopier : IAsyncStreamSource, IHasOptions
+ public class ProgressiveFileCopier : IAsyncStreamWriter, IHasHeaders
{
private readonly IFileSystem _fileSystem;
private readonly TranscodingJob _job;
@@ -27,7 +29,7 @@ namespace MediaBrowser.Api.Playback.Progressive
public long StartPosition { get; set; }
public bool AllowEndOfFile = true;
- private IDirectStreamProvider _directStreamProvider;
+ private readonly IDirectStreamProvider _directStreamProvider;
public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary<string, string> outputHeaders, TranscodingJob job, ILogger logger, CancellationToken cancellationToken)
{
@@ -48,7 +50,7 @@ namespace MediaBrowser.Api.Playback.Progressive
_cancellationToken = cancellationToken;
}
- public IDictionary<string, string> Options
+ public IDictionary<string, string> Headers
{
get
{
@@ -58,10 +60,10 @@ namespace MediaBrowser.Api.Playback.Progressive
private Stream GetInputStream()
{
- return _fileSystem.GetFileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true);
+ return _fileSystem.GetFileStream(_path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, true);
}
- public async Task WriteToAsync(Stream outputStream)
+ public async Task WriteToAsync(Stream outputStream, CancellationToken cancellationToken)
{
try
{
diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
index 21e8845f5f..e29aa7d5aa 100644
--- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
@@ -7,12 +7,15 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
-using ServiceStack;
using System;
using System.IO;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Playback.Progressive
{
@@ -56,7 +59,6 @@ namespace MediaBrowser.Api.Playback.Progressive
[Route("/Videos/{Id}/stream.mov", "HEAD")]
[Route("/Videos/{Id}/stream.iso", "HEAD")]
[Route("/Videos/{Id}/stream", "HEAD")]
- [Api(Description = "Gets a video stream")]
public class GetVideoStream : VideoStreamRequest
{
@@ -67,8 +69,7 @@ namespace MediaBrowser.Api.Playback.Progressive
/// </summary>
public class VideoService : BaseProgressiveStreamingService
{
- public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IImageProcessor imageProcessor, IHttpClient httpClient)
- : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, imageProcessor, httpClient)
+ public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext, imageProcessor)
{
}
@@ -110,7 +111,7 @@ namespace MediaBrowser.Api.Playback.Progressive
var inputModifier = GetInputModifier(state);
- return string.Format("{0} {1}{2} {3} {4} -map_metadata -1 -threads {5} {6}{7} -y \"{8}\"",
+ return string.Format("{0} {1}{2} {3} {4} -map_metadata -1 -map_chapters -1 -threads {5} {6}{7} -y \"{8}\"",
inputModifier,
GetInputArgument(state),
keyFrame,
@@ -150,6 +151,11 @@ namespace MediaBrowser.Api.Playback.Progressive
args += " -copyts -avoid_negative_ts disabled -start_at_zero";
}
+ if (!state.RunTimeTicks.HasValue)
+ {
+ args += " -fflags +genpts -flags +global_header";
+ }
+
return args;
}
@@ -191,6 +197,11 @@ namespace MediaBrowser.Api.Playback.Progressive
args += GetGraphicalSubtitleParam(state, videoCodec);
}
+ if (!state.RunTimeTicks.HasValue)
+ {
+ args += " -flags -global_header";
+ }
+
return args;
}
diff --git a/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs b/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs
index fc94d070a0..6bb3b6b804 100644
--- a/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs
+++ b/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs
@@ -1,14 +1,16 @@
using MediaBrowser.Common.Net;
-using ServiceStack.Web;
using System.Collections.Generic;
using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Playback
{
/// <summary>
/// Class StaticRemoteStreamWriter
/// </summary>
- public class StaticRemoteStreamWriter : IStreamWriter, IHasOptions
+ public class StaticRemoteStreamWriter : IAsyncStreamWriter, IHasHeaders
{
/// <summary>
/// The _input stream
@@ -29,20 +31,16 @@ namespace MediaBrowser.Api.Playback
/// Gets the options.
/// </summary>
/// <value>The options.</value>
- public IDictionary<string, string> Options
+ public IDictionary<string, string> Headers
{
get { return _options; }
}
- /// <summary>
- /// Writes to.
- /// </summary>
- /// <param name="responseStream">The response stream.</param>
- public void WriteTo(Stream responseStream)
+ public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken)
{
using (_response)
{
- _response.Content.CopyTo(responseStream, 819200);
+ await _response.Content.CopyToAsync(responseStream, 81920, cancellationToken).ConfigureAwait(false);
}
}
}
diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs
index 5b936f7182..50c7797650 100644
--- a/MediaBrowser.Api/Playback/StreamRequest.cs
+++ b/MediaBrowser.Api/Playback/StreamRequest.cs
@@ -1,5 +1,5 @@
using MediaBrowser.Model.Dlna;
-using ServiceStack;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Playback
{
@@ -195,6 +195,7 @@ namespace MediaBrowser.Api.Playback
public bool EnableSubtitlesInManifest { get; set; }
public bool EnableSplittingOnNonKeyFrames { get; set; }
+ public bool RequireAvc { get; set; }
public VideoStreamRequest()
{
diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs
index 003599390e..d1ef996aea 100644
--- a/MediaBrowser.Api/Playback/StreamState.cs
+++ b/MediaBrowser.Api/Playback/StreamState.cs
@@ -11,6 +11,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
+using System.Linq;
using System.Threading;
namespace MediaBrowser.Api.Playback
diff --git a/MediaBrowser.Api/Playback/TranscodingThrottler.cs b/MediaBrowser.Api/Playback/TranscodingThrottler.cs
index a7d53cd447..c42d0c3e4c 100644
--- a/MediaBrowser.Api/Playback/TranscodingThrottler.cs
+++ b/MediaBrowser.Api/Playback/TranscodingThrottler.cs
@@ -2,8 +2,8 @@
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Logging;
using System;
-using System.IO;
-using System.Threading;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Threading;
namespace MediaBrowser.Api.Playback
{
@@ -11,15 +11,19 @@ namespace MediaBrowser.Api.Playback
{
private readonly TranscodingJob _job;
private readonly ILogger _logger;
- private Timer _timer;
+ private ITimer _timer;
private bool _isPaused;
private readonly IConfigurationManager _config;
+ private readonly ITimerFactory _timerFactory;
+ private readonly IFileSystem _fileSystem;
- public TranscodingThrottler(TranscodingJob job, ILogger logger, IConfigurationManager config)
+ public TranscodingThrottler(TranscodingJob job, ILogger logger, IConfigurationManager config, ITimerFactory timerFactory, IFileSystem fileSystem)
{
_job = job;
_logger = logger;
_config = config;
+ _timerFactory = timerFactory;
+ _fileSystem = fileSystem;
}
private EncodingOptions GetOptions()
@@ -29,7 +33,7 @@ namespace MediaBrowser.Api.Playback
public void Start()
{
- _timer = new Timer(TimerCallback, null, 5000, 5000);
+ _timer = _timerFactory.Create(TimerCallback, null, 5000, 5000);
}
private void TimerCallback(object state)
@@ -120,7 +124,7 @@ namespace MediaBrowser.Api.Playback
try
{
- var bytesTranscoded = job.BytesTranscoded ?? new FileInfo(path).Length;
+ var bytesTranscoded = job.BytesTranscoded ?? _fileSystem.GetFileInfo(path).Length;
// Estimate the bytes the transcoder should be ahead
double gapFactor = gapLengthInTicks;
diff --git a/MediaBrowser.Api/PlaylistService.cs b/MediaBrowser.Api/PlaylistService.cs
index 9693992882..bb2bc449b0 100644
--- a/MediaBrowser.Api/PlaylistService.cs
+++ b/MediaBrowser.Api/PlaylistService.cs
@@ -5,9 +5,9 @@ using MediaBrowser.Controller.Playlists;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Playlists;
using MediaBrowser.Model.Querying;
-using ServiceStack;
using System.Linq;
using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
@@ -125,13 +125,15 @@ namespace MediaBrowser.Api
private readonly IDtoService _dtoService;
private readonly IUserManager _userManager;
private readonly ILibraryManager _libraryManager;
+ private readonly IAuthorizationContext _authContext;
- public PlaylistService(IDtoService dtoService, IPlaylistManager playlistManager, IUserManager userManager, ILibraryManager libraryManager)
+ public PlaylistService(IDtoService dtoService, IPlaylistManager playlistManager, IUserManager userManager, ILibraryManager libraryManager, IAuthorizationContext authContext)
{
_dtoService = dtoService;
_playlistManager = playlistManager;
_userManager = userManager;
_libraryManager = libraryManager;
+ _authContext = authContext;
}
public void Post(MoveItem request)
@@ -188,7 +190,7 @@ namespace MediaBrowser.Api
items = items.Take(request.Limit.Value).ToArray();
}
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
var dtos = (await _dtoService.GetBaseItemDtos(items.Select(i => i.Item2), dtoOptions, user).ConfigureAwait(false))
.ToArray();
diff --git a/MediaBrowser.Api/PluginService.cs b/MediaBrowser.Api/PluginService.cs
index 7ad69fd041..eb95224b79 100644
--- a/MediaBrowser.Api/PluginService.cs
+++ b/MediaBrowser.Api/PluginService.cs
@@ -8,14 +8,13 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Registration;
using MediaBrowser.Model.Serialization;
-using ServiceStack;
-using ServiceStack.Web;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
diff --git a/MediaBrowser.Api/Reports/Common/ReportViewType.cs b/MediaBrowser.Api/Reports/Common/ReportViewType.cs
index a5ffc7085f..8593c8e843 100644
--- a/MediaBrowser.Api/Reports/Common/ReportViewType.cs
+++ b/MediaBrowser.Api/Reports/Common/ReportViewType.cs
@@ -3,7 +3,6 @@ namespace MediaBrowser.Api.Reports
public enum ReportViewType
{
ReportData,
- ReportStatistics,
ReportActivities
}
diff --git a/MediaBrowser.Api/Reports/ReportRequests.cs b/MediaBrowser.Api/Reports/ReportRequests.cs
index 36f4bb3794..3627956588 100644
--- a/MediaBrowser.Api/Reports/ReportRequests.cs
+++ b/MediaBrowser.Api/Reports/ReportRequests.cs
@@ -1,6 +1,6 @@
using MediaBrowser.Api.UserLibrary;
-using ServiceStack;
using System.Collections.Generic;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Reports
{
@@ -60,7 +60,7 @@ namespace MediaBrowser.Api.Reports
{
/// <summary> Gets or sets the report view. </summary>
/// <value> The report view. </value>
- [ApiMember(Name = "ReportView", Description = "The report view. Values (ReportData, ReportStatistics, ReportActivities)", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
+ [ApiMember(Name = "ReportView", Description = "The report view. Values (ReportData, ReportActivities)", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ReportView { get; set; }
/// <summary> Gets or sets the report view. </summary>
@@ -101,7 +101,7 @@ namespace MediaBrowser.Api.Reports
{
/// <summary> Gets or sets the report view. </summary>
/// <value> The report view. </value>
- [ApiMember(Name = "ReportView", Description = "The report view. Values (ReportData, ReportStatistics, ReportActivities)", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
+ [ApiMember(Name = "ReportView", Description = "The report view. Values (ReportData, ReportActivities)", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ReportView { get; set; }
/// <summary> Gets or sets the report view. </summary>
@@ -120,13 +120,6 @@ namespace MediaBrowser.Api.Reports
public string ReportColumns { get; set; }
}
- [Route("/Reports/Statistics", "GET", Summary = "Gets reports statistics based on library items")]
- public class GetReportStatistics : BaseReportRequest, IReturn<ReportStatResult>
- {
- public int? TopItems { get; set; }
-
- }
-
[Route("/Reports/Items/Download", "GET", Summary = "Downloads report")]
public class GetReportDownload : BaseReportRequest, IReportsDownload
{
@@ -150,7 +143,7 @@ namespace MediaBrowser.Api.Reports
{
/// <summary> Gets or sets the report view. </summary>
/// <value> The report view. </value>
- [ApiMember(Name = "ReportView", Description = "The report view. Values (ReportData, ReportStatistics, ReportActivities)", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
+ [ApiMember(Name = "ReportView", Description = "The report view. Values (ReportData, ReportActivities)", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ReportView { get; set; }
/// <summary> Gets or sets the report view. </summary>
diff --git a/MediaBrowser.Api/Reports/ReportsService.cs b/MediaBrowser.Api/Reports/ReportsService.cs
index cb03d93822..5c4d21d66b 100644
--- a/MediaBrowser.Api/Reports/ReportsService.cs
+++ b/MediaBrowser.Api/Reports/ReportsService.cs
@@ -5,11 +5,10 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using System.Globalization;
using System.Linq;
-using MediaBrowser.Controller.Localization;
using System;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Activity;
-using MediaBrowser.Controller.Activity;
+using MediaBrowser.Model.Globalization;
namespace MediaBrowser.Api.Reports
{
@@ -83,8 +82,6 @@ namespace MediaBrowser.Api.Reports
ReportBuilder dataBuilder = new ReportBuilder(_libraryManager);
result = dataBuilder.GetHeaders(request);
break;
- case ReportViewType.ReportStatistics:
- break;
case ReportViewType.ReportActivities:
ReportActivitiesBuilder activityBuilder = new ReportActivitiesBuilder(_libraryManager, _userManager);
result = activityBuilder.GetHeaders(request);
@@ -113,20 +110,6 @@ namespace MediaBrowser.Api.Reports
/// <summary> Gets the given request. </summary>
/// <param name="request"> The request. </param>
/// <returns> A Task&lt;object&gt; </returns>
- public async Task<object> Get(GetReportStatistics request)
- {
- if (string.IsNullOrEmpty(request.IncludeItemTypes))
- return null;
- request.DisplayType = "Screen";
- var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
- var reportResult = await GetReportStatistic(request, user);
-
- return ToOptimizedResult(reportResult);
- }
-
- /// <summary> Gets the given request. </summary>
- /// <param name="request"> The request. </param>
- /// <returns> A Task&lt;object&gt; </returns>
public async Task<object> Get(GetReportDownload request)
{
if (string.IsNullOrEmpty(request.IncludeItemTypes))
@@ -156,7 +139,6 @@ namespace MediaBrowser.Api.Reports
ReportResult result = null;
switch (reportViewType)
{
- case ReportViewType.ReportStatistics:
case ReportViewType.ReportData:
ReportIncludeItemTypes reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes);
ReportBuilder dataBuilder = new ReportBuilder(_libraryManager);
@@ -464,20 +446,6 @@ namespace MediaBrowser.Api.Reports
return reportResult;
}
- /// <summary> Gets report statistic. </summary>
- /// <param name="request"> The request. </param>
- /// <returns> The report statistic. </returns>
- private async Task<ReportStatResult> GetReportStatistic(GetReportStatistics request, User user)
- {
- ReportIncludeItemTypes reportRowType = ReportHelper.GetRowType(request.IncludeItemTypes);
- QueryResult<BaseItem> queryResult = await GetQueryResult(request, user).ConfigureAwait(false);
-
- ReportStatBuilder reportBuilder = new ReportStatBuilder(_libraryManager);
- ReportStatResult reportResult = reportBuilder.GetResult(queryResult.Items, ReportHelper.GetRowType(request.IncludeItemTypes), request.TopItems ?? 5);
- reportResult.TotalRecordCount = reportResult.Groups.Count();
- return reportResult;
- }
-
#endregion
}
diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs
index d2da2ee846..e8ad9ea954 100644
--- a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs
+++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs
@@ -1,12 +1,11 @@
using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Tasks;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.ScheduledTasks
{
@@ -198,17 +197,13 @@ namespace MediaBrowser.Api.ScheduledTasks
throw new ResourceNotFoundException("Task not found");
}
- var hasKey = task.ScheduledTask as IHasKey;
- if (hasKey != null)
+ if (string.Equals(task.ScheduledTask.Key, "SystemUpdateTask", StringComparison.OrdinalIgnoreCase))
{
- if (string.Equals(hasKey.Key, "SystemUpdateTask", StringComparison.OrdinalIgnoreCase))
+ // This is a hack for now just to get the update application function to work when auto-update is disabled
+ if (!_config.Configuration.EnableAutoUpdate)
{
- // This is a hack for now just to get the update application function to work when auto-update is disabled
- if (!_config.Configuration.EnableAutoUpdate)
- {
- _config.Configuration.EnableAutoUpdate = true;
- _config.SaveConfiguration();
- }
+ _config.Configuration.EnableAutoUpdate = true;
+ _config.SaveConfiguration();
}
}
@@ -252,7 +247,7 @@ namespace MediaBrowser.Api.ScheduledTasks
var triggerInfos = request;
- task.Triggers = triggerInfos.Select(ScheduledTaskHelpers.GetTrigger);
+ task.Triggers = triggerInfos.ToArray();
}
}
}
diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs
index 5d3465c580..ee74ec450c 100644
--- a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs
+++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs
@@ -1,11 +1,10 @@
-using MediaBrowser.Common.ScheduledTasks;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Events;
+using MediaBrowser.Model.Events;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Tasks;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using MediaBrowser.Model.Threading;
namespace MediaBrowser.Api.ScheduledTasks
{
@@ -32,10 +31,8 @@ namespace MediaBrowser.Api.ScheduledTasks
/// <summary>
/// Initializes a new instance of the <see cref="ScheduledTasksWebSocketListener" /> class.
/// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="taskManager">The task manager.</param>
- public ScheduledTasksWebSocketListener(ILogger logger, ITaskManager taskManager)
- : base(logger)
+ public ScheduledTasksWebSocketListener(ILogger logger, ITaskManager taskManager, ITimerFactory timerFactory)
+ : base(logger, timerFactory)
{
TaskManager = taskManager;
@@ -85,7 +82,7 @@ namespace MediaBrowser.Api.ScheduledTasks
{
TaskManager.TaskExecuting -= TaskManager_TaskExecuting;
TaskManager.TaskCompleted -= TaskManager_TaskCompleted;
-
+
base.Dispose(dispose);
}
}
diff --git a/MediaBrowser.Api/SearchService.cs b/MediaBrowser.Api/SearchService.cs
index 5f6767a358..68fa7f92a7 100644
--- a/MediaBrowser.Api/SearchService.cs
+++ b/MediaBrowser.Api/SearchService.cs
@@ -7,9 +7,9 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Search;
-using ServiceStack;
using System.Linq;
using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
diff --git a/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs b/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs
index 8f68569b7a..b90a718529 100644
--- a/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs
+++ b/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs
@@ -6,6 +6,7 @@ using MediaBrowser.Model.Session;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using MediaBrowser.Model.Threading;
namespace MediaBrowser.Api.Session
{
@@ -31,10 +32,8 @@ namespace MediaBrowser.Api.Session
/// <summary>
/// Initializes a new instance of the <see cref="SessionInfoWebSocketListener"/> class.
/// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="sessionManager">The session manager.</param>
- public SessionInfoWebSocketListener(ILogger logger, ISessionManager sessionManager)
- : base(logger)
+ public SessionInfoWebSocketListener(ILogger logger, ISessionManager sessionManager, ITimerFactory timerFactory)
+ : base(logger, timerFactory)
{
_sessionManager = sessionManager;
diff --git a/MediaBrowser.Api/Session/SessionsService.cs b/MediaBrowser.Api/Session/SessionsService.cs
index 416c4fff93..9c107bdff0 100644
--- a/MediaBrowser.Api/Session/SessionsService.cs
+++ b/MediaBrowser.Api/Session/SessionsService.cs
@@ -4,12 +4,12 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Session;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Session
{
@@ -237,9 +237,6 @@ namespace MediaBrowser.Api.Session
[ApiMember(Name = "SupportsMediaControl", Description = "Determines whether media can be played remotely.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
public bool SupportsMediaControl { get; set; }
- [ApiMember(Name = "SupportsContentUploading", Description = "Determines whether camera upload is supported.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
- public bool SupportsContentUploading { get; set; }
-
[ApiMember(Name = "SupportsSync", Description = "Determines whether sync is supported.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
public bool SupportsSync { get; set; }
@@ -306,6 +303,7 @@ namespace MediaBrowser.Api.Session
private readonly IAuthorizationContext _authContext;
private readonly IAuthenticationRepository _authRepo;
private readonly IDeviceManager _deviceManager;
+ private readonly ISessionContext _sessionContext;
/// <summary>
/// Initializes a new instance of the <see cref="SessionsService" /> class.
@@ -314,13 +312,14 @@ namespace MediaBrowser.Api.Session
/// <param name="userManager">The user manager.</param>
/// <param name="authContext">The authentication context.</param>
/// <param name="authRepo">The authentication repo.</param>
- public SessionsService(ISessionManager sessionManager, IUserManager userManager, IAuthorizationContext authContext, IAuthenticationRepository authRepo, IDeviceManager deviceManager)
+ public SessionsService(ISessionManager sessionManager, IUserManager userManager, IAuthorizationContext authContext, IAuthenticationRepository authRepo, IDeviceManager deviceManager, ISessionContext sessionContext)
{
_sessionManager = sessionManager;
_userManager = userManager;
_authContext = authContext;
_authRepo = authRepo;
_deviceManager = deviceManager;
+ _sessionContext = sessionContext;
}
public void Delete(RevokeKey request)
@@ -419,7 +418,7 @@ namespace MediaBrowser.Api.Session
SeekPositionTicks = request.SeekPositionTicks
};
- var task = _sessionManager.SendPlaystateCommand(GetSession().Result.Id, request.Id, command, CancellationToken.None);
+ var task = _sessionManager.SendPlaystateCommand(GetSession(_sessionContext).Result.Id, request.Id, command, CancellationToken.None);
Task.WaitAll(task);
}
@@ -437,7 +436,7 @@ namespace MediaBrowser.Api.Session
ItemType = request.ItemType
};
- var task = _sessionManager.SendBrowseCommand(GetSession().Result.Id, request.Id, command, CancellationToken.None);
+ var task = _sessionManager.SendBrowseCommand(GetSession(_sessionContext).Result.Id, request.Id, command, CancellationToken.None);
Task.WaitAll(task);
}
@@ -456,7 +455,7 @@ namespace MediaBrowser.Api.Session
name = commandType.ToString();
}
- var currentSession = GetSession().Result;
+ var currentSession = GetSession(_sessionContext).Result;
var command = new GeneralCommand
{
@@ -482,7 +481,7 @@ namespace MediaBrowser.Api.Session
Text = request.Text
};
- var task = _sessionManager.SendMessageCommand(GetSession().Result.Id, request.Id, command, CancellationToken.None);
+ var task = _sessionManager.SendMessageCommand(GetSession(_sessionContext).Result.Id, request.Id, command, CancellationToken.None);
Task.WaitAll(task);
}
@@ -501,14 +500,14 @@ namespace MediaBrowser.Api.Session
StartPositionTicks = request.StartPositionTicks
};
- var task = _sessionManager.SendPlayCommand(GetSession().Result.Id, request.Id, command, CancellationToken.None);
+ var task = _sessionManager.SendPlayCommand(GetSession(_sessionContext).Result.Id, request.Id, command, CancellationToken.None);
Task.WaitAll(task);
}
public void Post(SendGeneralCommand request)
{
- var currentSession = GetSession().Result;
+ var currentSession = GetSession(_sessionContext).Result;
var command = new GeneralCommand
{
@@ -523,7 +522,7 @@ namespace MediaBrowser.Api.Session
public void Post(SendFullGeneralCommand request)
{
- var currentSession = GetSession().Result;
+ var currentSession = GetSession(_sessionContext).Result;
request.ControllingUserId = currentSession.UserId.HasValue ? currentSession.UserId.Value.ToString("N") : null;
@@ -546,7 +545,7 @@ namespace MediaBrowser.Api.Session
{
if (string.IsNullOrWhiteSpace(request.Id))
{
- request.Id = GetSession().Result.Id;
+ request.Id = GetSession(_sessionContext).Result.Id;
}
_sessionManager.ReportCapabilities(request.Id, new ClientCapabilities
{
@@ -558,8 +557,6 @@ namespace MediaBrowser.Api.Session
MessageCallbackUrl = request.MessageCallbackUrl,
- SupportsContentUploading = request.SupportsContentUploading,
-
SupportsSync = request.SupportsSync,
SupportsPersistentIdentifier = request.SupportsPersistentIdentifier
@@ -570,7 +567,7 @@ namespace MediaBrowser.Api.Session
{
if (string.IsNullOrWhiteSpace(request.Id))
{
- request.Id = GetSession().Result.Id;
+ request.Id = GetSession(_sessionContext).Result.Id;
}
_sessionManager.ReportCapabilities(request.Id, request);
}
diff --git a/MediaBrowser.Api/SimilarItemsHelper.cs b/MediaBrowser.Api/SimilarItemsHelper.cs
index ddcb6b7bf8..892c9f698a 100644
--- a/MediaBrowser.Api/SimilarItemsHelper.cs
+++ b/MediaBrowser.Api/SimilarItemsHelper.cs
@@ -5,12 +5,12 @@ using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
diff --git a/MediaBrowser.Api/Social/SharingService.cs b/MediaBrowser.Api/Social/SharingService.cs
index 7d61c24ecb..37941bd4a3 100644
--- a/MediaBrowser.Api/Social/SharingService.cs
+++ b/MediaBrowser.Api/Social/SharingService.cs
@@ -3,13 +3,12 @@ using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Social;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Social;
-using ServiceStack;
using System;
using System.IO;
using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Social
{
@@ -67,13 +66,15 @@ namespace MediaBrowser.Api.Social
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)
+ 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)
@@ -145,14 +146,14 @@ namespace MediaBrowser.Api.Social
{
if (image.IsLocalFile)
{
- return ToStaticFileResult(image.Path);
+ 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 ToStaticFileResult(updatedImage.Path);
+ return await _resultFactory.GetStaticFileResult(Request, updatedImage.Path).ConfigureAwait(false);
}
catch
{
diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs
index d5158677ca..25a4609351 100644
--- a/MediaBrowser.Api/StartupWizardService.cs
+++ b/MediaBrowser.Api/StartupWizardService.cs
@@ -7,11 +7,11 @@ using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.LiveTv;
-using ServiceStack;
using System;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
@@ -115,10 +115,11 @@ namespace MediaBrowser.Api
config.EnableStandaloneMusicKeys = true;
config.EnableCaseSensitiveItemIds = true;
config.EnableFolderView = true;
- config.SchemaVersion = 109;
config.EnableSimpleArtistDetection = true;
config.SkipDeserializationForBasicTypes = true;
config.SkipDeserializationForPrograms = true;
+ config.SkipDeserializationForAudio = true;
+ config.EnableSeriesPresentationUniqueKey = true;
}
public void Post(UpdateStartupConfiguration request)
diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs
index b3ccee76dd..47d442e796 100644
--- a/MediaBrowser.Api/Subtitles/SubtitleService.cs
+++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs
@@ -6,7 +6,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -15,7 +14,10 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Services;
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
namespace MediaBrowser.Api.Subtitles
@@ -133,8 +135,9 @@ namespace MediaBrowser.Api.Subtitles
private readonly IMediaSourceManager _mediaSourceManager;
private readonly IProviderManager _providerManager;
private readonly IFileSystem _fileSystem;
+ private readonly IAuthorizationContext _authContext;
- public SubtitleService(ILibraryManager libraryManager, ISubtitleManager subtitleManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IProviderManager providerManager, IFileSystem fileSystem)
+ public SubtitleService(ILibraryManager libraryManager, ISubtitleManager subtitleManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IProviderManager providerManager, IFileSystem fileSystem, IAuthorizationContext authContext)
{
_libraryManager = libraryManager;
_subtitleManager = subtitleManager;
@@ -142,6 +145,7 @@ namespace MediaBrowser.Api.Subtitles
_mediaSourceManager = mediaSourceManager;
_providerManager = providerManager;
_fileSystem = fileSystem;
+ _authContext = authContext;
}
public async Task<object> Get(GetSubtitlePlaylist request)
@@ -168,7 +172,7 @@ namespace MediaBrowser.Api.Subtitles
long positionTicks = 0;
var segmentLengthTicks = TimeSpan.FromSeconds(request.SegmentLength).Ticks;
- var accessToken = AuthorizationContext.GetAuthorizationInfo(Request).Token;
+ var accessToken = _authContext.GetAuthorizationInfo(Request).Token;
while (positionTicks < runtime)
{
diff --git a/MediaBrowser.Api/Sync/SyncJobWebSocketListener.cs b/MediaBrowser.Api/Sync/SyncJobWebSocketListener.cs
index 61a26d160a..ac9749a6dc 100644
--- a/MediaBrowser.Api/Sync/SyncJobWebSocketListener.cs
+++ b/MediaBrowser.Api/Sync/SyncJobWebSocketListener.cs
@@ -1,11 +1,11 @@
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Sync;
+using MediaBrowser.Controller.Sync;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Sync;
using System;
using System.Linq;
using System.Threading.Tasks;
+using MediaBrowser.Model.Threading;
namespace MediaBrowser.Api.Sync
{
@@ -26,8 +26,8 @@ namespace MediaBrowser.Api.Sync
private readonly ISyncManager _syncManager;
private string _jobId;
- public SyncJobWebSocketListener(ILogger logger, ISyncManager syncManager)
- : base(logger)
+ public SyncJobWebSocketListener(ILogger logger, ISyncManager syncManager, ITimerFactory timerFactory)
+ : base(logger, timerFactory)
{
_syncManager = syncManager;
_syncManager.SyncJobCancelled += _syncManager_SyncJobCancelled;
diff --git a/MediaBrowser.Api/Sync/SyncJobsWebSocketListener.cs b/MediaBrowser.Api/Sync/SyncJobsWebSocketListener.cs
index 906a52209a..5f9d1ff0ee 100644
--- a/MediaBrowser.Api/Sync/SyncJobsWebSocketListener.cs
+++ b/MediaBrowser.Api/Sync/SyncJobsWebSocketListener.cs
@@ -1,9 +1,9 @@
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Sync;
+using MediaBrowser.Controller.Sync;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Sync;
using System.Collections.Generic;
using System.Threading.Tasks;
+using MediaBrowser.Model.Threading;
namespace MediaBrowser.Api.Sync
{
@@ -25,8 +25,8 @@ namespace MediaBrowser.Api.Sync
private string _userId;
private string _targetId;
- public SyncJobsWebSocketListener(ILogger logger, ISyncManager syncManager)
- : base(logger)
+ public SyncJobsWebSocketListener(ILogger logger, ISyncManager syncManager, ITimerFactory timerFactory)
+ : base(logger, timerFactory)
{
_syncManager = syncManager;
_syncManager.SyncJobCancelled += _syncManager_SyncJobCancelled;
diff --git a/MediaBrowser.Api/Sync/SyncService.cs b/MediaBrowser.Api/Sync/SyncService.cs
index 821591dc4e..e50d2b77f9 100644
--- a/MediaBrowser.Api/Sync/SyncService.cs
+++ b/MediaBrowser.Api/Sync/SyncService.cs
@@ -6,11 +6,11 @@ using MediaBrowser.Controller.Sync;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Sync;
using MediaBrowser.Model.Users;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Sync
{
@@ -161,13 +161,15 @@ namespace MediaBrowser.Api.Sync
private readonly IDtoService _dtoService;
private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager;
+ private readonly IAuthorizationContext _authContext;
- public SyncService(ISyncManager syncManager, IDtoService dtoService, ILibraryManager libraryManager, IUserManager userManager)
+ public SyncService(ISyncManager syncManager, IDtoService dtoService, ILibraryManager libraryManager, IUserManager userManager, IAuthorizationContext authContext)
{
_syncManager = syncManager;
_dtoService = dtoService;
_libraryManager = libraryManager;
_userManager = userManager;
+ _authContext = authContext;
}
public object Get(GetSyncTargets request)
@@ -263,7 +265,7 @@ namespace MediaBrowser.Api.Sync
result.Targets = _syncManager.GetSyncTargets(request.UserId)
.ToList();
- var auth = AuthorizationContext.GetAuthorizationInfo(Request);
+ var auth = _authContext.GetAuthorizationInfo(Request);
var authenticatedUser = _userManager.GetUserById(auth.UserId);
if (!string.IsNullOrWhiteSpace(request.TargetId))
diff --git a/MediaBrowser.Api/System/ActivityLogService.cs b/MediaBrowser.Api/System/ActivityLogService.cs
index d24bc99cb6..e3a18a9335 100644
--- a/MediaBrowser.Api/System/ActivityLogService.cs
+++ b/MediaBrowser.Api/System/ActivityLogService.cs
@@ -1,10 +1,9 @@
-using MediaBrowser.Controller.Activity;
-using MediaBrowser.Controller.Net;
+using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Querying;
-using ServiceStack;
using System;
using System.Globalization;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.System
{
diff --git a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs
index a951cd3d68..c641695dd8 100644
--- a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs
+++ b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs
@@ -1,10 +1,9 @@
-using MediaBrowser.Controller.Activity;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Activity;
+using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Logging;
using System.Collections.Generic;
using System.Threading.Tasks;
+using MediaBrowser.Model.Threading;
namespace MediaBrowser.Api.System
{
@@ -27,8 +26,7 @@ namespace MediaBrowser.Api.System
/// </summary>
private readonly IActivityManager _activityManager;
- public ActivityLogWebSocketListener(ILogger logger, IActivityManager activityManager)
- : base(logger)
+ public ActivityLogWebSocketListener(ILogger logger, ITimerFactory timerFactory, IActivityManager activityManager) : base(logger, timerFactory)
{
_activityManager = activityManager;
_activityManager.EntryCreated += _activityManager_EntryCreated;
diff --git a/MediaBrowser.Api/System/SystemInfoWebSocketListener.cs b/MediaBrowser.Api/System/SystemInfoWebSocketListener.cs
index a53bfac276..8d74cc66c0 100644
--- a/MediaBrowser.Api/System/SystemInfoWebSocketListener.cs
+++ b/MediaBrowser.Api/System/SystemInfoWebSocketListener.cs
@@ -3,6 +3,7 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.System;
using System.Threading.Tasks;
+using MediaBrowser.Model.Threading;
namespace MediaBrowser.Api.System
{
@@ -28,10 +29,8 @@ namespace MediaBrowser.Api.System
/// <summary>
/// Initializes a new instance of the <see cref="SystemInfoWebSocketListener" /> class.
/// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="appHost">The app host.</param>
- public SystemInfoWebSocketListener(ILogger logger, IServerApplicationHost appHost)
- : base(logger)
+ public SystemInfoWebSocketListener(ILogger logger, IServerApplicationHost appHost, ITimerFactory timerFactory)
+ : base(logger, timerFactory)
{
_appHost = appHost;
}
diff --git a/MediaBrowser.Api/System/SystemService.cs b/MediaBrowser.Api/System/SystemService.cs
index c2318dccbc..bdae670e15 100644
--- a/MediaBrowser.Api/System/SystemService.cs
+++ b/MediaBrowser.Api/System/SystemService.cs
@@ -4,14 +4,16 @@ using MediaBrowser.Common.Security;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.System;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.System
{
@@ -124,7 +126,7 @@ namespace MediaBrowser.Api.System
.Where(i => string.Equals(i.Extension, ".txt", StringComparison.OrdinalIgnoreCase))
.ToList();
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
files = new List<FileSystemMetadata>();
}
@@ -149,7 +151,7 @@ namespace MediaBrowser.Api.System
var file = _fileSystem.GetFiles(_appPaths.LogDirectoryPath)
.First(i => string.Equals(i.Name, request.Name, StringComparison.OrdinalIgnoreCase));
- return ResultFactory.GetStaticFileResult(Request, file.FullName, FileShare.ReadWrite);
+ return ResultFactory.GetStaticFileResult(Request, file.FullName, FileShareMode.ReadWrite);
}
/// <summary>
diff --git a/MediaBrowser.Api/TestService.cs b/MediaBrowser.Api/TestService.cs
new file mode 100644
index 0000000000..5340b816cf
--- /dev/null
+++ b/MediaBrowser.Api/TestService.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
+
+namespace MediaBrowser.Api
+{
+ [Route("/Test/String", "GET")]
+ public class GetString
+ {
+ }
+
+ [Route("/Test/OptimizedString", "GET")]
+ public class GetOptimizedString
+ {
+ }
+
+ [Route("/Test/Bytes", "GET")]
+ public class GetBytes
+ {
+ }
+
+ [Route("/Test/OptimizedBytes", "GET")]
+ public class GetOptimizedBytes
+ {
+ }
+
+ [Route("/Test/Stream", "GET")]
+ public class GetStream
+ {
+ }
+
+ [Route("/Test/OptimizedStream", "GET")]
+ public class GetOptimizedStream
+ {
+ }
+
+ [Route("/Test/BytesWithContentType", "GET")]
+ public class GetBytesWithContentType
+ {
+ }
+
+ public class TestService : BaseApiService
+ {
+ public object Get(GetString request)
+ {
+ return "Welcome to Emby!";
+ }
+ public object Get(GetOptimizedString request)
+ {
+ return ToOptimizedResult("Welcome to Emby!");
+ }
+ public object Get(GetBytes request)
+ {
+ return Encoding.UTF8.GetBytes("Welcome to Emby!");
+ }
+ public object Get(GetOptimizedBytes request)
+ {
+ return ToOptimizedResult(Encoding.UTF8.GetBytes("Welcome to Emby!"));
+ }
+ public object Get(GetBytesWithContentType request)
+ {
+ return ApiEntryPoint.Instance.ResultFactory.GetResult(Encoding.UTF8.GetBytes("Welcome to Emby!"), "text/html");
+ }
+ public object Get(GetStream request)
+ {
+ return new MemoryStream(Encoding.UTF8.GetBytes("Welcome to Emby!"));
+ }
+ public object Get(GetOptimizedStream request)
+ {
+ return ToOptimizedResult(new MemoryStream(Encoding.UTF8.GetBytes("Welcome to Emby!")));
+ }
+ }
+}
diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs
index ef93d2fc94..71c5e732aa 100644
--- a/MediaBrowser.Api/TvShowsService.cs
+++ b/MediaBrowser.Api/TvShowsService.cs
@@ -8,12 +8,12 @@ using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.TV;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
@@ -265,6 +265,7 @@ namespace MediaBrowser.Api
private readonly IItemRepository _itemRepo;
private readonly IDtoService _dtoService;
private readonly ITVSeriesManager _tvSeriesManager;
+ private readonly IAuthorizationContext _authContext;
/// <summary>
/// Initializes a new instance of the <see cref="TvShowsService" /> class.
@@ -272,7 +273,7 @@ namespace MediaBrowser.Api
/// <param name="userManager">The user manager.</param>
/// <param name="userDataManager">The user data repository.</param>
/// <param name="libraryManager">The library manager.</param>
- public TvShowsService(IUserManager userManager, IUserDataManager userDataManager, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService, ITVSeriesManager tvSeriesManager)
+ public TvShowsService(IUserManager userManager, IUserDataManager userDataManager, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService, ITVSeriesManager tvSeriesManager, IAuthorizationContext authContext)
{
_userManager = userManager;
_userDataManager = userDataManager;
@@ -280,6 +281,7 @@ namespace MediaBrowser.Api
_itemRepo = itemRepo;
_dtoService = dtoService;
_tvSeriesManager = tvSeriesManager;
+ _authContext = authContext;
}
/// <summary>
@@ -302,7 +304,7 @@ namespace MediaBrowser.Api
(!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder :
_libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id);
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
@@ -334,7 +336,7 @@ namespace MediaBrowser.Api
var parentIdGuid = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId);
- var options = GetDtoOptions(request);
+ var options = GetDtoOptions(_authContext, request);
var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
@@ -379,7 +381,7 @@ namespace MediaBrowser.Api
var user = _userManager.GetUserById(request.UserId);
- var options = GetDtoOptions(request);
+ var options = GetDtoOptions(_authContext, request);
var returnItems = (await _dtoService.GetBaseItemDtos(result.Items, options, user).ConfigureAwait(false)).ToArray();
@@ -434,7 +436,7 @@ namespace MediaBrowser.Api
}).ConfigureAwait(false)).Items.OfType<Season>();
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
var returnItems = (await _dtoService.GetBaseItemDtos(seasons, dtoOptions, user).ConfigureAwait(false))
.ToArray();
@@ -526,7 +528,7 @@ namespace MediaBrowser.Api
var pagedItems = ApplyPaging(returnList, request.StartIndex, request.Limit);
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
var dtos = (await _dtoService.GetBaseItemDtos(pagedItems, dtoOptions, user).ConfigureAwait(false))
.ToArray();
diff --git a/MediaBrowser.Api/UserLibrary/ArtistsService.cs b/MediaBrowser.Api/UserLibrary/ArtistsService.cs
index ad031f483f..5bbd96c7c3 100644
--- a/MediaBrowser.Api/UserLibrary/ArtistsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ArtistsService.cs
@@ -6,9 +6,9 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
-using ServiceStack;
using System.Collections.Generic;
using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.UserLibrary
{
@@ -50,18 +50,6 @@ namespace MediaBrowser.Api.UserLibrary
public class ArtistsService : BaseItemsByNameService<MusicArtist>
{
/// <summary>
- /// Initializes a new instance of the <see cref="ArtistsService" /> class.
- /// </summary>
- /// <param name="userManager">The user manager.</param>
- /// <param name="libraryManager">The library manager.</param>
- /// <param name="userDataRepository">The user data repository.</param>
- /// <param name="itemRepo">The item repo.</param>
- public ArtistsService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepo, IDtoService dtoService)
- : base(userManager, libraryManager, userDataRepository, itemRepo, dtoService)
- {
- }
-
- /// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
@@ -81,8 +69,8 @@ namespace MediaBrowser.Api.UserLibrary
private BaseItemDto GetItem(GetArtist request)
{
var item = GetArtist(request.Name, LibraryManager);
-
- var dtoOptions = GetDtoOptions(request);
+
+ var dtoOptions = GetDtoOptions(AuthorizationContext, request);
if (!string.IsNullOrWhiteSpace(request.UserId))
{
@@ -148,5 +136,9 @@ namespace MediaBrowser.Api.UserLibrary
{
throw new NotImplementedException();
}
+
+ public ArtistsService(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/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
index 182a92fc83..ff285b6053 100644
--- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
+++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
@@ -4,11 +4,12 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Linq;
+using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.UserLibrary
{
@@ -30,6 +31,7 @@ namespace MediaBrowser.Api.UserLibrary
protected readonly IUserDataManager UserDataRepository;
protected readonly IItemRepository ItemRepository;
protected IDtoService DtoService { get; private set; }
+ protected IAuthorizationContext AuthorizationContext { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="BaseItemsByNameService{TItemType}" /> class.
@@ -39,13 +41,14 @@ namespace MediaBrowser.Api.UserLibrary
/// <param name="userDataRepository">The user data repository.</param>
/// <param name="itemRepository">The item repository.</param>
/// <param name="dtoService">The dto service.</param>
- protected BaseItemsByNameService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepository, IDtoService dtoService)
+ protected BaseItemsByNameService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepository, IDtoService dtoService, IAuthorizationContext authorizationContext)
{
UserManager = userManager;
LibraryManager = libraryManager;
UserDataRepository = userDataRepository;
ItemRepository = itemRepository;
DtoService = dtoService;
+ AuthorizationContext = authorizationContext;
}
protected BaseItem GetParentItem(GetItemsByName request)
@@ -86,7 +89,7 @@ namespace MediaBrowser.Api.UserLibrary
protected ItemsResult GetResultSlim(GetItemsByName request)
{
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(AuthorizationContext, request);
User user = null;
BaseItem parentItem;
@@ -223,7 +226,7 @@ namespace MediaBrowser.Api.UserLibrary
/// <returns>Task{ItemsResult}.</returns>
protected ItemsResult GetResult(GetItemsByName request)
{
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(AuthorizationContext, request);
User user = null;
BaseItem parentItem;
diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
index 96acb1f607..f106adde51 100644
--- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
+++ b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
@@ -1,9 +1,9 @@
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Linq;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.UserLibrary
{
diff --git a/MediaBrowser.Api/UserLibrary/GameGenresService.cs b/MediaBrowser.Api/UserLibrary/GameGenresService.cs
index a0f3855c55..2eef1ab2fd 100644
--- a/MediaBrowser.Api/UserLibrary/GameGenresService.cs
+++ b/MediaBrowser.Api/UserLibrary/GameGenresService.cs
@@ -4,10 +4,10 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
-using ServiceStack;
using System;
using System.Collections.Generic;
using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.UserLibrary
{
@@ -37,11 +37,6 @@ namespace MediaBrowser.Api.UserLibrary
[Authenticated]
public class GameGenresService : BaseItemsByNameService<GameGenre>
{
- public GameGenresService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepo, IDtoService dtoService)
- : base(userManager, libraryManager, userDataRepository, itemRepo, dtoService)
- {
- }
-
/// <summary>
/// Gets the specified request.
/// </summary>
@@ -63,8 +58,8 @@ namespace MediaBrowser.Api.UserLibrary
{
var item = GetGameGenre(request.Name, LibraryManager);
- var dtoOptions = GetDtoOptions(request);
-
+ var dtoOptions = GetDtoOptions(AuthorizationContext, request);
+
if (!string.IsNullOrWhiteSpace(request.UserId))
{
var user = UserManager.GetUserById(request.UserId);
@@ -102,5 +97,9 @@ namespace MediaBrowser.Api.UserLibrary
{
throw new NotImplementedException();
}
+
+ public GameGenresService(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/GenresService.cs b/MediaBrowser.Api/UserLibrary/GenresService.cs
index ec2bc6bda5..664efac14c 100644
--- a/MediaBrowser.Api/UserLibrary/GenresService.cs
+++ b/MediaBrowser.Api/UserLibrary/GenresService.cs
@@ -5,10 +5,10 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using ServiceStack;
using System;
using System.Collections.Generic;
using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.UserLibrary
{
@@ -47,11 +47,6 @@ namespace MediaBrowser.Api.UserLibrary
[Authenticated]
public class GenresService : BaseItemsByNameService<Genre>
{
- public GenresService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepo, IDtoService dtoService)
- : base(userManager, libraryManager, userDataRepository, itemRepo, dtoService)
- {
- }
-
/// <summary>
/// Gets the specified request.
/// </summary>
@@ -72,8 +67,8 @@ namespace MediaBrowser.Api.UserLibrary
private BaseItemDto GetItem(GetGenre request)
{
var item = GetGenre(request.Name, LibraryManager);
-
- var dtoOptions = GetDtoOptions(request);
+
+ var dtoOptions = GetDtoOptions(AuthorizationContext ,request);
if (!string.IsNullOrWhiteSpace(request.UserId))
{
@@ -124,5 +119,9 @@ namespace MediaBrowser.Api.UserLibrary
{
throw new NotImplementedException();
}
+
+ public GenresService(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/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs
index 04395da3cd..a07128f749 100644
--- a/MediaBrowser.Api/UserLibrary/ItemsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs
@@ -1,16 +1,16 @@
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.UserLibrary
{
@@ -41,6 +41,7 @@ namespace MediaBrowser.Api.UserLibrary
private readonly ILocalizationManager _localization;
private readonly IDtoService _dtoService;
+ private readonly IAuthorizationContext _authContext;
/// <summary>
/// Initializes a new instance of the <see cref="ItemsService" /> class.
@@ -49,7 +50,7 @@ namespace MediaBrowser.Api.UserLibrary
/// <param name="libraryManager">The library manager.</param>
/// <param name="localization">The localization.</param>
/// <param name="dtoService">The dto service.</param>
- public ItemsService(IUserManager userManager, ILibraryManager libraryManager, ILocalizationManager localization, IDtoService dtoService)
+ public ItemsService(IUserManager userManager, ILibraryManager libraryManager, ILocalizationManager localization, IDtoService dtoService, IAuthorizationContext authContext)
{
if (userManager == null)
{
@@ -72,6 +73,7 @@ namespace MediaBrowser.Api.UserLibrary
_libraryManager = libraryManager;
_localization = localization;
_dtoService = dtoService;
+ _authContext = authContext;
}
/// <summary>
@@ -100,7 +102,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
var result = await GetQueryResult(request, dtoOptions, user).ConfigureAwait(false);
diff --git a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs
index b2eba070fc..305c136dfb 100644
--- a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs
+++ b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs
@@ -6,9 +6,9 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
-using ServiceStack;
using System.Collections.Generic;
using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.UserLibrary
{
@@ -38,11 +38,6 @@ namespace MediaBrowser.Api.UserLibrary
[Authenticated]
public class MusicGenresService : BaseItemsByNameService<MusicGenre>
{
- public MusicGenresService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepo, IDtoService dtoService)
- : base(userManager, libraryManager, userDataRepository, itemRepo, dtoService)
- {
- }
-
/// <summary>
/// Gets the specified request.
/// </summary>
@@ -63,8 +58,8 @@ namespace MediaBrowser.Api.UserLibrary
private BaseItemDto GetItem(GetMusicGenre request)
{
var item = GetMusicGenre(request.Name, LibraryManager);
-
- var dtoOptions = GetDtoOptions(request);
+
+ var dtoOptions = GetDtoOptions(AuthorizationContext, request);
if (!string.IsNullOrWhiteSpace(request.UserId))
{
@@ -103,5 +98,9 @@ namespace MediaBrowser.Api.UserLibrary
{
throw new NotImplementedException();
}
+
+ public MusicGenresService(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/PersonsService.cs b/MediaBrowser.Api/UserLibrary/PersonsService.cs
index e6a60fcc6d..dbce22578b 100644
--- a/MediaBrowser.Api/UserLibrary/PersonsService.cs
+++ b/MediaBrowser.Api/UserLibrary/PersonsService.cs
@@ -4,9 +4,9 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
-using ServiceStack;
using System.Collections.Generic;
using System.Linq;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.UserLibrary
{
@@ -46,18 +46,6 @@ namespace MediaBrowser.Api.UserLibrary
public class PersonsService : BaseItemsByNameService<Person>
{
/// <summary>
- /// Initializes a new instance of the <see cref="PersonsService" /> class.
- /// </summary>
- /// <param name="userManager">The user manager.</param>
- /// <param name="libraryManager">The library manager.</param>
- /// <param name="userDataRepository">The user data repository.</param>
- /// <param name="itemRepo">The item repo.</param>
- public PersonsService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepo, IDtoService dtoService)
- : base(userManager, libraryManager, userDataRepository, itemRepo, dtoService)
- {
- }
-
- /// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
@@ -77,8 +65,8 @@ namespace MediaBrowser.Api.UserLibrary
private BaseItemDto GetItem(GetPerson request)
{
var item = GetPerson(request.Name, LibraryManager);
-
- var dtoOptions = GetDtoOptions(request);
+
+ var dtoOptions = GetDtoOptions(AuthorizationContext, request);
if (!string.IsNullOrWhiteSpace(request.UserId))
{
@@ -155,5 +143,9 @@ namespace MediaBrowser.Api.UserLibrary
return allPeople.Where(i => allIds.Contains(i.ItemId)).OrderBy(p => p.SortOrder ?? int.MaxValue).ThenBy(p => p.Type);
}
+
+ 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/PlaystateService.cs b/MediaBrowser.Api/UserLibrary/PlaystateService.cs
index 710d337ec1..5fe6c1771f 100644
--- a/MediaBrowser.Api/UserLibrary/PlaystateService.cs
+++ b/MediaBrowser.Api/UserLibrary/PlaystateService.cs
@@ -4,11 +4,11 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Session;
-using ServiceStack;
using System;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.UserLibrary
{
@@ -234,13 +234,17 @@ namespace MediaBrowser.Api.UserLibrary
private readonly IUserDataManager _userDataRepository;
private readonly ILibraryManager _libraryManager;
private readonly ISessionManager _sessionManager;
+ private readonly ISessionContext _sessionContext;
+ private readonly IAuthorizationContext _authContext;
- public PlaystateService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, ISessionManager sessionManager)
+ public PlaystateService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, ISessionManager sessionManager, ISessionContext sessionContext, IAuthorizationContext authContext)
{
_userManager = userManager;
_userDataRepository = userDataRepository;
_libraryManager = libraryManager;
_sessionManager = sessionManager;
+ _sessionContext = sessionContext;
+ _authContext = authContext;
}
/// <summary>
@@ -265,7 +269,7 @@ namespace MediaBrowser.Api.UserLibrary
datePlayed = DateTime.ParseExact(request.DatePlayed, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
}
- var session = await GetSession().ConfigureAwait(false);
+ var session = await GetSession(_sessionContext).ConfigureAwait(false);
var dto = await UpdatePlayedStatus(user, request.Id, true, datePlayed).ConfigureAwait(false);
@@ -303,7 +307,7 @@ namespace MediaBrowser.Api.UserLibrary
public void Post(ReportPlaybackStart request)
{
- request.SessionId = GetSession().Result.Id;
+ request.SessionId = GetSession(_sessionContext).Result.Id;
var task = _sessionManager.OnPlaybackStart(request);
@@ -335,7 +339,7 @@ namespace MediaBrowser.Api.UserLibrary
public void Post(ReportPlaybackProgress request)
{
- request.SessionId = GetSession().Result.Id;
+ request.SessionId = GetSession(_sessionContext).Result.Id;
var task = _sessionManager.OnPlaybackProgress(request);
@@ -369,10 +373,10 @@ namespace MediaBrowser.Api.UserLibrary
if (!string.IsNullOrWhiteSpace(request.PlaySessionId))
{
- ApiEntryPoint.Instance.KillTranscodingJobs(AuthorizationContext.GetAuthorizationInfo(Request).DeviceId, request.PlaySessionId, s => true);
+ ApiEntryPoint.Instance.KillTranscodingJobs(_authContext.GetAuthorizationInfo(Request).DeviceId, request.PlaySessionId, s => true);
}
- request.SessionId = GetSession().Result.Id;
+ request.SessionId = GetSession(_sessionContext).Result.Id;
var task = _sessionManager.OnPlaybackStopped(request);
@@ -394,7 +398,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var user = _userManager.GetUserById(request.UserId);
- var session = await GetSession().ConfigureAwait(false);
+ var session = await GetSession(_sessionContext).ConfigureAwait(false);
var dto = await UpdatePlayedStatus(user, request.Id, false, null).ConfigureAwait(false);
diff --git a/MediaBrowser.Api/UserLibrary/StudiosService.cs b/MediaBrowser.Api/UserLibrary/StudiosService.cs
index 9e9c25d78a..f4debcf48a 100644
--- a/MediaBrowser.Api/UserLibrary/StudiosService.cs
+++ b/MediaBrowser.Api/UserLibrary/StudiosService.cs
@@ -5,10 +5,10 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
-using ServiceStack;
using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.UserLibrary
{
@@ -47,11 +47,6 @@ namespace MediaBrowser.Api.UserLibrary
[Authenticated]
public class StudiosService : BaseItemsByNameService<Studio>
{
- public StudiosService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepo, IDtoService dtoService)
- : base(userManager, libraryManager, userDataRepository, itemRepo, dtoService)
- {
- }
-
/// <summary>
/// Gets the specified request.
/// </summary>
@@ -73,8 +68,8 @@ namespace MediaBrowser.Api.UserLibrary
{
var item = GetStudio(request.Name, LibraryManager);
- var dtoOptions = GetDtoOptions(request);
-
+ var dtoOptions = GetDtoOptions(AuthorizationContext, request);
+
if (!string.IsNullOrWhiteSpace(request.UserId))
{
var user = UserManager.GetUserById(request.UserId);
@@ -117,5 +112,9 @@ namespace MediaBrowser.Api.UserLibrary
.DistinctNames()
.Select(name => LibraryManager.GetStudio(name));
}
+
+ 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 c392ef4634..1ac98d1656 100644
--- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
+++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
@@ -6,14 +6,17 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.UserLibrary
{
@@ -268,8 +271,9 @@ namespace MediaBrowser.Api.UserLibrary
private readonly IDtoService _dtoService;
private readonly IUserViewManager _userViewManager;
private readonly IFileSystem _fileSystem;
+ private readonly IAuthorizationContext _authContext;
- public UserLibraryService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, IUserViewManager userViewManager, IFileSystem fileSystem)
+ public UserLibraryService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, IUserViewManager userViewManager, IFileSystem fileSystem, IAuthorizationContext authContext)
{
_userManager = userManager;
_libraryManager = libraryManager;
@@ -277,6 +281,7 @@ namespace MediaBrowser.Api.UserLibrary
_dtoService = dtoService;
_userViewManager = userViewManager;
_fileSystem = fileSystem;
+ _authContext = authContext;
}
/// <summary>
@@ -313,20 +318,20 @@ namespace MediaBrowser.Api.UserLibrary
UserId = request.UserId
});
- var options = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
var dtos = list.Select(i =>
{
var item = i.Item2[0];
var childCount = 0;
- if (i.Item1 != null && i.Item2.Count > 0)
+ if (i.Item1 != null && (i.Item2.Count > 1 || i.Item1 is MusicAlbum))
{
item = i.Item1;
childCount = i.Item2.Count;
}
- var dto = _dtoService.GetBaseItemDto(item, options, user);
+ var dto = _dtoService.GetBaseItemDto(item, dtoOptions, user);
dto.ChildCount = childCount;
@@ -349,7 +354,7 @@ namespace MediaBrowser.Api.UserLibrary
// Get them from the child tree
if (series != null)
{
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
// Avoid implicitly captured closure
var currentUser = user;
@@ -380,7 +385,7 @@ namespace MediaBrowser.Api.UserLibrary
// Get them from the db
if (movie != null)
{
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
var dtos = movie.SpecialFeatureIds
.Select(_libraryManager.GetItemById)
@@ -419,7 +424,7 @@ namespace MediaBrowser.Api.UserLibrary
trailerIds = hasTrailers.GetTrailerIds();
}
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
var dtos = trailerIds
.Select(_libraryManager.GetItemById)
@@ -441,7 +446,7 @@ namespace MediaBrowser.Api.UserLibrary
await RefreshItemOnDemandIfNeeded(item).ConfigureAwait(false);
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
var result = _dtoService.GetBaseItemDto(item, dtoOptions, user);
@@ -480,7 +485,7 @@ namespace MediaBrowser.Api.UserLibrary
var item = user.RootFolder;
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
var result = _dtoService.GetBaseItemDto(item, dtoOptions, user);
@@ -500,7 +505,7 @@ namespace MediaBrowser.Api.UserLibrary
var items = await _libraryManager.GetIntros(item, user).ConfigureAwait(false);
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
var dtos = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user))
.ToArray();
diff --git a/MediaBrowser.Api/UserLibrary/UserViewsService.cs b/MediaBrowser.Api/UserLibrary/UserViewsService.cs
index d29191db40..715745a7dc 100644
--- a/MediaBrowser.Api/UserLibrary/UserViewsService.cs
+++ b/MediaBrowser.Api/UserLibrary/UserViewsService.cs
@@ -5,12 +5,13 @@ using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Library;
using MediaBrowser.Model.Querying;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.UserLibrary
{
@@ -57,12 +58,14 @@ namespace MediaBrowser.Api.UserLibrary
private readonly IUserManager _userManager;
private readonly IUserViewManager _userViewManager;
private readonly IDtoService _dtoService;
+ private readonly IAuthorizationContext _authContext;
- public UserViewsService(IUserManager userManager, IUserViewManager userViewManager, IDtoService dtoService)
+ public UserViewsService(IUserManager userManager, IUserViewManager userViewManager, IDtoService dtoService, IAuthorizationContext authContext)
{
_userManager = userManager;
_userViewManager = userViewManager;
_dtoService = dtoService;
+ _authContext = authContext;
}
public async Task<object> Get(GetUserViews request)
@@ -82,7 +85,7 @@ namespace MediaBrowser.Api.UserLibrary
query.PresetViews = request.PresetViews.Split(',');
}
- var app = AuthorizationContext.GetAuthorizationInfo(Request).Client ?? string.Empty;
+ 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 };
@@ -91,10 +94,11 @@ namespace MediaBrowser.Api.UserLibrary
var folders = await _userViewManager.GetUserViews(query, CancellationToken.None).ConfigureAwait(false);
- var dtoOptions = GetDtoOptions(request);
- dtoOptions.Fields = new List<ItemFields>();
+ var dtoOptions = GetDtoOptions(_authContext, request);
dtoOptions.Fields.Add(ItemFields.PrimaryImageAspectRatio);
dtoOptions.Fields.Add(ItemFields.DisplayPreferencesId);
+ dtoOptions.Fields.Remove(ItemFields.SyncInfo);
+ dtoOptions.Fields.Remove(ItemFields.BasicSyncInfo);
var user = _userManager.GetUserById(request.UserId);
diff --git a/MediaBrowser.Api/UserLibrary/YearsService.cs b/MediaBrowser.Api/UserLibrary/YearsService.cs
index ef80f87142..1059b72cb8 100644
--- a/MediaBrowser.Api/UserLibrary/YearsService.cs
+++ b/MediaBrowser.Api/UserLibrary/YearsService.cs
@@ -4,9 +4,9 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
-using ServiceStack;
using System.Collections.Generic;
using System.Linq;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.UserLibrary
{
@@ -45,11 +45,6 @@ namespace MediaBrowser.Api.UserLibrary
[Authenticated]
public class YearsService : BaseItemsByNameService<Year>
{
- public YearsService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepo, IDtoService dtoService)
- : base(userManager, libraryManager, userDataRepository, itemRepo, dtoService)
- {
- }
-
/// <summary>
/// Gets the specified request.
/// </summary>
@@ -70,8 +65,8 @@ namespace MediaBrowser.Api.UserLibrary
private BaseItemDto GetItem(GetYear request)
{
var item = LibraryManager.GetYear(request.Year);
-
- var dtoOptions = GetDtoOptions(request);
+
+ var dtoOptions = GetDtoOptions(AuthorizationContext, request);
if (!string.IsNullOrWhiteSpace(request.UserId))
{
@@ -111,5 +106,9 @@ namespace MediaBrowser.Api.UserLibrary
.Distinct()
.Select(year => LibraryManager.GetYear(year));
}
+
+ public YearsService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepository, IDtoService dtoService, IAuthorizationContext authorizationContext) : base(userManager, libraryManager, userDataRepository, itemRepository, dtoService, authorizationContext)
+ {
+ }
}
}
diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs
index 07ff36c41f..96c7fc111c 100644
--- a/MediaBrowser.Api/UserService.cs
+++ b/MediaBrowser.Api/UserService.cs
@@ -9,11 +9,11 @@ using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Connect;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Users;
-using ServiceStack;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
@@ -246,7 +246,7 @@ namespace MediaBrowser.Api
/// <summary>
/// Class UsersService
/// </summary>
- public class UserService : BaseApiService, IHasAuthorization
+ public class UserService : BaseApiService
{
/// <summary>
/// The _user manager
@@ -256,14 +256,16 @@ namespace MediaBrowser.Api
private readonly IServerConfigurationManager _config;
private readonly INetworkManager _networkManager;
private readonly IDeviceManager _deviceManager;
+ private readonly IAuthorizationContext _authContext;
- public UserService(IUserManager userManager, ISessionManager sessionMananger, IServerConfigurationManager config, INetworkManager networkManager, IDeviceManager deviceManager)
+ public UserService(IUserManager userManager, ISessionManager sessionMananger, IServerConfigurationManager config, INetworkManager networkManager, IDeviceManager deviceManager, IAuthorizationContext authContext)
{
_userManager = userManager;
_sessionMananger = sessionMananger;
_config = config;
_networkManager = networkManager;
_deviceManager = deviceManager;
+ _authContext = authContext;
}
public object Get(GetPublicUsers request)
@@ -316,7 +318,7 @@ namespace MediaBrowser.Api
if (filterByDevice)
{
- var deviceId = AuthorizationContext.GetAuthorizationInfo(Request).DeviceId;
+ var deviceId = _authContext.GetAuthorizationInfo(Request).DeviceId;
if (!string.IsNullOrWhiteSpace(deviceId))
{
@@ -412,7 +414,7 @@ namespace MediaBrowser.Api
public async Task<object> Post(AuthenticateUserByName request)
{
- var auth = AuthorizationContext.GetAuthorizationInfo(Request);
+ var auth = _authContext.GetAuthorizationInfo(Request);
var result = await _sessionMananger.AuthenticateNewSession(new AuthenticationRequest
{
@@ -442,7 +444,7 @@ namespace MediaBrowser.Api
public async Task PostAsync(UpdateUserPassword request)
{
- AssertCanUpdateUser(_userManager, request.Id);
+ AssertCanUpdateUser(_authContext, _userManager, request.Id);
var user = _userManager.GetUserById(request.Id);
@@ -466,7 +468,7 @@ namespace MediaBrowser.Api
await _userManager.ChangePassword(user, request.NewPassword).ConfigureAwait(false);
- var currentToken = AuthorizationContext.GetAuthorizationInfo(Request).Token;
+ var currentToken = _authContext.GetAuthorizationInfo(Request).Token;
await _sessionMananger.RevokeUserTokens(user.Id.ToString("N"), currentToken).ConfigureAwait(false);
}
@@ -480,7 +482,7 @@ namespace MediaBrowser.Api
public async Task PostAsync(UpdateUserEasyPassword request)
{
- AssertCanUpdateUser(_userManager, request.Id);
+ AssertCanUpdateUser(_authContext, _userManager, request.Id);
var user = _userManager.GetUserById(request.Id);
@@ -516,7 +518,7 @@ namespace MediaBrowser.Api
// https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs
var id = GetPathValue(1);
- AssertCanUpdateUser(_userManager, id);
+ AssertCanUpdateUser(_authContext, _userManager, id);
var dtoUser = request;
@@ -566,7 +568,7 @@ namespace MediaBrowser.Api
public void Post(UpdateUserConfiguration request)
{
- AssertCanUpdateUser(_userManager, request.Id);
+ AssertCanUpdateUser(_authContext, _userManager, request.Id);
var task = _userManager.UpdateConfiguration(request.Id, request);
@@ -606,7 +608,7 @@ namespace MediaBrowser.Api
throw new ArgumentException("There must be at least one enabled user in the system.");
}
- var currentToken = AuthorizationContext.GetAuthorizationInfo(Request).Token;
+ var currentToken = _authContext.GetAuthorizationInfo(Request).Token;
await _sessionMananger.RevokeUserTokens(user.Id.ToString("N"), currentToken).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Api/VideosService.cs b/MediaBrowser.Api/VideosService.cs
index 4a5eb1eab7..f7b83f23bf 100644
--- a/MediaBrowser.Api/VideosService.cs
+++ b/MediaBrowser.Api/VideosService.cs
@@ -5,13 +5,15 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Querying;
-using ServiceStack;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Api
{
@@ -54,8 +56,9 @@ namespace MediaBrowser.Api
private readonly IFileSystem _fileSystem;
private readonly IItemRepository _itemRepo;
private readonly IServerConfigurationManager _config;
+ private readonly IAuthorizationContext _authContext;
- public VideosService(ILibraryManager libraryManager, IUserManager userManager, IDtoService dtoService, IItemRepository itemRepo, IFileSystem fileSystem, IServerConfigurationManager config)
+ public VideosService(ILibraryManager libraryManager, IUserManager userManager, IDtoService dtoService, IItemRepository itemRepo, IFileSystem fileSystem, IServerConfigurationManager config, IAuthorizationContext authContext)
{
_libraryManager = libraryManager;
_userManager = userManager;
@@ -63,6 +66,7 @@ namespace MediaBrowser.Api
_itemRepo = itemRepo;
_fileSystem = fileSystem;
_config = config;
+ _authContext = authContext;
}
/// <summary>
@@ -80,7 +84,7 @@ namespace MediaBrowser.Api
: _libraryManager.RootFolder)
: _libraryManager.GetItemById(request.Id);
- var dtoOptions = GetDtoOptions(request);
+ var dtoOptions = GetDtoOptions(_authContext, request);
var video = item as Video;
BaseItemDto[] items;
diff --git a/MediaBrowser.Api/packages.config b/MediaBrowser.Api/packages.config
deleted file mode 100644
index 4f2cbae7cb..0000000000
--- a/MediaBrowser.Api/packages.config
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
- <package id="CommonIO" version="1.0.0.9" targetFramework="net45" />
- <package id="morelinq" version="1.4.0" targetFramework="net45" />
- <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
-</packages> \ No newline at end of file
diff --git a/MediaBrowser.Api/project.json b/MediaBrowser.Api/project.json
new file mode 100644
index 0000000000..fbbe9eaf32
--- /dev/null
+++ b/MediaBrowser.Api/project.json
@@ -0,0 +1,17 @@
+{
+ "frameworks":{
+ "netstandard1.6":{
+ "dependencies":{
+ "NETStandard.Library":"1.6.0",
+ }
+ },
+ ".NETPortable,Version=v4.5,Profile=Profile7":{
+ "buildOptions": {
+ "define": [ ]
+ },
+ "frameworkAssemblies":{
+
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Common.Implementations/Logging/NlogManager.cs b/MediaBrowser.Common.Implementations/Logging/NlogManager.cs
deleted file mode 100644
index 1bbcccd884..0000000000
--- a/MediaBrowser.Common.Implementations/Logging/NlogManager.cs
+++ /dev/null
@@ -1,264 +0,0 @@
-using MediaBrowser.Model.Logging;
-using NLog;
-using NLog.Config;
-using NLog.Targets;
-using NLog.Targets.Wrappers;
-using System;
-using System.IO;
-using System.Linq;
-
-namespace MediaBrowser.Common.Implementations.Logging
-{
- /// <summary>
- /// Class NlogManager
- /// </summary>
- public class NlogManager : ILogManager
- {
- /// <summary>
- /// Occurs when [logger loaded].
- /// </summary>
- public event EventHandler LoggerLoaded;
- /// <summary>
- /// Gets or sets the log directory.
- /// </summary>
- /// <value>The log directory.</value>
- private string LogDirectory { get; set; }
- /// <summary>
- /// Gets or sets the log file prefix.
- /// </summary>
- /// <value>The log file prefix.</value>
- private string LogFilePrefix { get; set; }
- /// <summary>
- /// Gets the log file path.
- /// </summary>
- /// <value>The log file path.</value>
- public string LogFilePath { get; private set; }
-
- /// <summary>
- /// Gets or sets the exception message prefix.
- /// </summary>
- /// <value>The exception message prefix.</value>
- public string ExceptionMessagePrefix { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="NlogManager" /> class.
- /// </summary>
- /// <param name="logDirectory">The log directory.</param>
- /// <param name="logFileNamePrefix">The log file name prefix.</param>
- public NlogManager(string logDirectory, string logFileNamePrefix)
- {
- LogDirectory = logDirectory;
- LogFilePrefix = logFileNamePrefix;
-
- LogManager.Configuration = new LoggingConfiguration ();
- }
-
- private LogSeverity _severity = LogSeverity.Debug;
- public LogSeverity LogSeverity
- {
- get
- {
- return _severity;
- }
- set
- {
- var changed = _severity != value;
-
- _severity = value;
-
- if (changed)
- {
- UpdateLogLevel(value);
- }
- }
- }
-
- private void UpdateLogLevel(LogSeverity newLevel)
- {
- var level = GetLogLevel(newLevel);
-
- var rules = LogManager.Configuration.LoggingRules;
-
- foreach (var rule in rules)
- {
- if (!rule.IsLoggingEnabledForLevel(level))
- {
- rule.EnableLoggingForLevel(level);
- }
- foreach (var lev in rule.Levels.ToArray())
- {
- if (lev < level)
- {
- rule.DisableLoggingForLevel(lev);
- }
- }
- }
- }
-
- /// <summary>
- /// Adds the file target.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="level">The level.</param>
- private void AddFileTarget(string path, LogSeverity level)
- {
- RemoveTarget("ApplicationLogFileWrapper");
-
- var wrapper = new AsyncTargetWrapper ();
- wrapper.Name = "ApplicationLogFileWrapper";
-
- var logFile = new FileTarget
- {
- FileName = path,
- Layout = "${longdate} ${level} ${logger}: ${message}"
- };
-
- logFile.Name = "ApplicationLogFile";
-
- wrapper.WrappedTarget = logFile;
-
- AddLogTarget(wrapper, level);
- }
-
- /// <summary>
- /// Adds the log target.
- /// </summary>
- /// <param name="target">The target.</param>
- /// <param name="level">The level.</param>
- public void AddLogTarget(Target target, LogSeverity level)
- {
- var config = LogManager.Configuration;
- config.AddTarget(target.Name, target);
-
- var rule = new LoggingRule("*", GetLogLevel(level), target);
- config.LoggingRules.Add(rule);
-
- LogManager.Configuration = config;
- }
-
- /// <summary>
- /// Removes the target.
- /// </summary>
- /// <param name="name">The name.</param>
- public void RemoveTarget(string name)
- {
- var config = LogManager.Configuration;
-
- var target = config.FindTargetByName(name);
-
- if (target != null)
- {
- foreach (var rule in config.LoggingRules.ToList())
- {
- var contains = rule.Targets.Contains(target);
-
- rule.Targets.Remove(target);
-
- if (contains)
- {
- config.LoggingRules.Remove(rule);
- }
- }
-
- config.RemoveTarget(name);
- LogManager.Configuration = config;
- }
- }
-
- /// <summary>
- /// Gets the logger.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>ILogger.</returns>
- public Model.Logging.ILogger GetLogger(string name)
- {
- return new NLogger(name, this);
- }
-
- /// <summary>
- /// Gets the log level.
- /// </summary>
- /// <param name="severity">The severity.</param>
- /// <returns>LogLevel.</returns>
- /// <exception cref="System.ArgumentException">Unrecognized LogSeverity</exception>
- private LogLevel GetLogLevel(LogSeverity severity)
- {
- switch (severity)
- {
- case LogSeverity.Debug:
- return LogLevel.Debug;
- case LogSeverity.Error:
- return LogLevel.Error;
- case LogSeverity.Fatal:
- return LogLevel.Fatal;
- case LogSeverity.Info:
- return LogLevel.Info;
- case LogSeverity.Warn:
- return LogLevel.Warn;
- default:
- throw new ArgumentException("Unrecognized LogSeverity");
- }
- }
-
- /// <summary>
- /// Reloads the logger.
- /// </summary>
- /// <param name="level">The level.</param>
- public void ReloadLogger(LogSeverity level)
- {
- LogFilePath = Path.Combine(LogDirectory, LogFilePrefix + "-" + decimal.Round(DateTime.Now.Ticks / 10000000) + ".txt");
-
- Directory.CreateDirectory(Path.GetDirectoryName(LogFilePath));
-
- AddFileTarget(LogFilePath, level);
-
- LogSeverity = level;
-
- if (LoggerLoaded != null)
- {
- try
- {
- LoggerLoaded(this, EventArgs.Empty);
- }
- catch (Exception ex)
- {
- GetLogger("Logger").ErrorException("Error in LoggerLoaded event", ex);
- }
- }
- }
-
- /// <summary>
- /// Flushes this instance.
- /// </summary>
- public void Flush()
- {
- LogManager.Flush();
- }
-
-
- public void AddConsoleOutput()
- {
- RemoveTarget("ConsoleTargetWrapper");
-
- var wrapper = new AsyncTargetWrapper ();
- wrapper.Name = "ConsoleTargetWrapper";
-
- var target = new ConsoleTarget()
- {
- Layout = "${level}, ${logger}, ${message}",
- Error = false
- };
-
- target.Name = "ConsoleTarget";
-
- wrapper.WrappedTarget = target;
-
- AddLogTarget(wrapper, LogSeverity);
- }
-
- public void RemoveConsoleOutput()
- {
- RemoveTarget("ConsoleTargetWrapper");
- }
- }
-}
diff --git a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj
deleted file mode 100644
index f3444f01b0..0000000000
--- a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj
+++ /dev/null
@@ -1,153 +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>{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>MediaBrowser.Common.Implementations</RootNamespace>
- <AssemblyName>MediaBrowser.Common.Implementations</AssemblyName>
- <FileAlignment>512</FileAlignment>
- <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
- <ProductVersion>10.0.0</ProductVersion>
- <SchemaVersion>2.0</SchemaVersion>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
- <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>
- <TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
- </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>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release Mono|AnyCPU' ">
- <DebugType>none</DebugType>
- <Optimize>true</Optimize>
- <OutputPath>bin\Release Mono\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
- </PropertyGroup>
- <PropertyGroup>
- <RunPostBuildEvent>Always</RunPostBuildEvent>
- </PropertyGroup>
- <ItemGroup>
- <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
- </Reference>
- <Reference Include="Microsoft.IO.RecyclableMemoryStream, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
- <HintPath>..\packages\Microsoft.IO.RecyclableMemoryStream.1.1.0.0\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll</HintPath>
- <Private>True</Private>
- </Reference>
- <Reference Include="MoreLinq">
- <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
- </Reference>
- <Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
- <HintPath>..\packages\NLog.4.3.8\lib\net45\NLog.dll</HintPath>
- <Private>True</Private>
- </Reference>
- <Reference Include="Patterns.Logging">
- <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
- </Reference>
- <Reference Include="SharpCompress, Version=0.10.2.0, Culture=neutral, PublicKeyToken=beaf6f427e128133, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\ThirdParty\SharpCompress\SharpCompress.dll</HintPath>
- </Reference>
- <Reference Include="SimpleInjector, Version=3.2.2.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
- <HintPath>..\packages\SimpleInjector.3.2.2\lib\net45\SimpleInjector.dll</HintPath>
- <Private>True</Private>
- </Reference>
- <Reference Include="System" />
- <Reference Include="System.Configuration" />
- <Reference Include="System.Core" />
- <Reference Include="Microsoft.CSharp" />
- <Reference Include="System.Data" />
- <Reference Include="System.Net" />
- <Reference Include="System.Text.Json">
- <HintPath>..\ThirdParty\fastjsonparser\System.Text.Json.dll</HintPath>
- </Reference>
- <Reference Include="System.Xml" />
- <Reference Include="ServiceStack.Text">
- <HintPath>..\ThirdParty\ServiceStack.Text\ServiceStack.Text.dll</HintPath>
- </Reference>
- </ItemGroup>
- <ItemGroup>
- <Compile Include="..\SharedVersion.cs">
- <Link>Properties\SharedVersion.cs</Link>
- </Compile>
- <Compile Include="Archiving\ZipClient.cs" />
- <Compile Include="BaseApplicationHost.cs" />
- <Compile Include="BaseApplicationPaths.cs" />
- <Compile Include="Configuration\BaseConfigurationManager.cs" />
- <Compile Include="Configuration\ConfigurationHelper.cs" />
- <Compile Include="Devices\DeviceId.cs" />
- <Compile Include="HttpClientManager\HttpClientInfo.cs" />
- <Compile Include="HttpClientManager\HttpClientManager.cs" />
- <Compile Include="IO\IsoManager.cs" />
- <Compile Include="IO\MemoryStreamProvider.cs" />
- <Compile Include="Logging\LogHelper.cs" />
- <Compile Include="Logging\NLogger.cs" />
- <Compile Include="Logging\NlogManager.cs" />
- <Compile Include="Networking\BaseNetworkManager.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="ScheduledTasks\ScheduledTaskWorker.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="Security\MbAdmin.cs" />
- <Compile Include="Security\MBLicenseFile.cs" />
- <Compile Include="Security\PluginSecurityManager.cs" />
- <Compile Include="Security\RegRecord.cs" />
- <Compile Include="Security\SuppporterInfoResponse.cs" />
- <Compile Include="Serialization\JsonSerializer.cs" />
- <Compile Include="Serialization\XmlSerializer.cs" />
- <Compile Include="Updates\GithubUpdater.cs" />
- <Compile Include="Updates\InstallationManager.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>
- <ItemGroup>
- <None Include="packages.config" />
- </ItemGroup>
- <ItemGroup />
- <Import Project="$(MSBuildToolsPath)\Microsoft.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.Implementations/Security/MBLicenseFile.cs b/MediaBrowser.Common.Implementations/Security/MBLicenseFile.cs
deleted file mode 100644
index 78515cd141..0000000000
--- a/MediaBrowser.Common.Implementations/Security/MBLicenseFile.cs
+++ /dev/null
@@ -1,157 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Security.Cryptography;
-using System.Text;
-
-namespace MediaBrowser.Common.Implementations.Security
-{
- internal class MBLicenseFile
- {
- private readonly IApplicationPaths _appPaths;
-
- public string RegKey
- {
- get { return _regKey; }
- set
- {
- if (value != _regKey)
- {
- //if key is changed - clear out our saved validations
- _updateRecords.Clear();
- _regKey = value;
- }
- }
- }
-
- private string Filename
- {
- get
- {
- return Path.Combine(_appPaths.ConfigurationDirectoryPath, "mb.lic");
- }
- }
-
- private readonly ConcurrentDictionary<Guid, DateTime> _updateRecords = new ConcurrentDictionary<Guid, DateTime>();
- private readonly object _fileLock = new object();
- private string _regKey;
-
- public MBLicenseFile(IApplicationPaths appPaths)
- {
- _appPaths = appPaths;
-
- Load();
- }
-
- private void SetUpdateRecord(Guid key, DateTime value)
- {
- _updateRecords.AddOrUpdate(key, value, (k, v) => value);
- }
-
- public void AddRegCheck(string featureId)
- {
- using (var provider = new MD5CryptoServiceProvider())
- {
- var key = new Guid(provider.ComputeHash(Encoding.Unicode.GetBytes(featureId)));
- var value = DateTime.UtcNow;
-
- SetUpdateRecord(key, value);
- Save();
- }
-
- }
-
- public void RemoveRegCheck(string featureId)
- {
- using (var provider = new MD5CryptoServiceProvider())
- {
- var key = new Guid(provider.ComputeHash(Encoding.Unicode.GetBytes(featureId)));
- DateTime val;
-
- _updateRecords.TryRemove(key, out val);
-
- Save();
- }
-
- }
-
- public DateTime LastChecked(string featureId)
- {
- using (var provider = new MD5CryptoServiceProvider())
- {
- DateTime last;
- _updateRecords.TryGetValue(new Guid(provider.ComputeHash(Encoding.Unicode.GetBytes(featureId))), out last);
-
- // guard agains people just putting a large number in the file
- return last < DateTime.UtcNow ? last : DateTime.MinValue;
- }
- }
-
- private void Load()
- {
- string[] contents = null;
- var licenseFile = Filename;
- lock (_fileLock)
- {
- try
- {
- contents = File.ReadAllLines(licenseFile);
- }
- catch (DirectoryNotFoundException)
- {
- File.Create(licenseFile).Close();
- }
- catch (FileNotFoundException)
- {
- File.Create(licenseFile).Close();
- }
- }
- if (contents != null && contents.Length > 0)
- {
- //first line is reg key
- RegKey = contents[0];
-
- //next is legacy key
- if (contents.Length > 1)
- {
- // Don't need this anymore
- }
-
- //the rest of the lines should be pairs of features and timestamps
- for (var i = 2; i < contents.Length; i = i + 2)
- {
- var feat = Guid.Parse(contents[i]);
-
- SetUpdateRecord(feat, new DateTime(Convert.ToInt64(contents[i + 1])));
- }
- }
- }
-
- public void Save()
- {
- //build our array
- var lines = new List<string>
- {
- RegKey,
-
- // Legacy key
- string.Empty
- };
-
- foreach (var pair in _updateRecords
- .ToList())
- {
- lines.Add(pair.Key.ToString());
- lines.Add(pair.Value.Ticks.ToString(CultureInfo.InvariantCulture));
- }
-
- var licenseFile = Filename;
- Directory.CreateDirectory(Path.GetDirectoryName(licenseFile));
- lock (_fileLock) File.WriteAllLines(licenseFile, lines);
- }
- }
-}
diff --git a/MediaBrowser.Common.Implementations/Security/MbAdmin.cs b/MediaBrowser.Common.Implementations/Security/MbAdmin.cs
deleted file mode 100644
index 76ff92c2eb..0000000000
--- a/MediaBrowser.Common.Implementations/Security/MbAdmin.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-
-namespace MediaBrowser.Common.Implementations.Security
-{
- public class MbAdmin
- {
- public const string HttpUrl = "https://www.mb3admin.com/admin/";
-
- /// <summary>
- /// Leaving as http for now until we get it squared away
- /// </summary>
- public const string HttpsUrl = "https://www.mb3admin.com/admin/";
- }
-}
diff --git a/MediaBrowser.Common.Implementations/Security/SuppporterInfoResponse.cs b/MediaBrowser.Common.Implementations/Security/SuppporterInfoResponse.cs
deleted file mode 100644
index 49c5af8d82..0000000000
--- a/MediaBrowser.Common.Implementations/Security/SuppporterInfoResponse.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-
-namespace MediaBrowser.Common.Implementations.Security
-{
- internal class SuppporterInfoResponse
- {
- public string email { get; set; }
- public string supporterKey { get; set; }
- public int totalRegs { get; set; }
- public int totalMachines { get; set; }
- public string expDate { get; set; }
- public string regDate { get; set; }
- public string planType { get; set; }
- }
-}
diff --git a/MediaBrowser.Common.Implementations/packages.config b/MediaBrowser.Common.Implementations/packages.config
deleted file mode 100644
index 40c727a063..0000000000
--- a/MediaBrowser.Common.Implementations/packages.config
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
- <package id="CommonIO" version="1.0.0.9" targetFramework="net45" />
- <package id="Microsoft.IO.RecyclableMemoryStream" version="1.1.0.0" targetFramework="net45" />
- <package id="morelinq" version="1.4.0" targetFramework="net45" />
- <package id="NLog" version="4.3.8" targetFramework="net45" />
- <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
- <package id="SimpleInjector" version="3.2.2" targetFramework="net45" />
-</packages> \ No newline at end of file
diff --git a/MediaBrowser.Common/Configuration/IApplicationPaths.cs b/MediaBrowser.Common/Configuration/IApplicationPaths.cs
index d3bf033029..d2446ce46d 100644
--- a/MediaBrowser.Common/Configuration/IApplicationPaths.cs
+++ b/MediaBrowser.Common/Configuration/IApplicationPaths.cs
@@ -7,12 +7,6 @@ namespace MediaBrowser.Common.Configuration
public interface IApplicationPaths
{
/// <summary>
- /// Gets the application path.
- /// </summary>
- /// <value>The application path.</value>
- string ApplicationPath { get; }
-
- /// <summary>
/// Gets the path to the program data folder
/// </summary>
/// <value>The program data path.</value>
diff --git a/MediaBrowser.Common/Extensions/BaseExtensions.cs b/MediaBrowser.Common/Extensions/BaseExtensions.cs
index 22dfa378fc..d7f4424faa 100644
--- a/MediaBrowser.Common/Extensions/BaseExtensions.cs
+++ b/MediaBrowser.Common/Extensions/BaseExtensions.cs
@@ -1,9 +1,7 @@
using System;
using System.Globalization;
-using System.Linq;
-using System.Security.Cryptography;
-using System.Text;
using System.Text.RegularExpressions;
+using MediaBrowser.Model.Cryptography;
namespace MediaBrowser.Common.Extensions
{
@@ -12,6 +10,7 @@ namespace MediaBrowser.Common.Extensions
/// </summary>
public static class BaseExtensions
{
+ public static ICryptoProvider CryptographyProvider { get; set; }
/// <summary>
/// Strips the HTML.
@@ -26,15 +25,6 @@ namespace MediaBrowser.Common.Extensions
return Regex.Replace(htmlString, pattern, string.Empty).Trim();
}
- public static string RemoveDiacritics(this string text)
- {
- return String.Concat(
- text.Normalize(NormalizationForm.FormD)
- .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) !=
- UnicodeCategory.NonSpacingMark)
- ).Normalize(NormalizationForm.FormC);
- }
-
/// <summary>
/// Gets the M d5.
/// </summary>
@@ -42,10 +32,7 @@ namespace MediaBrowser.Common.Extensions
/// <returns>Guid.</returns>
public static Guid GetMD5(this string str)
{
- using (var provider = MD5.Create())
- {
- return new Guid(provider.ComputeHash(Encoding.Unicode.GetBytes(str)));
- }
+ return CryptographyProvider.GetMD5(str);
}
/// <summary>
diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs
index c843e828b2..8353eccdde 100644
--- a/MediaBrowser.Common/IApplicationHost.cs
+++ b/MediaBrowser.Common/IApplicationHost.cs
@@ -142,7 +142,7 @@ namespace MediaBrowser.Common
/// Gets the plugins.
/// </summary>
/// <value>The plugins.</value>
- IEnumerable<IPlugin> Plugins { get; }
+ IPlugin[] Plugins { get; }
/// <summary>
/// Removes the plugin.
diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj
index a46aaf9f7b..eb082f707a 100644
--- a/MediaBrowser.Common/MediaBrowser.Common.csproj
+++ b/MediaBrowser.Common/MediaBrowser.Common.csproj
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<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>
@@ -13,7 +13,10 @@
<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>
@@ -24,7 +27,6 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>AnyCPU</PlatformTarget>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
@@ -33,7 +35,6 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release Mono|AnyCPU' ">
<DebugType>none</DebugType>
@@ -42,14 +43,8 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<ItemGroup>
- <Reference Include="System" />
- <Reference Include="System.Core" />
- <Reference Include="Microsoft.CSharp" />
- </ItemGroup>
- <ItemGroup>
<Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link>
</Compile>
@@ -60,9 +55,7 @@
<Compile Include="Extensions\BaseExtensions.cs" />
<Compile Include="Extensions\ResourceNotFoundException.cs" />
<Compile Include="IDependencyContainer.cs" />
- <Compile Include="IO\IMemoryStreamProvider.cs" />
<Compile Include="IO\ProgressStream.cs" />
- <Compile Include="IO\StreamDefaults.cs" />
<Compile Include="Configuration\IApplicationPaths.cs" />
<Compile Include="Net\HttpRequestOptions.cs" />
<Compile Include="Net\HttpResponseInfo.cs" />
@@ -72,26 +65,12 @@
<Compile Include="Plugins\IDependencyModule.cs" />
<Compile Include="Plugins\IPlugin.cs" />
<Compile Include="Progress\ActionableProgress.cs" />
- <Compile Include="ScheduledTasks\IConfigurableScheduledTask.cs" />
- <Compile Include="ScheduledTasks\IHasKey.cs" />
- <Compile Include="ScheduledTasks\IScheduledTask.cs" />
- <Compile Include="ScheduledTasks\IScheduledTaskWorker.cs" />
- <Compile Include="ScheduledTasks\ITaskManager.cs" />
- <Compile Include="ScheduledTasks\ITaskTrigger.cs" />
- <Compile Include="ScheduledTasks\ScheduledTaskHelpers.cs" />
- <Compile Include="ScheduledTasks\StartupTrigger.cs" />
- <Compile Include="ScheduledTasks\SystemEventTrigger.cs" />
<Compile Include="Plugins\BasePlugin.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="ScheduledTasks\DailyTrigger.cs" />
- <Compile Include="ScheduledTasks\IntervalTrigger.cs" />
- <Compile Include="ScheduledTasks\TaskCompletionEventArgs.cs" />
- <Compile Include="ScheduledTasks\TaskExecutionOptions.cs" />
- <Compile Include="ScheduledTasks\WeeklyTrigger.cs" />
<Compile Include="Security\IRequiresRegistration.cs" />
<Compile Include="Security\ISecurityManager.cs" />
<Compile Include="Security\PaymentRequiredException.cs" />
- <Compile Include="Threading\PeriodicTimer.cs" />
+ <Compile Include="Updates\GithubUpdater.cs" />
<Compile Include="Updates\IInstallationManager.cs" />
<Compile Include="Updates\InstallationEventArgs.cs" />
<Compile Include="Updates\InstallationFailedEventArgs.cs" />
@@ -102,8 +81,10 @@
<Name>MediaBrowser.Model</Name>
</ProjectReference>
</ItemGroup>
- <ItemGroup />
- <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <ItemGroup>
+ <None Include="project.json" />
+ </ItemGroup>
+ <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent />
</PropertyGroup>
diff --git a/MediaBrowser.Common/MediaBrowser.Common.nuget.targets b/MediaBrowser.Common/MediaBrowser.Common.nuget.targets
new file mode 100644
index 0000000000..e69ce0e64f
--- /dev/null
+++ b/MediaBrowser.Common/MediaBrowser.Common.nuget.targets
@@ -0,0 +1,6 @@
+<?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
index 1a7f414a77..e1ecd6595e 100644
--- a/MediaBrowser.Common/Net/HttpRequestOptions.cs
+++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs
@@ -17,7 +17,7 @@ namespace MediaBrowser.Common.Net
/// <value>The URL.</value>
public string Url { get; set; }
- public DecompressionMethods? DecompressionMethod { get; set; }
+ public CompressionMethod? DecompressionMethod { get; set; }
/// <summary>
/// Gets or sets the accept header.
@@ -116,7 +116,6 @@ namespace MediaBrowser.Common.Net
public HttpRequestOptions()
{
EnableHttpCompression = true;
- BufferContent = true;
RequestHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
@@ -142,4 +141,10 @@ namespace MediaBrowser.Common.Net
None = 0,
Unconditional = 1
}
+
+ public enum CompressionMethod
+ {
+ Deflate,
+ Gzip
+ }
}
diff --git a/MediaBrowser.Common/Net/HttpResponseInfo.cs b/MediaBrowser.Common/Net/HttpResponseInfo.cs
index 890c893e61..ed941a4474 100644
--- a/MediaBrowser.Common/Net/HttpResponseInfo.cs
+++ b/MediaBrowser.Common/Net/HttpResponseInfo.cs
@@ -1,5 +1,5 @@
using System;
-using System.Collections.Specialized;
+using System.Collections.Generic;
using System.IO;
using System.Net;
@@ -50,16 +50,18 @@ namespace MediaBrowser.Common.Net
/// Gets or sets the headers.
/// </summary>
/// <value>The headers.</value>
- public NameValueCollection Headers { get; set; }
+ 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()
diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs
index de63ddd513..3bc22c07f5 100644
--- a/MediaBrowser.Common/Net/INetworkManager.cs
+++ b/MediaBrowser.Common/Net/INetworkManager.cs
@@ -2,18 +2,13 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using System.Collections.Generic;
using System.Net;
+using System.Threading.Tasks;
namespace MediaBrowser.Common.Net
{
public interface INetworkManager
{
/// <summary>
- /// Gets the machine's local ip address
- /// </summary>
- /// <returns>IPAddress.</returns>
- IEnumerable<IPAddress> GetLocalIpAddresses();
-
- /// <summary>
/// Gets a random port number that is currently available
/// </summary>
/// <returns>System.Int32.</returns>
@@ -46,24 +41,18 @@ namespace MediaBrowser.Common.Net
IEnumerable<FileSystemEntryInfo> GetNetworkDevices();
/// <summary>
- /// Parses the specified endpointstring.
- /// </summary>
- /// <param name="endpointstring">The endpointstring.</param>
- /// <returns>IPEndPoint.</returns>
- IPEndPoint Parse(string endpointstring);
-
- /// <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);
- /// <summary>
- /// Generates a self signed certificate at the locatation specified by <paramref name="certificatePath"/>.
- /// </summary>
- /// <param name="certificatePath">The path to generate the certificate.</param>
- /// <param name="hostname">The common name for the certificate.</param>
- void GenerateSelfSignedSslCertificate(string certificatePath, string hostname);
+ 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
index b75accf9b0..95e076096c 100644
--- a/MediaBrowser.Common/Plugins/BasePlugin.cs
+++ b/MediaBrowser.Common/Plugins/BasePlugin.cs
@@ -3,8 +3,6 @@ using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Serialization;
using System;
using System.IO;
-using System.Reflection;
-using System.Runtime.InteropServices;
namespace MediaBrowser.Common.Plugins
{
@@ -12,7 +10,7 @@ namespace MediaBrowser.Common.Plugins
/// Provides a common base class for all plugins
/// </summary>
/// <typeparam name="TConfigurationType">The type of the T configuration type.</typeparam>
- public abstract class BasePlugin<TConfigurationType> : IPlugin
+ public abstract class BasePlugin<TConfigurationType> : IPlugin, IPluginAssembly
where TConfigurationType : BasePluginConfiguration
{
/// <summary>
@@ -57,69 +55,42 @@ namespace MediaBrowser.Common.Plugins
get { return typeof(TConfigurationType); }
}
- /// <summary>
- /// The _assembly name
- /// </summary>
- private AssemblyName _assemblyName;
- /// <summary>
- /// Gets the name of the assembly.
- /// </summary>
- /// <value>The name of the assembly.</value>
- protected AssemblyName AssemblyName
+ public void SetAttributes(string assemblyFilePath, string assemblyFileName, Version assemblyVersion, Guid assemblyId)
{
- get
- {
- return _assemblyName ?? (_assemblyName = GetType().Assembly.GetName());
- }
+ AssemblyFilePath = assemblyFilePath;
+ AssemblyFileName = assemblyFileName;
+ Version = assemblyVersion;
+ Id = assemblyId;
}
- /// <summary>
- /// The _unique id
- /// </summary>
- private Guid? _uniqueId;
+ 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 Guid Id
- {
- get
- {
-
- if (!_uniqueId.HasValue)
- {
- var attribute = (GuidAttribute)GetType().Assembly.GetCustomAttributes(typeof(GuidAttribute), true)[0];
- _uniqueId = new Guid(attribute.Value);
- }
-
- return _uniqueId.Value;
- }
- }
+ public Guid Id { get; private set; }
/// <summary>
/// Gets the plugin version
/// </summary>
/// <value>The version.</value>
- public Version Version
- {
- get
- {
- return AssemblyName.Version;
- }
- }
+ public Version Version { get; private set; }
/// <summary>
/// Gets the name the assembly file
/// </summary>
/// <value>The name of the assembly file.</value>
- public string AssemblyFileName
- {
- get
- {
- return AssemblyName.Name + ".dll";
- }
- }
+ protected string AssemblyFileName { get; private set; }
/// <summary>
/// Gets the last date modified of the configuration
@@ -132,19 +103,7 @@ namespace MediaBrowser.Common.Plugins
// Ensure it's been lazy loaded
var config = Configuration;
- return File.GetLastWriteTimeUtc(ConfigurationFilePath);
- }
- }
-
- /// <summary>
- /// Gets the last date modified of the plugin
- /// </summary>
- /// <value>The assembly date last modified.</value>
- public DateTime AssemblyDateLastModified
- {
- get
- {
- return File.GetLastWriteTimeUtc(AssemblyFilePath);
+ return _dateModifiedFn(ConfigurationFilePath);
}
}
@@ -152,13 +111,7 @@ namespace MediaBrowser.Common.Plugins
/// Gets the path to the assembly file
/// </summary>
/// <value>The assembly file path.</value>
- public string AssemblyFilePath
- {
- get
- {
- return Path.Combine(ApplicationPaths.PluginsPath, AssemblyFileName);
- }
- }
+ public string AssemblyFilePath { get; private set; }
/// <summary>
/// The _configuration sync lock
@@ -203,14 +156,6 @@ namespace MediaBrowser.Common.Plugins
{
return (TConfigurationType)XmlSerializer.DeserializeFromFile(typeof(TConfigurationType), path);
}
- catch (DirectoryNotFoundException)
- {
- return (TConfigurationType)Activator.CreateInstance(typeof(TConfigurationType));
- }
- catch (FileNotFoundException)
- {
- return (TConfigurationType)Activator.CreateInstance(typeof(TConfigurationType));
- }
catch
{
return (TConfigurationType)Activator.CreateInstance(typeof(TConfigurationType));
@@ -239,10 +184,6 @@ namespace MediaBrowser.Common.Plugins
}
/// <summary>
- /// The _data folder path
- /// </summary>
- private string _dataFolderPath;
- /// <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>
@@ -250,16 +191,9 @@ namespace MediaBrowser.Common.Plugins
{
get
{
- if (_dataFolderPath == null)
- {
- // Give the folder name the same name as the config file name
- // We can always make this configurable if/when needed
- _dataFolderPath = Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(ConfigurationFileName));
-
- Directory.CreateDirectory(_dataFolderPath);
- }
-
- return _dataFolderPath;
+ // 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));
}
}
@@ -272,8 +206,6 @@ namespace MediaBrowser.Common.Plugins
{
ApplicationPaths = applicationPaths;
XmlSerializer = xmlSerializer;
-
- IsFirstRun = !File.Exists(ConfigurationFilePath);
}
/// <summary>
@@ -288,7 +220,7 @@ namespace MediaBrowser.Common.Plugins
{
lock (_configurationSaveLock)
{
- Directory.CreateDirectory(Path.GetDirectoryName(ConfigurationFilePath));
+ _directoryCreateFn(Path.GetDirectoryName(ConfigurationFilePath));
XmlSerializer.SerializeToFile(Configuration, ConfigurationFilePath);
}
@@ -349,4 +281,9 @@ namespace MediaBrowser.Common.Plugins
get { return Configuration; }
}
}
+
+ public interface IPluginAssembly
+ {
+ void SetAttributes(string assemblyFilePath, string assemblyFileName, Version assemblyVersion, Guid assemblyId);
+ }
}
diff --git a/MediaBrowser.Common/Plugins/IPlugin.cs b/MediaBrowser.Common/Plugins/IPlugin.cs
index 5a1252d1cd..999a783fdf 100644
--- a/MediaBrowser.Common/Plugins/IPlugin.cs
+++ b/MediaBrowser.Common/Plugins/IPlugin.cs
@@ -39,12 +39,6 @@ namespace MediaBrowser.Common.Plugins
Version Version { get; }
/// <summary>
- /// Gets the name the assembly file
- /// </summary>
- /// <value>The name of the assembly file.</value>
- string AssemblyFileName { 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>
@@ -57,12 +51,6 @@ namespace MediaBrowser.Common.Plugins
DateTime ConfigurationDateLastModified { get; }
/// <summary>
- /// Gets the last date modified of the plugin
- /// </summary>
- /// <value>The assembly date last modified.</value>
- DateTime AssemblyDateLastModified { get; }
-
- /// <summary>
/// Gets the path to the assembly file
/// </summary>
/// <value>The assembly file path.</value>
@@ -116,5 +104,7 @@ namespace MediaBrowser.Common.Plugins
/// 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/Properties/AssemblyInfo.cs b/MediaBrowser.Common/Properties/AssemblyInfo.cs
index d7bbb6f3ab..09fd68f93a 100644
--- a/MediaBrowser.Common/Properties/AssemblyInfo.cs
+++ b/MediaBrowser.Common/Properties/AssemblyInfo.cs
@@ -18,9 +18,6 @@ using System.Runtime.InteropServices;
// 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("cdec1bb7-6ffd-409f-b41f-0524a73df9be")]
-
// Version information for an assembly consists of the following four values:
//
// Major Version
diff --git a/MediaBrowser.Common/ScheduledTasks/IHasKey.cs b/MediaBrowser.Common/ScheduledTasks/IHasKey.cs
deleted file mode 100644
index 5736cb6162..0000000000
--- a/MediaBrowser.Common/ScheduledTasks/IHasKey.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace MediaBrowser.Common.ScheduledTasks
-{
- public interface IHasKey
- {
- string Key { get; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Common/ScheduledTasks/ScheduledTaskHelpers.cs b/MediaBrowser.Common/ScheduledTasks/ScheduledTaskHelpers.cs
deleted file mode 100644
index 4ad33341ad..0000000000
--- a/MediaBrowser.Common/ScheduledTasks/ScheduledTaskHelpers.cs
+++ /dev/null
@@ -1,194 +0,0 @@
-using MediaBrowser.Model.Tasks;
-using System;
-using System.Linq;
-
-namespace MediaBrowser.Common.ScheduledTasks
-{
- /// <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 = null;
-
- var hasKey = task.ScheduledTask as IHasKey;
-
- if (hasKey != null)
- {
- key = hasKey.Key;
- }
-
- var triggers = task.Triggers
- .Select(GetTriggerInfo)
- .OrderBy(i => i.Type)
- .ThenBy(i => i.DayOfWeek ?? DayOfWeek.Sunday)
- .ThenBy(i => i.TimeOfDayTicks ?? 0)
- .ToList();
-
- return new TaskInfo
- {
- Name = task.Name,
- CurrentProgressPercentage = task.CurrentProgress,
- State = task.State,
- Id = task.Id,
- LastExecutionResult = task.LastExecutionResult,
-
- Triggers = triggers,
-
- Description = task.Description,
- Category = task.Category,
- IsHidden = isHidden,
- Key = key
- };
- }
-
- /// <summary>
- /// Gets the trigger info.
- /// </summary>
- /// <param name="trigger">The trigger.</param>
- /// <returns>TaskTriggerInfo.</returns>
- public static TaskTriggerInfo GetTriggerInfo(ITaskTrigger trigger)
- {
- var info = new TaskTriggerInfo
- {
- Type = trigger.GetType().Name
- };
-
- var dailyTrigger = trigger as DailyTrigger;
-
- if (dailyTrigger != null)
- {
- info.TimeOfDayTicks = dailyTrigger.TimeOfDay.Ticks;
- }
-
- var weeklyTaskTrigger = trigger as WeeklyTrigger;
-
- if (weeklyTaskTrigger != null)
- {
- info.TimeOfDayTicks = weeklyTaskTrigger.TimeOfDay.Ticks;
- info.DayOfWeek = weeklyTaskTrigger.DayOfWeek;
- }
-
- var intervalTaskTrigger = trigger as IntervalTrigger;
-
- if (intervalTaskTrigger != null)
- {
- info.IntervalTicks = intervalTaskTrigger.Interval.Ticks;
- }
-
- var systemEventTrigger = trigger as SystemEventTrigger;
-
- if (systemEventTrigger != null)
- {
- info.SystemEvent = systemEventTrigger.SystemEvent;
- }
-
- if (trigger.TaskOptions != null)
- {
- info.MaxRuntimeMs = trigger.TaskOptions.MaxRuntimeMs;
- }
-
- return info;
- }
-
- /// <summary>
- /// Converts a TaskTriggerInfo into a concrete BaseTaskTrigger
- /// </summary>
- /// <param name="info">The info.</param>
- /// <returns>BaseTaskTrigger.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- /// <exception cref="System.ArgumentException">Invalid trigger type: + info.Type</exception>
- public static ITaskTrigger GetTrigger(TaskTriggerInfo info)
- {
- var options = new TaskExecutionOptions
- {
- MaxRuntimeMs = info.MaxRuntimeMs
- };
-
- if (info.Type.Equals(typeof(DailyTrigger).Name, StringComparison.OrdinalIgnoreCase))
- {
- if (!info.TimeOfDayTicks.HasValue)
- {
- throw new ArgumentNullException();
- }
-
- return new DailyTrigger
- {
- TimeOfDay = TimeSpan.FromTicks(info.TimeOfDayTicks.Value),
- TaskOptions = options
- };
- }
-
- if (info.Type.Equals(typeof(WeeklyTrigger).Name, StringComparison.OrdinalIgnoreCase))
- {
- if (!info.TimeOfDayTicks.HasValue)
- {
- throw new ArgumentNullException();
- }
-
- if (!info.DayOfWeek.HasValue)
- {
- throw new ArgumentNullException();
- }
-
- return new WeeklyTrigger
- {
- TimeOfDay = TimeSpan.FromTicks(info.TimeOfDayTicks.Value),
- DayOfWeek = info.DayOfWeek.Value,
- TaskOptions = options
- };
- }
-
- if (info.Type.Equals(typeof(IntervalTrigger).Name, StringComparison.OrdinalIgnoreCase))
- {
- if (!info.IntervalTicks.HasValue)
- {
- throw new ArgumentNullException();
- }
-
- return new IntervalTrigger
- {
- Interval = TimeSpan.FromTicks(info.IntervalTicks.Value),
- TaskOptions = options
- };
- }
-
- if (info.Type.Equals(typeof(SystemEventTrigger).Name, StringComparison.OrdinalIgnoreCase))
- {
- if (!info.SystemEvent.HasValue)
- {
- throw new ArgumentNullException();
- }
-
- return new SystemEventTrigger
- {
- SystemEvent = info.SystemEvent.Value,
- TaskOptions = options
- };
- }
-
- if (info.Type.Equals(typeof(StartupTrigger).Name, StringComparison.OrdinalIgnoreCase))
- {
- return new StartupTrigger();
- }
-
- throw new ArgumentException("Unrecognized trigger type: " + info.Type);
- }
- }
-}
diff --git a/MediaBrowser.Common/Threading/PeriodicTimer.cs b/MediaBrowser.Common/Threading/PeriodicTimer.cs
deleted file mode 100644
index 75ccada4ef..0000000000
--- a/MediaBrowser.Common/Threading/PeriodicTimer.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-using System;
-using System.Threading;
-using Microsoft.Win32;
-
-namespace MediaBrowser.Common.Threading
-{
- public class PeriodicTimer : IDisposable
- {
- public Action<object> Callback { get; set; }
- private Timer _timer;
- private readonly object _state;
- private readonly object _timerLock = new object();
- private readonly TimeSpan _period;
-
- public PeriodicTimer(Action<object> callback, object state, TimeSpan dueTime, TimeSpan period)
- {
- if (callback == null)
- {
- throw new ArgumentNullException("callback");
- }
-
- Callback = callback;
- _period = period;
- _state = state;
-
- StartTimer(dueTime);
- }
-
- void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
- {
- if (e.Mode == PowerModes.Resume)
- {
- DisposeTimer();
- StartTimer(Timeout.InfiniteTimeSpan);
- }
- }
-
- private void TimerCallback(object state)
- {
- Callback(state);
- }
-
- private void StartTimer(TimeSpan dueTime)
- {
- lock (_timerLock)
- {
- _timer = new Timer(TimerCallback, _state, dueTime, _period);
-
- SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
- }
- }
-
- private void DisposeTimer()
- {
- SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
-
- lock (_timerLock)
- {
- if (_timer != null)
- {
- _timer.Dispose();
- _timer = null;
- }
- }
- }
-
- public void Dispose()
- {
- DisposeTimer();
- }
- }
-}
diff --git a/MediaBrowser.Common.Implementations/Updates/GithubUpdater.cs b/MediaBrowser.Common/Updates/GithubUpdater.cs
index 371c2ea113..c5000391d8 100644
--- a/MediaBrowser.Common.Implementations/Updates/GithubUpdater.cs
+++ b/MediaBrowser.Common/Updates/GithubUpdater.cs
@@ -8,7 +8,7 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Updates;
-namespace MediaBrowser.Common.Implementations.Updates
+namespace MediaBrowser.Common.Updates
{
public class GithubUpdater
{
diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs
index f7a202f972..636526567e 100644
--- a/MediaBrowser.Common/Updates/IInstallationManager.cs
+++ b/MediaBrowser.Common/Updates/IInstallationManager.cs
@@ -2,7 +2,6 @@
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Updates;
using System;
-using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@@ -24,7 +23,7 @@ namespace MediaBrowser.Common.Updates
/// <summary>
/// The completed installations
/// </summary>
- ConcurrentBag<InstallationInfo> CompletedInstallations { get; set; }
+ IEnumerable<InstallationInfo> CompletedInstallations { get; }
/// <summary>
/// Occurs when [plugin uninstalled].
diff --git a/MediaBrowser.Common/project.json b/MediaBrowser.Common/project.json
new file mode 100644
index 0000000000..fbbe9eaf32
--- /dev/null
+++ b/MediaBrowser.Common/project.json
@@ -0,0 +1,17 @@
+{
+ "frameworks":{
+ "netstandard1.6":{
+ "dependencies":{
+ "NETStandard.Library":"1.6.0",
+ }
+ },
+ ".NETPortable,Version=v4.5,Profile=Profile7":{
+ "buildOptions": {
+ "define": [ ]
+ },
+ "frameworkAssemblies":{
+
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs
index e0b7204b87..a2590dec72 100644
--- a/MediaBrowser.Controller/Channels/Channel.cs
+++ b/MediaBrowser.Controller/Channels/Channel.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Querying;
using System;
using System.Linq;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
using System.Threading;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Controller/Dlna/ControlRequest.cs b/MediaBrowser.Controller/Dlna/ControlRequest.cs
index 7020cc0d9f..ff951ec9eb 100644
--- a/MediaBrowser.Controller/Dlna/ControlRequest.cs
+++ b/MediaBrowser.Controller/Dlna/ControlRequest.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.IO;
namespace MediaBrowser.Controller.Dlna
{
@@ -6,7 +7,7 @@ namespace MediaBrowser.Controller.Dlna
{
public IDictionary<string, string> Headers { get; set; }
- public string InputXml { get; set; }
+ public Stream InputXml { get; set; }
public string TargetServerUuId { get; set; }
diff --git a/MediaBrowser.Controller/Dlna/IDeviceDiscovery.cs b/MediaBrowser.Controller/Dlna/IDeviceDiscovery.cs
deleted file mode 100644
index d2c5b9e4e8..0000000000
--- a/MediaBrowser.Controller/Dlna/IDeviceDiscovery.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Net;
-using MediaBrowser.Model.Events;
-
-namespace MediaBrowser.Controller.Dlna
-{
- public interface IDeviceDiscovery
- {
- event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscovered;
- event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceLeft;
- }
-
- public class UpnpDeviceInfo
- {
- public Uri Location { get; set; }
- public Dictionary<string, string> Headers { get; set; }
- public IPEndPoint LocalEndPoint { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Dlna/SsdpMessageEventArgs.cs b/MediaBrowser.Controller/Dlna/SsdpMessageEventArgs.cs
deleted file mode 100644
index 804f34311a..0000000000
--- a/MediaBrowser.Controller/Dlna/SsdpMessageEventArgs.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Net;
-
-namespace MediaBrowser.Controller.Dlna
-{
- public class SsdpMessageEventArgs
- {
- public string Method { get; set; }
-
- public EndPoint EndPoint { get; set; }
-
- public Dictionary<string, string> Headers { get; set; }
-
- public IPEndPoint LocalEndPoint { get; set; }
- public byte[] Message { get; set; }
-
- public SsdpMessageEventArgs()
- {
- Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- }
- }
-}
diff --git a/Emby.Drawing/IImageEncoder.cs b/MediaBrowser.Controller/Drawing/IImageEncoder.cs
index 73b5398993..de1909e542 100644
--- a/Emby.Drawing/IImageEncoder.cs
+++ b/MediaBrowser.Controller/Drawing/IImageEncoder.cs
@@ -1,8 +1,7 @@
-using MediaBrowser.Controller.Drawing;
+using System;
using MediaBrowser.Model.Drawing;
-using System;
-namespace Emby.Drawing
+namespace MediaBrowser.Controller.Drawing
{
public interface IImageEncoder : IDisposable
{
diff --git a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs
index 3fd8d84dd5..f4b3d94554 100644
--- a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs
+++ b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs
@@ -35,6 +35,7 @@ namespace MediaBrowser.Controller.Drawing
public bool AddPlayedIndicator { get; set; }
public int? UnplayedCount { get; set; }
+ public int? Blur { get; set; }
public double PercentPlayed { get; set; }
@@ -84,6 +85,7 @@ namespace MediaBrowser.Controller.Drawing
!AddPlayedIndicator &&
PercentPlayed.Equals(0) &&
!UnplayedCount.HasValue &&
+ !Blur.HasValue &&
string.IsNullOrEmpty(BackgroundColor) &&
string.IsNullOrEmpty(ForegroundLayer);
}
diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs
index 7b769deb3e..a6b9d860a0 100644
--- a/MediaBrowser.Controller/Entities/AggregateFolder.cs
+++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs
@@ -4,11 +4,12 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
-using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Controller.Entities
{
diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs
index 56c3248f33..e4f638cb6f 100644
--- a/MediaBrowser.Controller/Entities/Audio/Audio.cs
+++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs
@@ -7,10 +7,10 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
-using System.Runtime.Serialization;
using System.Threading;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Channels;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Controller.Entities.Audio
{
@@ -22,14 +22,10 @@ namespace MediaBrowser.Controller.Entities.Audio
IHasArtist,
IHasMusicGenres,
IHasLookupInfo<SongInfo>,
- IHasMediaSources,
- IThemeMedia
+ IHasMediaSources
{
public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
- public int? TotalBitrate { get; set; }
- public ExtraType? ExtraType { get; set; }
-
/// <summary>
/// Gets or sets the artist.
/// </summary>
@@ -39,15 +35,6 @@ namespace MediaBrowser.Controller.Entities.Audio
public List<string> AlbumArtists { get; set; }
[IgnoreDataMember]
- public bool IsThemeMedia
- {
- get
- {
- return ExtraType.HasValue && ExtraType.Value == Model.Entities.ExtraType.ThemeSong;
- }
- }
-
- [IgnoreDataMember]
public override bool EnableRefreshOnDateModifiedChange
{
get { return true; }
@@ -75,6 +62,12 @@ namespace MediaBrowser.Controller.Entities.Audio
}
[IgnoreDataMember]
+ public override bool SupportsInheritedParentImages
+ {
+ get { return true; }
+ }
+
+ [IgnoreDataMember]
protected override bool SupportsOwnedItems
{
get
diff --git a/MediaBrowser.Controller/Entities/Audio/AudioPodcast.cs b/MediaBrowser.Controller/Entities/Audio/AudioPodcast.cs
index 9072e10947..8c820d3677 100644
--- a/MediaBrowser.Controller/Entities/Audio/AudioPodcast.cs
+++ b/MediaBrowser.Controller/Entities/Audio/AudioPodcast.cs
@@ -1,6 +1,16 @@
-namespace MediaBrowser.Controller.Entities.Audio
+using MediaBrowser.Model.Serialization;
+
+namespace MediaBrowser.Controller.Entities.Audio
{
public class AudioPodcast : Audio
{
+ [IgnoreDataMember]
+ public override bool SupportsPositionTicksResume
+ {
+ get
+ {
+ return true;
+ }
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
index 3f457d89a9..ffdbba6f2e 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
@@ -5,7 +5,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Users;
using System.Collections.Generic;
using System.Linq;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Library;
@@ -33,6 +33,12 @@ namespace MediaBrowser.Controller.Entities.Audio
}
[IgnoreDataMember]
+ public override bool SupportsInheritedParentImages
+ {
+ get { return true; }
+ }
+
+ [IgnoreDataMember]
public MusicArtist MusicArtist
{
get
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
index a31f04fc4f..fd20f2ab44 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
@@ -6,10 +6,12 @@ using MediaBrowser.Model.Users;
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Runtime.Serialization;
+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
{
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
index bd991d9f47..d75b31f6ae 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
@@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Extensions;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Controller.Entities.Audio
{
@@ -29,6 +31,15 @@ namespace MediaBrowser.Controller.Entities.Audio
get { return true; }
}
+ [IgnoreDataMember]
+ public override bool SupportsAncestors
+ {
+ get
+ {
+ return false;
+ }
+ }
+
/// <summary>
/// Returns the folder containing the item.
/// If the item is a folder, it returns the folder itself
diff --git a/MediaBrowser.Controller/Entities/AudioBook.cs b/MediaBrowser.Controller/Entities/AudioBook.cs
new file mode 100644
index 0000000000..efeb9b497d
--- /dev/null
+++ b/MediaBrowser.Controller/Entities/AudioBook.cs
@@ -0,0 +1,64 @@
+using System;
+using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Entities;
+
+namespace MediaBrowser.Controller.Entities
+{
+ public class AudioBook : Audio.Audio, IHasSeries
+ {
+ [IgnoreDataMember]
+ public override bool SupportsPositionTicksResume
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ [IgnoreDataMember]
+ public string SeriesPresentationUniqueKey { get; set; }
+ [IgnoreDataMember]
+ public string SeriesName { get; set; }
+ [IgnoreDataMember]
+ public Guid? SeriesId { get; set; }
+ [IgnoreDataMember]
+ public string SeriesSortName { get; set; }
+
+ public string FindSeriesSortName()
+ {
+ return SeriesSortName;
+ }
+ public string FindSeriesName()
+ {
+ return SeriesName;
+ }
+ public string FindSeriesPresentationUniqueKey()
+ {
+ return SeriesPresentationUniqueKey;
+ }
+
+ [IgnoreDataMember]
+ public override bool EnableRefreshOnDateModifiedChange
+ {
+ get { return true; }
+ }
+
+ 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
index e1a7741c94..2aa53d6515 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -5,7 +5,6 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
@@ -19,14 +18,20 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
-using System.Runtime.Serialization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Extensions;
+using MediaBrowser.Controller.IO;
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
{
@@ -138,6 +143,15 @@ namespace MediaBrowser.Controller.Entities
}
}
+ [IgnoreDataMember]
+ public virtual bool SupportsPositionTicksResume
+ {
+ get
+ {
+ return false;
+ }
+ }
+
public bool DetectIsInMixedFolder()
{
if (SupportsIsInMixedFolderDetection)
@@ -199,6 +213,19 @@ namespace MediaBrowser.Controller.Entities
get { return PremiereDate.HasValue && PremiereDate.Value.ToLocalTime().Date >= DateTime.Now.Date; }
}
+ public int? TotalBitrate { get; set; }
+ 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>
@@ -279,28 +306,11 @@ namespace MediaBrowser.Controller.Entities
/// If this content came from an external service, the id of the content on that service
/// </summary>
[IgnoreDataMember]
- public string ExternalId
- {
- get { return this.GetProviderId("ProviderExternalId"); }
- set
- {
- this.SetProviderId("ProviderExternalId", value);
- }
- }
+ public string ExternalId { get; set; }
[IgnoreDataMember]
public string ExternalSeriesId { get; set; }
- [IgnoreDataMember]
- public string ExternalSeriesIdLegacy
- {
- get { return this.GetProviderId("ProviderExternalSeriesId"); }
- set
- {
- this.SetProviderId("ProviderExternalSeriesId", value);
- }
- }
-
/// <summary>
/// Gets or sets the etag.
/// </summary>
@@ -1031,7 +1041,7 @@ namespace MediaBrowser.Controller.Entities
audio = dbItem;
}
- audio.ExtraType = ExtraType.ThemeSong;
+ audio.ExtraType = MediaBrowser.Model.Entities.ExtraType.ThemeSong;
return audio;
@@ -1061,7 +1071,7 @@ namespace MediaBrowser.Controller.Entities
item = dbItem;
}
- item.ExtraType = ExtraType.ThemeVideo;
+ item.ExtraType = MediaBrowser.Model.Entities.ExtraType.ThemeVideo;
return item;
@@ -1211,7 +1221,7 @@ namespace MediaBrowser.Controller.Entities
if (!i.IsThemeMedia)
{
- i.ExtraType = ExtraType.ThemeVideo;
+ i.ExtraType = MediaBrowser.Model.Entities.ExtraType.ThemeVideo;
subOptions.ForceSave = true;
}
@@ -1241,7 +1251,7 @@ namespace MediaBrowser.Controller.Entities
if (!i.IsThemeMedia)
{
- i.ExtraType = ExtraType.ThemeSong;
+ i.ExtraType = MediaBrowser.Model.Entities.ExtraType.ThemeSong;
subOptions.ForceSave = true;
}
@@ -1569,6 +1579,12 @@ namespace MediaBrowser.Controller.Entities
return IsVisibleStandaloneInternal(user, true);
}
+ [IgnoreDataMember]
+ public virtual bool SupportsInheritedParentImages
+ {
+ get { return false; }
+ }
+
protected bool IsVisibleStandaloneInternal(User user, bool checkFolders)
{
if (!IsVisible(user))
@@ -1875,19 +1891,7 @@ namespace MediaBrowser.Controller.Entities
if (info.IsLocalFile)
{
- // Delete the source file
- var currentFile = new FileInfo(info.Path);
-
- // Deletion will fail if the file is hidden so remove the attribute first
- if (currentFile.Exists)
- {
- if ((currentFile.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
- {
- currentFile.Attributes &= ~FileAttributes.Hidden;
- }
-
- FileSystem.DeleteFile(currentFile.FullName);
- }
+ FileSystem.DeleteFile(info.Path);
}
return UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
@@ -2146,7 +2150,7 @@ namespace MediaBrowser.Controller.Entities
{
MetadataCountryCode = GetPreferredMetadataCountryCode(),
MetadataLanguage = GetPreferredMetadataLanguage(),
- Name = Name,
+ Name = GetNameForMetadataLookup(),
ProviderIds = ProviderIds,
IndexNumber = IndexNumber,
ParentIndexNumber = ParentIndexNumber,
@@ -2155,6 +2159,11 @@ namespace MediaBrowser.Controller.Entities
};
}
+ 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>
@@ -2183,7 +2192,7 @@ namespace MediaBrowser.Controller.Entities
return path;
}
- public virtual Task FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, BaseItemDto itemDto, User user)
+ public virtual Task FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, BaseItemDto itemDto, User user, List<ItemFields> itemFields)
{
if (RunTimeTicks.HasValue)
{
@@ -2336,17 +2345,25 @@ namespace MediaBrowser.Controller.Entities
{
get
{
- if (GetParent() is AggregateFolder || this is BasePluginFolder || this is Channel)
+ if (this is BasePluginFolder || this is Channel)
{
return true;
}
var view = this as UserView;
- if (view != null && string.Equals(view.ViewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase))
+ if (view != null)
{
- return true;
+ if (string.Equals(view.ViewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ if (string.Equals(view.ViewType, CollectionType.Channels, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
}
- if (view != null && string.Equals(view.ViewType, CollectionType.Channels, StringComparison.OrdinalIgnoreCase))
+
+ if (GetParent() is AggregateFolder)
{
return true;
}
diff --git a/MediaBrowser.Controller/Entities/BasePluginFolder.cs b/MediaBrowser.Controller/Entities/BasePluginFolder.cs
index bd109af7a7..7dbea317c8 100644
--- a/MediaBrowser.Controller/Entities/BasePluginFolder.cs
+++ b/MediaBrowser.Controller/Entities/BasePluginFolder.cs
@@ -1,5 +1,5 @@

-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Controller.Entities
{
diff --git a/MediaBrowser.Controller/Entities/Book.cs b/MediaBrowser.Controller/Entities/Book.cs
index 56f9fa784a..a6da389f05 100644
--- a/MediaBrowser.Controller/Entities/Book.cs
+++ b/MediaBrowser.Controller/Entities/Book.cs
@@ -2,7 +2,7 @@
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using System.Linq;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Controller.Entities
@@ -19,6 +19,8 @@ namespace MediaBrowser.Controller.Entities
}
[IgnoreDataMember]
+ public string SeriesPresentationUniqueKey { get; set; }
+ [IgnoreDataMember]
public string SeriesName { get; set; }
[IgnoreDataMember]
public Guid? SeriesId { get; set; }
@@ -33,6 +35,10 @@ namespace MediaBrowser.Controller.Entities
{
return SeriesName;
}
+ public string FindSeriesPresentationUniqueKey()
+ {
+ return SeriesPresentationUniqueKey;
+ }
[IgnoreDataMember]
public override bool EnableRefreshOnDateModifiedChange
diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs
index 41e277b7ca..681f16f07c 100644
--- a/MediaBrowser.Controller/Entities/CollectionFolder.cs
+++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs
@@ -5,14 +5,14 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
-using MoreLinq;
namespace MediaBrowser.Controller.Entities
{
@@ -27,6 +27,7 @@ namespace MediaBrowser.Controller.Entities
public CollectionFolder()
{
PhysicalLocationsList = new List<string>();
+ PhysicalFolderIds = new List<Guid>();
}
[IgnoreDataMember]
@@ -77,7 +78,7 @@ namespace MediaBrowser.Controller.Entities
{
return new LibraryOptions();
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
return new LibraryOptions();
}
@@ -153,6 +154,7 @@ namespace MediaBrowser.Controller.Entities
}
public List<string> PhysicalLocationsList { get; set; }
+ public List<Guid> PhysicalFolderIds { get; set; }
protected override IEnumerable<FileSystemMetadata> GetFileSystemChildren(IDirectoryService directoryService)
{
@@ -176,6 +178,18 @@ namespace MediaBrowser.Controller.Entities
}
}
+ if (!changed)
+ {
+ var folderIds = PhysicalFolderIds.ToList();
+
+ var newFolderIds = GetPhysicalFolders(false).Select(i => i.Id).ToList();
+
+ if (!folderIds.SequenceEqual(newFolderIds))
+ {
+ changed = true;
+ }
+ }
+
return changed;
}
@@ -186,6 +200,39 @@ namespace MediaBrowser.Controller.Entities
return changed;
}
+ 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());
+
+ LinkedChildren = linkedChildren;
+
+ var folderIds = PhysicalFolderIds.ToList();
+ var newFolderIds = physicalFolders.Select(i => i.Id).ToList();
+
+ if (!folderIds.SequenceEqual(newFolderIds))
+ {
+ changed = true;
+ if (setFolders)
+ {
+ PhysicalFolderIds = newFolderIds.ToList();
+ }
+ }
+
+ return changed;
+ }
+
internal override bool IsValidFromResolver(BaseItem newItem)
{
var newCollectionFolder = newItem as CollectionFolder;
@@ -263,25 +310,6 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// Our children are actually just references to the ones in the physical root...
/// </summary>
- /// <value>The linked children.</value>
- public override List<LinkedChild> LinkedChildren
- {
- get { return GetLinkedChildrenInternal(); }
- set
- {
- base.LinkedChildren = value;
- }
- }
- private List<LinkedChild> GetLinkedChildrenInternal()
- {
- return GetPhysicalParents()
- .SelectMany(c => c.LinkedChildren)
- .ToList();
- }
-
- /// <summary>
- /// Our children are actually just references to the ones in the physical root...
- /// </summary>
/// <value>The actual children.</value>
[IgnoreDataMember]
protected override IEnumerable<BaseItem> ActualChildren
@@ -291,11 +319,16 @@ namespace MediaBrowser.Controller.Entities
private IEnumerable<BaseItem> GetActualChildren()
{
- return GetPhysicalParents().SelectMany(c => c.Children);
+ return GetPhysicalFolders(true).SelectMany(c => c.Children);
}
- public IEnumerable<Folder> GetPhysicalParents()
+ 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();
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index cc71ab43e3..943c9d8822 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -8,14 +8,16 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Channels;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Controller.Entities
{
@@ -101,6 +103,16 @@ namespace MediaBrowser.Controller.Entities
}
}
+ public override bool CanDelete()
+ {
+ if (IsRoot)
+ {
+ return false;
+ }
+
+ return base.CanDelete();
+ }
+
public override bool RequiresRefresh()
{
var baseResult = base.RequiresRefresh();
@@ -679,6 +691,19 @@ namespace MediaBrowser.Controller.Entities
return result.TotalRecordCount;
}
+ public virtual int GetRecursiveChildCount(User user)
+ {
+ return GetItems(new InternalItemsQuery(user)
+ {
+ Recursive = true,
+ IsFolder = false,
+ IsVirtualItem = false,
+ EnableTotalRecordCount = true,
+ Limit = 0
+
+ }).Result.TotalRecordCount;
+ }
+
public QueryResult<BaseItem> QueryRecursive(InternalItemsQuery query)
{
var user = query.User;
@@ -1207,7 +1232,7 @@ namespace MediaBrowser.Controller.Entities
/// Refreshes the linked children.
/// </summary>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
- private bool RefreshLinkedChildren(IEnumerable<FileSystemMetadata> fileSystemChildren)
+ protected virtual bool RefreshLinkedChildren(IEnumerable<FileSystemMetadata> fileSystemChildren)
{
var currentManualLinks = LinkedChildren.Where(i => i.Type == LinkedChildType.Manual).ToList();
var currentShortcutLinks = LinkedChildren.Where(i => i.Type == LinkedChildType.Shortcut).ToList();
@@ -1217,7 +1242,7 @@ namespace MediaBrowser.Controller.Entities
if (SupportsShortcutChildren)
{
newShortcutLinks = fileSystemChildren
- .Where(i => (i.Attributes & FileAttributes.Directory) != FileAttributes.Directory && FileSystem.IsShortcut(i.FullName))
+ .Where(i => !i.IsDirectory && FileSystem.IsShortcut(i.FullName))
.Select(i =>
{
try
@@ -1381,60 +1406,64 @@ namespace MediaBrowser.Controller.Entities
{
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 async Task FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, BaseItemDto itemDto, User user)
+ public override async Task FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, BaseItemDto itemDto, User user, List<ItemFields> itemFields)
{
if (!SupportsUserDataFromChildren)
{
return;
}
- var unplayedQueryResult = await GetItems(new InternalItemsQuery(user)
+ if (itemDto != null)
{
- Recursive = true,
- IsFolder = false,
- IsVirtualItem = false,
- EnableTotalRecordCount = true,
- Limit = 0,
- IsPlayed = false
-
- }).ConfigureAwait(false);
+ if (itemFields.Contains(ItemFields.RecursiveItemCount))
+ {
+ itemDto.RecursiveItemCount = GetRecursiveChildCount(user);
+ }
+ }
- var allItemsQueryResult = await GetItems(new InternalItemsQuery(user)
+ if (SupportsPlayedStatus)
{
- Recursive = true,
- IsFolder = false,
- IsVirtualItem = false,
- EnableTotalRecordCount = true,
- Limit = 0
-
- }).ConfigureAwait(false);
+ var unplayedQueryResult = await GetItems(new InternalItemsQuery(user)
+ {
+ Recursive = true,
+ IsFolder = false,
+ IsVirtualItem = false,
+ EnableTotalRecordCount = true,
+ Limit = 0,
+ IsPlayed = false
- if (itemDto != null)
- {
- itemDto.RecursiveItemCount = allItemsQueryResult.TotalRecordCount;
- }
+ }).ConfigureAwait(false);
- var recursiveItemCount = allItemsQueryResult.TotalRecordCount;
- double unplayedCount = unplayedQueryResult.TotalRecordCount;
+ double unplayedCount = unplayedQueryResult.TotalRecordCount;
- if (recursiveItemCount > 0)
- {
- var unplayedPercentage = (unplayedCount / recursiveItemCount) * 100;
- dto.PlayedPercentage = 100 - unplayedPercentage;
- dto.Played = dto.PlayedPercentage.Value >= 100;
dto.UnplayedItemCount = unplayedQueryResult.TotalRecordCount;
- }
- if (itemDto != null)
- {
- if (this is Season || this is MusicAlbum)
+ 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
{
- itemDto.ChildCount = recursiveItemCount;
+ dto.Played = (dto.UnplayedItemCount ?? 0) == 0;
}
}
}
diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs
index aa2ccf4bc0..8bfb8be99d 100644
--- a/MediaBrowser.Controller/Entities/Game.cs
+++ b/MediaBrowser.Controller/Entities/Game.cs
@@ -4,7 +4,7 @@ using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Controller.Entities
{
diff --git a/MediaBrowser.Controller/Entities/GameGenre.cs b/MediaBrowser.Controller/Entities/GameGenre.cs
index 6448828fb3..22a8675c5e 100644
--- a/MediaBrowser.Controller/Entities/GameGenre.cs
+++ b/MediaBrowser.Controller/Entities/GameGenre.cs
@@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Extensions;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Controller.Entities
{
@@ -35,6 +37,15 @@ namespace MediaBrowser.Controller.Entities
}
}
+ [IgnoreDataMember]
+ public override bool SupportsAncestors
+ {
+ get
+ {
+ return false;
+ }
+ }
+
/// <summary>
/// Gets a value indicating whether this instance is owned item.
/// </summary>
diff --git a/MediaBrowser.Controller/Entities/GameSystem.cs b/MediaBrowser.Controller/Entities/GameSystem.cs
index 9e00ab260d..d8879155af 100644
--- a/MediaBrowser.Controller/Entities/GameSystem.cs
+++ b/MediaBrowser.Controller/Entities/GameSystem.cs
@@ -1,4 +1,4 @@
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using System;
diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs
index 1736ba8c76..da4ee352fa 100644
--- a/MediaBrowser.Controller/Entities/Genre.cs
+++ b/MediaBrowser.Controller/Entities/Genre.cs
@@ -1,9 +1,11 @@
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
using MediaBrowser.Controller.Entities.Audio;
using System;
using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Extensions;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Controller.Entities
{
@@ -38,6 +40,15 @@ namespace MediaBrowser.Controller.Entities
}
}
+ [IgnoreDataMember]
+ public override bool SupportsAncestors
+ {
+ get
+ {
+ return false;
+ }
+ }
+
public override bool IsSaveLocalMetadataEnabled()
{
return true;
diff --git a/MediaBrowser.Controller/Entities/IHasImages.cs b/MediaBrowser.Controller/Entities/IHasImages.cs
index 1ab0566e0e..888e2080de 100644
--- a/MediaBrowser.Controller/Entities/IHasImages.cs
+++ b/MediaBrowser.Controller/Entities/IHasImages.cs
@@ -3,8 +3,10 @@ using MediaBrowser.Model.Entities;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Controller.Entities
{
diff --git a/MediaBrowser.Controller/Entities/IHasOriginalTitle.cs b/MediaBrowser.Controller/Entities/IHasOriginalTitle.cs
deleted file mode 100644
index 6f5cb59bc0..0000000000
--- a/MediaBrowser.Controller/Entities/IHasOriginalTitle.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-
-namespace MediaBrowser.Controller.Entities
-{
- public interface IHasOriginalTitle
- {
- string OriginalTitle { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/IHasSeries.cs b/MediaBrowser.Controller/Entities/IHasSeries.cs
index 531f587881..203be93e88 100644
--- a/MediaBrowser.Controller/Entities/IHasSeries.cs
+++ b/MediaBrowser.Controller/Entities/IHasSeries.cs
@@ -15,5 +15,7 @@ namespace MediaBrowser.Controller.Entities
string FindSeriesSortName();
Guid? SeriesId { get; set; }
Guid? FindSeriesId();
+ string SeriesPresentationUniqueKey { get; set; }
+ string FindSeriesPresentationUniqueKey();
}
}
diff --git a/MediaBrowser.Controller/Entities/IHasUserData.cs b/MediaBrowser.Controller/Entities/IHasUserData.cs
index c21e170ae7..0b3b7dc8d9 100644
--- a/MediaBrowser.Controller/Entities/IHasUserData.cs
+++ b/MediaBrowser.Controller/Entities/IHasUserData.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Querying;
namespace MediaBrowser.Controller.Entities
{
@@ -14,10 +15,7 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// Fills the user data dto values.
/// </summary>
- /// <param name="dto">The dto.</param>
- /// <param name="userData">The user data.</param>
- /// <param name="user">The user.</param>
- Task FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, BaseItemDto itemDto, User user);
+ Task FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, BaseItemDto itemDto, User user, List<ItemFields> fields);
bool EnableRememberingTrackSelections { get; }
diff --git a/MediaBrowser.Controller/Entities/IThemeMedia.cs b/MediaBrowser.Controller/Entities/IThemeMedia.cs
deleted file mode 100644
index b2eff230ff..0000000000
--- a/MediaBrowser.Controller/Entities/IThemeMedia.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-
-namespace MediaBrowser.Controller.Entities
-{
- public interface IThemeMedia
- {
- bool IsThemeMedia { get; }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
index 3fb118a9cf..a2d278a71c 100644
--- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
+++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
@@ -123,9 +123,7 @@ namespace MediaBrowser.Controller.Entities
public int? MinParentalRating { get; set; }
public int? MaxParentalRating { get; set; }
- public bool? IsCurrentSchema { get; set; }
public bool? HasDeadParentId { get; set; }
- public bool? IsOffline { get; set; }
public bool? IsVirtualItem { get; set; }
public Guid? ParentId { get; set; }
@@ -143,11 +141,13 @@ namespace MediaBrowser.Controller.Entities
public SeriesStatus[] SeriesStatuses { get; set; }
public string AlbumArtistStartsWithOrGreater { get; set; }
public string ExternalSeriesId { get; set; }
+ public string ExternalId { get; set; }
public string[] AlbumNames { get; set; }
public string[] ArtistNames { get; set; }
public string[] ExcludeArtistIds { get; set; }
public string AncestorWithPresentationUniqueKey { get; set; }
+ public string SeriesPresentationUniqueKey { get; set; }
public bool GroupByPresentationUniqueKey { get; set; }
public bool EnableTotalRecordCount { get; set; }
diff --git a/MediaBrowser.Controller/Entities/ItemImageInfo.cs b/MediaBrowser.Controller/Entities/ItemImageInfo.cs
index aa3917c15f..672595db84 100644
--- a/MediaBrowser.Controller/Entities/ItemImageInfo.cs
+++ b/MediaBrowser.Controller/Entities/ItemImageInfo.cs
@@ -1,6 +1,6 @@
using MediaBrowser.Model.Entities;
using System;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Controller.Entities
{
diff --git a/MediaBrowser.Controller/Entities/LinkedChild.cs b/MediaBrowser.Controller/Entities/LinkedChild.cs
index ac13657b94..4d3c13c6e3 100644
--- a/MediaBrowser.Controller/Entities/LinkedChild.cs
+++ b/MediaBrowser.Controller/Entities/LinkedChild.cs
@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Controller.Entities
{
diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
index 6d5278f1fe..3e6c88a85d 100644
--- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
+++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
@@ -7,7 +7,7 @@ using MediaBrowser.Model.Users;
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
using MediaBrowser.Controller.Entities.Audio;
namespace MediaBrowser.Controller.Entities.Movies
@@ -202,5 +202,10 @@ namespace MediaBrowser.Controller.Entities.Movies
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
index f13adb21c7..ec04879b54 100644
--- a/MediaBrowser.Controller/Entities/Movies/Movie.cs
+++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs
@@ -4,18 +4,20 @@ using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+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, IHasBudget, IHasTrailers, IHasAwards, IHasMetascore, IHasLookupInfo<MovieInfo>, ISupportsBoxSetGrouping, IHasOriginalTitle
+ public class Movie : Video, IHasSpecialFeatures, IHasBudget, IHasTrailers, IHasAwards, IHasMetascore, IHasLookupInfo<MovieInfo>, ISupportsBoxSetGrouping
{
public List<Guid> SpecialFeatureIds { get; set; }
@@ -123,7 +125,18 @@ namespace MediaBrowser.Controller.Entities.Movies
if (!DetectIsInMixedFolder())
{
- info.Name = System.IO.Path.GetFileName(ContainingFolderPath);
+ 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;
diff --git a/MediaBrowser.Controller/Entities/MusicVideo.cs b/MediaBrowser.Controller/Entities/MusicVideo.cs
index 6e632a26cf..e3fc4259ae 100644
--- a/MediaBrowser.Controller/Entities/MusicVideo.cs
+++ b/MediaBrowser.Controller/Entities/MusicVideo.cs
@@ -2,7 +2,7 @@
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using System.Collections.Generic;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Controller.Entities
{
diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs
index f21bc0a71e..0c36442af3 100644
--- a/MediaBrowser.Controller/Entities/Person.cs
+++ b/MediaBrowser.Controller/Entities/Person.cs
@@ -2,9 +2,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Runtime.Serialization;
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Extensions;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Controller.Entities
{
@@ -38,7 +40,7 @@ namespace MediaBrowser.Controller.Entities
public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query)
{
- query.Person = Name;
+ query.PersonIds = new[] { Id.ToString("N") };
return LibraryManager.GetItemList(query);
}
@@ -93,7 +95,7 @@ namespace MediaBrowser.Controller.Entities
{
var itemsWithPerson = LibraryManager.GetItemIds(new InternalItemsQuery
{
- Person = Name
+ PersonIds = new[] { Id.ToString("N") }
});
return inputItems.Where(i => itemsWithPerson.Contains(i.Id));
diff --git a/MediaBrowser.Controller/Entities/Photo.cs b/MediaBrowser.Controller/Entities/Photo.cs
index a42fce8bbd..e95ceee52c 100644
--- a/MediaBrowser.Controller/Entities/Photo.cs
+++ b/MediaBrowser.Controller/Entities/Photo.cs
@@ -1,6 +1,6 @@
using MediaBrowser.Model.Drawing;
using System.Linq;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Controller.Entities
{
diff --git a/MediaBrowser.Controller/Entities/PhotoAlbum.cs b/MediaBrowser.Controller/Entities/PhotoAlbum.cs
index cb5c5c4539..c902ac8cda 100644
--- a/MediaBrowser.Controller/Entities/PhotoAlbum.cs
+++ b/MediaBrowser.Controller/Entities/PhotoAlbum.cs
@@ -1,7 +1,7 @@
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Users;
using System.Linq;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Controller.Entities
{
diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs
index 04b09b7442..ec623eedac 100644
--- a/MediaBrowser.Controller/Entities/Studio.cs
+++ b/MediaBrowser.Controller/Entities/Studio.cs
@@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Extensions;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Controller.Entities
{
@@ -37,6 +39,15 @@ namespace MediaBrowser.Controller.Entities
}
}
+ [IgnoreDataMember]
+ public override bool SupportsAncestors
+ {
+ get
+ {
+ return false;
+ }
+ }
+
public override bool CanDelete()
{
return false;
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs
index 1a02043d6f..e6ebcb7fd9 100644
--- a/MediaBrowser.Controller/Entities/TV/Episode.cs
+++ b/MediaBrowser.Controller/Entities/TV/Episode.cs
@@ -3,8 +3,9 @@ using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Controller.Entities.TV
{
@@ -71,6 +72,12 @@ namespace MediaBrowser.Controller.Entities.TV
{
return IsStacked || MediaSourceCount > 1;
}
+ }
+
+ [IgnoreDataMember]
+ public override bool SupportsInheritedParentImages
+ {
+ get { return true; }
}
[IgnoreDataMember]
@@ -159,18 +166,37 @@ namespace MediaBrowser.Controller.Entities.TV
{
return FindParent<Season>() != null;
}
- }
+ }
+
+ [IgnoreDataMember]
+ public string SeriesPresentationUniqueKey { get; set; }
[IgnoreDataMember]
public string SeriesName { get; set; }
[IgnoreDataMember]
- public string SeasonName { get; set; }
+ public string SeasonName { get; set; }
+
+ public string FindSeriesPresentationUniqueKey()
+ {
+ var series = Series;
+ return series == null ? null : series.PresentationUniqueKey;
+ }
public string FindSeasonName()
{
- var season = Season;
- return season == null ? SeasonName : season.Name;
+ 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()
diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs
index ce13f5fc5f..f2a6586e2f 100644
--- a/MediaBrowser.Controller/Entities/TV/Season.cs
+++ b/MediaBrowser.Controller/Entities/TV/Season.cs
@@ -2,12 +2,11 @@
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Users;
-using MoreLinq;
using System.Collections.Generic;
using System.Linq;
-using System.Runtime.Serialization;
using System.Threading.Tasks;
using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Controller.Entities.TV
{
@@ -41,13 +40,15 @@ namespace MediaBrowser.Controller.Entities.TV
}
[IgnoreDataMember]
+ public override bool SupportsInheritedParentImages
+ {
+ get { return true; }
+ }
+
+ [IgnoreDataMember]
public override Guid? DisplayParentId
{
- get
- {
- var series = Series;
- return series == null ? ParentId : series.Id;
- }
+ get { return SeriesId; }
}
[IgnoreDataMember]
@@ -203,11 +204,20 @@ namespace MediaBrowser.Controller.Entities.TV
}
[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;
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
index 39703f67ac..872011ce8d 100644
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ b/MediaBrowser.Controller/Entities/TV/Series.cs
@@ -6,18 +6,18 @@ using MediaBrowser.Model.Users;
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Providers;
-using MoreLinq;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Controller.Entities.TV
{
/// <summary>
/// Class Series
/// </summary>
- public class Series : Folder, IHasTrailers, IHasDisplayOrder, IHasLookupInfo<SeriesInfo>, IMetadataContainer, IHasOriginalTitle
+ public class Series : Folder, IHasTrailers, IHasDisplayOrder, IHasLookupInfo<SeriesInfo>, IMetadataContainer
{
public int? AnimeSeriesIndex { get; set; }
@@ -95,17 +95,27 @@ namespace MediaBrowser.Controller.Entities.TV
public override string CreatePresentationUniqueKey()
{
- var userdatakeys = GetUserDataKeys();
-
- if (userdatakeys.Count > 1)
+ if (LibraryManager.GetLibraryOptions(this).EnableAutomaticSeriesGrouping)
{
- return AddLibrariesToPresentationUniqueKey(userdatakeys[0]);
+ 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();
@@ -120,25 +130,62 @@ namespace MediaBrowser.Controller.Entities.TV
private static string GetUniqueSeriesKey(BaseItem series)
{
- if (ConfigurationManager.Configuration.SchemaVersion < 97)
- {
- return series.Id.ToString("N");
- }
return series.GetPresentationUniqueKey();
}
public override int GetChildCount(User user)
{
- var result = LibraryManager.GetItemsResult(new InternalItemsQuery(user)
+ var enableSeriesPresentationKey = ConfigurationManager.Configuration.EnableSeriesPresentationUniqueKey;
+ var seriesKey = GetUniqueSeriesKey(this);
+
+ var result = LibraryManager.GetCount(new InternalItemsQuery(user)
{
- AncestorWithPresentationUniqueKey = GetUniqueSeriesKey(this),
+ AncestorWithPresentationUniqueKey = enableSeriesPresentationKey ? null : seriesKey,
+ SeriesPresentationUniqueKey = enableSeriesPresentationKey ? seriesKey : null,
IncludeItemTypes = new[] { typeof(Season).Name },
- SortBy = new[] { ItemSortBy.SortName },
IsVirtualItem = false,
- Limit = 0
+ Limit = 0,
+ DtoOptions = new Dto.DtoOptions
+ {
+ Fields = new List<ItemFields>
+ {
+
+ },
+ EnableImages = false
+ }
});
- return result.TotalRecordCount;
+ return result;
+ }
+
+ public override int GetRecursiveChildCount(User user)
+ {
+ var enableSeriesPresentationKey = ConfigurationManager.Configuration.EnableSeriesPresentationUniqueKey;
+ var seriesKey = GetUniqueSeriesKey(this);
+
+ var query = new InternalItemsQuery(user)
+ {
+ AncestorWithPresentationUniqueKey = enableSeriesPresentationKey ? null : seriesKey,
+ SeriesPresentationUniqueKey = enableSeriesPresentationKey ? seriesKey : null,
+ DtoOptions = new Dto.DtoOptions
+ {
+ Fields = new List<ItemFields>
+ {
+
+ },
+ EnableImages = false
+ }
+ };
+
+ if (query.IncludeItemTypes.Length == 0)
+ {
+ query.IncludeItemTypes = new[] { typeof(Episode).Name, typeof(Season).Name };
+ }
+ query.IsVirtualItem = false;
+ query.Limit = 0;
+ var totalRecordCount = LibraryManager.GetCount(query);
+
+ return totalRecordCount;
}
/// <summary>
@@ -204,13 +251,15 @@ namespace MediaBrowser.Controller.Entities.TV
{
var config = user.Configuration;
+ var enableSeriesPresentationKey = ConfigurationManager.Configuration.EnableSeriesPresentationUniqueKey;
var seriesKey = GetUniqueSeriesKey(this);
var query = new InternalItemsQuery(user)
{
- AncestorWithPresentationUniqueKey = seriesKey,
- IncludeItemTypes = new[] {typeof (Season).Name},
- SortBy = new[] {ItemSortBy.SortName}
+ AncestorWithPresentationUniqueKey = enableSeriesPresentationKey ? null : seriesKey,
+ SeriesPresentationUniqueKey = enableSeriesPresentationKey ? seriesKey : null,
+ IncludeItemTypes = new[] { typeof(Season).Name },
+ SortBy = new[] { ItemSortBy.SortName }
};
if (!config.DisplayMissingEpisodes && !config.DisplayUnairedEpisodes)
@@ -240,7 +289,11 @@ namespace MediaBrowser.Controller.Entities.TV
if (query.Recursive)
{
- query.AncestorWithPresentationUniqueKey = GetUniqueSeriesKey(this);
+ var enableSeriesPresentationKey = ConfigurationManager.Configuration.EnableSeriesPresentationUniqueKey;
+ var seriesKey = GetUniqueSeriesKey(this);
+
+ query.AncestorWithPresentationUniqueKey = enableSeriesPresentationKey ? null : seriesKey;
+ query.SeriesPresentationUniqueKey = enableSeriesPresentationKey ? seriesKey : null;
if (query.SortBy.Length == 0)
{
query.SortBy = new[] { ItemSortBy.SortName };
@@ -262,13 +315,15 @@ namespace MediaBrowser.Controller.Entities.TV
public IEnumerable<Episode> GetEpisodes(User user)
{
+ var enableSeriesPresentationKey = ConfigurationManager.Configuration.EnableSeriesPresentationUniqueKey;
var seriesKey = GetUniqueSeriesKey(this);
var query = new InternalItemsQuery(user)
{
- AncestorWithPresentationUniqueKey = seriesKey,
- IncludeItemTypes = new[] {typeof (Episode).Name, typeof (Season).Name},
- SortBy = new[] {ItemSortBy.SortName}
+ AncestorWithPresentationUniqueKey = enableSeriesPresentationKey ? null : seriesKey,
+ SeriesPresentationUniqueKey = enableSeriesPresentationKey ? seriesKey : null,
+ IncludeItemTypes = new[] { typeof(Episode).Name, typeof(Season).Name },
+ SortBy = new[] { ItemSortBy.SortName }
};
var config = user.Configuration;
if (!config.DisplayMissingEpisodes && !config.DisplayUnairedEpisodes)
@@ -368,11 +423,19 @@ namespace MediaBrowser.Controller.Entities.TV
public IEnumerable<Episode> GetSeasonEpisodes(Season parentSeason, User user)
{
- var seriesKey = GetUniqueSeriesKey(this);
+ var enableSeriesPresentationKey = ConfigurationManager.Configuration.EnableSeriesPresentationUniqueKey;
+
+ 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 = seriesKey,
+ AncestorWithPresentationUniqueKey = queryFromSeries && enableSeriesPresentationKey ? null : seriesKey,
+ SeriesPresentationUniqueKey = queryFromSeries && enableSeriesPresentationKey ? seriesKey : null,
IncludeItemTypes = new[] { typeof(Episode).Name },
SortBy = new[] { ItemSortBy.SortName }
};
diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs
index 0780cfec5e..dd6d8a999f 100644
--- a/MediaBrowser.Controller/Entities/Trailer.cs
+++ b/MediaBrowser.Controller/Entities/Trailer.cs
@@ -2,15 +2,15 @@
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using System.Collections.Generic;
-using System.Runtime.Serialization;
using MediaBrowser.Model.Providers;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Controller.Entities
{
/// <summary>
/// Class Trailer
/// </summary>
- public class Trailer : Video, IHasBudget, IHasMetascore, IHasOriginalTitle, IHasLookupInfo<TrailerInfo>
+ public class Trailer : Video, IHasLookupInfo<TrailerInfo>
{
public Trailer()
{
@@ -21,8 +21,6 @@ namespace MediaBrowser.Controller.Entities
public List<TrailerType> TrailerTypes { get; set; }
- public float? Metascore { get; set; }
-
public List<MediaUrl> RemoteTrailers { get; set; }
[IgnoreDataMember]
@@ -31,18 +29,6 @@ namespace MediaBrowser.Controller.Entities
get { return TrailerTypes.Contains(TrailerType.LocalTrailer); }
}
- /// <summary>
- /// Gets or sets the budget.
- /// </summary>
- /// <value>The budget.</value>
- public double? Budget { get; set; }
-
- /// <summary>
- /// Gets or sets the revenue.
- /// </summary>
- /// <value>The revenue.</value>
- public double? Revenue { get; set; }
-
public override UnratedItem GetBlockUnratedType()
{
return UnratedItem.Trailer;
diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs
index 46da469fa5..d5d229bb35 100644
--- a/MediaBrowser.Controller/Entities/User.cs
+++ b/MediaBrowser.Controller/Entities/User.cs
@@ -6,7 +6,6 @@ using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Users;
using System;
using System.Linq;
-using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
diff --git a/MediaBrowser.Controller/Entities/UserItemData.cs b/MediaBrowser.Controller/Entities/UserItemData.cs
index f95fd70364..0e13269499 100644
--- a/MediaBrowser.Controller/Entities/UserItemData.cs
+++ b/MediaBrowser.Controller/Entities/UserItemData.cs
@@ -1,5 +1,5 @@
using System;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Controller.Entities
{
diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs
index b67817846f..b602990ac8 100644
--- a/MediaBrowser.Controller/Entities/UserRootFolder.cs
+++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs
@@ -1,4 +1,4 @@
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Library;
using MediaBrowser.Model.Querying;
diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs
index a689e0d091..5ac5843d70 100644
--- a/MediaBrowser.Controller/Entities/UserView.cs
+++ b/MediaBrowser.Controller/Entities/UserView.cs
@@ -4,7 +4,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using System;
using System.Collections.Generic;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
using System.Threading.Tasks;
using System.Linq;
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
index 390d1bf025..bc806e76e2 100644
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
@@ -17,7 +17,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration;
-using MoreLinq;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Controller.Entities
{
@@ -1778,7 +1778,7 @@ namespace MediaBrowser.Controller.Entities
.Where(i => user.IsFolderGrouped(i.Id) && UserView.IsEligibleForGrouping(i));
}
- private IEnumerable<Folder> GetMediaFolders(User user, IEnumerable<string> viewTypes)
+ private List<Folder> GetMediaFolders(User user, IEnumerable<string> viewTypes)
{
if (user == null)
{
@@ -1788,7 +1788,7 @@ namespace MediaBrowser.Controller.Entities
var folder = i as ICollectionFolder;
return folder != null && viewTypes.Contains(folder.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
- });
+ }).ToList();
}
return GetMediaFolders(user)
.Where(i =>
@@ -1796,17 +1796,17 @@ namespace MediaBrowser.Controller.Entities
var folder = i as ICollectionFolder;
return folder != null && viewTypes.Contains(folder.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
- });
+ }).ToList();
}
- private IEnumerable<Folder> GetMediaFolders(Folder parent, User user, IEnumerable<string> viewTypes)
+ private List<Folder> GetMediaFolders(Folder parent, User user, IEnumerable<string> viewTypes)
{
if (parent == null || parent is UserView)
{
return GetMediaFolders(user, viewTypes);
}
- return new[] { parent };
+ return new List<Folder> { parent };
}
private IEnumerable<BaseItem> GetRecursiveChildren(Folder parent, User user, IEnumerable<string> viewTypes)
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index 78d7a7fdd9..7ba59df4f3 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -8,12 +8,14 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
-using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Channels;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Controller.Entities
{
@@ -23,8 +25,7 @@ namespace MediaBrowser.Controller.Entities
public class Video : BaseItem,
IHasAspectRatio,
ISupportsPlaceHolders,
- IHasMediaSources,
- IThemeMedia
+ IHasMediaSources
{
[IgnoreDataMember]
public string PrimaryVersionId { get; set; }
@@ -35,16 +36,16 @@ namespace MediaBrowser.Controller.Entities
public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
[IgnoreDataMember]
- public bool IsThemeMedia
+ public override bool SupportsPlayedStatus
{
get
{
- return ExtraType.HasValue && ExtraType.Value == Model.Entities.ExtraType.ThemeVideo;
+ return true;
}
}
[IgnoreDataMember]
- public override bool SupportsPlayedStatus
+ public override bool SupportsPositionTicksResume
{
get
{
@@ -86,9 +87,6 @@ namespace MediaBrowser.Controller.Entities
get { return true; }
}
- public int? TotalBitrate { get; set; }
- public ExtraType? ExtraType { get; set; }
-
/// <summary>
/// Gets or sets the timestamp.
/// </summary>
@@ -112,12 +110,6 @@ namespace MediaBrowser.Controller.Entities
public string ShortcutPath { get; set; }
/// <summary>
- /// Gets or sets the video bit rate.
- /// </summary>
- /// <value>The video bit rate.</value>
- public int? VideoBitRate { get; set; }
-
- /// <summary>
/// Gets or sets the default index of the video stream.
/// </summary>
/// <value>The default index of the video stream.</value>
diff --git a/MediaBrowser.Controller/Entities/Year.cs b/MediaBrowser.Controller/Entities/Year.cs
index 4197ea93e5..75fb694353 100644
--- a/MediaBrowser.Controller/Entities/Year.cs
+++ b/MediaBrowser.Controller/Entities/Year.cs
@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Controller.Entities
{
@@ -33,6 +33,15 @@ namespace MediaBrowser.Controller.Entities
}
}
+ [IgnoreDataMember]
+ public override bool SupportsAncestors
+ {
+ get
+ {
+ return false;
+ }
+ }
+
public override bool CanDelete()
{
return false;
diff --git a/MediaBrowser.Controller/Extensions/StringExtensions.cs b/MediaBrowser.Controller/Extensions/StringExtensions.cs
new file mode 100644
index 0000000000..60e7815dbe
--- /dev/null
+++ b/MediaBrowser.Controller/Extensions/StringExtensions.cs
@@ -0,0 +1,17 @@
+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
index 6e7e60f88c..d40829d44b 100644
--- a/MediaBrowser.Controller/IO/FileData.cs
+++ b/MediaBrowser.Controller/IO/FileData.cs
@@ -3,7 +3,8 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Controller.IO
{
diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs
index d6744e8043..44c0031974 100644
--- a/MediaBrowser.Controller/IServerApplicationHost.cs
+++ b/MediaBrowser.Controller/IServerApplicationHost.cs
@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
+using MediaBrowser.Model.Net;
namespace MediaBrowser.Controller
{
@@ -25,12 +26,6 @@ namespace MediaBrowser.Controller
/// </summary>
/// <value><c>true</c> if [supports automatic run at startup]; otherwise, <c>false</c>.</value>
bool SupportsAutoRunAtStartup { get; }
-
- /// <summary>
- /// Gets a value indicating whether [supports library monitor].
- /// </summary>
- /// <value><c>true</c> if [supports library monitor]; otherwise, <c>false</c>.</value>
- bool SupportsLibraryMonitor { get; }
/// <summary>
/// Gets the HTTP server port.
@@ -66,7 +61,7 @@ namespace MediaBrowser.Controller
/// Gets the local ip address.
/// </summary>
/// <value>The local ip address.</value>
- Task<List<IPAddress>> GetLocalIpAddresses();
+ Task<List<IpAddressInfo>> GetLocalIpAddresses();
/// <summary>
/// Gets the local API URL.
@@ -84,9 +79,7 @@ namespace MediaBrowser.Controller
/// <summary>
/// Gets the local API URL.
/// </summary>
- /// <param name="ipAddress">The ip address.</param>
- /// <returns>System.String.</returns>
- string GetLocalApiUrl(IPAddress ipAddress);
+ string GetLocalApiUrl(IpAddressInfo address);
void LaunchUrl(string url);
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index 975c596594..bf9a076264 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -10,10 +10,12 @@ using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Controller.Library
{
@@ -60,6 +62,8 @@ namespace MediaBrowser.Controller.Library
/// <returns>BaseItem.</returns>
BaseItem FindByPath(string path, bool? isFolder);
+ Guid? FindIdByPath(string path, bool? isFolder);
+
/// <summary>
/// Gets the artist.
/// </summary>
@@ -536,10 +540,7 @@ namespace MediaBrowser.Controller.Library
/// <summary>
/// Gets the items.
/// </summary>
- /// <param name="query">The query.</param>
- /// <param name="parentIds">The parent ids.</param>
- /// <returns>List&lt;BaseItem&gt;.</returns>
- IEnumerable<BaseItem> GetItemList(InternalItemsQuery query, IEnumerable<string> parentIds);
+ IEnumerable<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents);
/// <summary>
/// Gets the items result.
@@ -572,5 +573,6 @@ namespace MediaBrowser.Controller.Library
void RegisterIgnoredPath(string path);
void UnRegisterIgnoredPath(string path);
+ int GetCount(InternalItemsQuery query);
}
} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Library/IUserDataManager.cs b/MediaBrowser.Controller/Library/IUserDataManager.cs
index 86c52c4c31..5940c7e292 100644
--- a/MediaBrowser.Controller/Library/IUserDataManager.cs
+++ b/MediaBrowser.Controller/Library/IUserDataManager.cs
@@ -5,6 +5,7 @@ using MediaBrowser.Model.Entities;
using System;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Querying;
namespace MediaBrowser.Controller.Library
{
@@ -37,12 +38,9 @@ namespace MediaBrowser.Controller.Library
/// <summary>
/// Gets the user data dto.
/// </summary>
- /// <param name="item">The item.</param>
- /// <param name="user">The user.</param>
- /// <returns>UserItemDataDto.</returns>
Task<UserItemDataDto> GetUserDataDto(IHasUserData item, User user);
- Task<UserItemDataDto> GetUserDataDto(IHasUserData item, BaseItemDto itemDto, User user);
+ Task<UserItemDataDto> GetUserDataDto(IHasUserData item, BaseItemDto itemDto, User user, List<ItemFields> fields);
/// <summary>
/// Get all user data for the given user
diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs
index ec0ac325bc..763d27ebaf 100644
--- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs
+++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs
@@ -4,9 +4,11 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using CommonIO;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Controller.Library
{
@@ -97,18 +99,6 @@ namespace MediaBrowser.Controller.Library
}
/// <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>
- public bool IsHidden
- {
- get
- {
- return (FileInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
- }
- }
-
- /// <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>
diff --git a/MediaBrowser.Controller/Library/NameExtensions.cs b/MediaBrowser.Controller/Library/NameExtensions.cs
index 72f036b0a5..693b7b2217 100644
--- a/MediaBrowser.Controller/Library/NameExtensions.cs
+++ b/MediaBrowser.Controller/Library/NameExtensions.cs
@@ -1,29 +1,20 @@
-using MediaBrowser.Common.Extensions;
-using MoreLinq;
-using System;
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using MediaBrowser.Controller.Extensions;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Controller.Library
{
public static class NameExtensions
{
- public static bool AreEqual(string x, string y)
- {
- if (string.IsNullOrWhiteSpace(x) && string.IsNullOrWhiteSpace(y))
- {
- return true;
- }
-
- return string.Compare(x, y, CultureInfo.InvariantCulture, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace) == 0;
- }
-
public static bool EqualsAny(IEnumerable<string> names, string x)
{
x = NormalizeForComparison(x);
- return names.Any(y => string.Compare(x, y, CultureInfo.InvariantCulture, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace) == 0);
+ return names.Any(y => string.Compare(x, y, StringComparison.OrdinalIgnoreCase) == 0);
+ //return names.Any(y => string.Compare(x, y, CultureInfo.InvariantCulture, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace) == 0);
}
private static string NormalizeForComparison(string name)
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
index 8e3c1931b1..08e4956adc 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
@@ -393,5 +393,8 @@ namespace MediaBrowser.Controller.LiveTv
event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCancelled;
event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCreated;
event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCreated;
+
+ string GetEmbyTvActiveRecordingPath(string id);
+ Task<LiveStream> GetEmbyTvLiveStream(string id);
}
}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
index de5ffcd588..e67fc57594 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
@@ -7,7 +7,7 @@ using MediaBrowser.Model.LiveTv;
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
using System.Threading.Tasks;
using MediaBrowser.Controller.Library;
@@ -46,6 +46,15 @@ namespace MediaBrowser.Controller.LiveTv
set { }
}
+ [IgnoreDataMember]
+ public override bool SupportsPositionTicksResume
+ {
+ get
+ {
+ return true;
+ }
+ }
+
/// <summary>
/// Gets a value indicating whether this instance is owned item.
/// </summary>
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
index 5e99d6fa3f..d164b5e0d9 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
@@ -6,7 +6,7 @@ using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.MediaInfo;
using System.Collections.Generic;
using System.Globalization;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Controller.LiveTv
{
@@ -39,6 +39,15 @@ namespace MediaBrowser.Controller.LiveTv
}
[IgnoreDataMember]
+ public override bool SupportsPositionTicksResume
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ [IgnoreDataMember]
public override SourceType SourceType
{
get { return SourceType.LiveTV; }
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
index c7f47b825b..ffb6a75556 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
@@ -4,9 +4,12 @@ using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.LiveTv;
using System;
using System.Collections.Generic;
-using System.Runtime.Serialization;
+using System.Linq;
+using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
+using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Controller.LiveTv
{
@@ -236,6 +239,40 @@ namespace MediaBrowser.Controller.LiveTv
}
}
+ 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();
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
index 2c0ced0e6c..18894120a8 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
@@ -6,7 +6,7 @@ using MediaBrowser.Model.LiveTv;
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
using System.Threading.Tasks;
using MediaBrowser.Controller.Library;
diff --git a/MediaBrowser.Controller/LiveTv/RecordingGroup.cs b/MediaBrowser.Controller/LiveTv/RecordingGroup.cs
index 5c86de08b8..8093b5a768 100644
--- a/MediaBrowser.Controller/LiveTv/RecordingGroup.cs
+++ b/MediaBrowser.Controller/LiveTv/RecordingGroup.cs
@@ -1,4 +1,4 @@
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Users;
diff --git a/MediaBrowser.Controller/LiveTv/TimerInfo.cs b/MediaBrowser.Controller/LiveTv/TimerInfo.cs
index fd614253ae..10ed95fe5e 100644
--- a/MediaBrowser.Controller/LiveTv/TimerInfo.cs
+++ b/MediaBrowser.Controller/LiveTv/TimerInfo.cs
@@ -34,6 +34,8 @@ namespace MediaBrowser.Controller.LiveTv
/// <value>The program identifier.</value>
public string ProgramId { get; set; }
+ public string ShowId { get; set; }
+
/// <summary>
/// Name of the recording.
/// </summary>
@@ -44,6 +46,8 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary>
public string Overview { get; set; }
+ public string SeriesId { get; set; }
+
/// <summary>
/// The start date of the recording, in UTC.
/// </summary>
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index 36d59d3e44..28229f8a7e 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<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>
@@ -11,9 +11,10 @@
<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>
- <ReleaseVersion>
- </ReleaseVersion>
+ <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -44,37 +45,13 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
- <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
- </Reference>
- <Reference Include="Interfaces.IO">
- <HintPath>..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll</HintPath>
- </Reference>
- <Reference Include="MoreLinq">
- <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
- </Reference>
- <Reference Include="Patterns.Logging">
- <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
- </Reference>
- <Reference Include="System" />
- <Reference Include="System.Core" />
- <Reference Include="System.Data" />
- <Reference Include="System.Net" />
- <Reference Include="System.Runtime.Serialization" />
- <Reference Include="Microsoft.CSharp" />
- <Reference Include="System.Xml" />
- <Reference Include="System.Xml.Linq" />
- <Reference Include="ServiceStack.Interfaces">
- <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath>
- </Reference>
+ <None Include="project.json" />
+ <!-- A reference to the entire .NET Framework is automatically included -->
</ItemGroup>
<ItemGroup>
<Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link>
</Compile>
- <Compile Include="Activity\IActivityManager.cs" />
- <Compile Include="Activity\IActivityRepository.cs" />
<Compile Include="Channels\ChannelItemInfo.cs" />
<Compile Include="Channels\ChannelItemResult.cs" />
<Compile Include="Channels\ChannelItemType.cs" />
@@ -107,12 +84,11 @@
<Compile Include="Dlna\EventSubscriptionResponse.cs" />
<Compile Include="Dlna\IConnectionManager.cs" />
<Compile Include="Dlna\IContentDirectory.cs" />
- <Compile Include="Dlna\IDeviceDiscovery.cs" />
<Compile Include="Dlna\IDlnaManager.cs" />
<Compile Include="Dlna\IEventManager.cs" />
<Compile Include="Dlna\IMediaReceiverRegistrar.cs" />
<Compile Include="Dlna\IUpnpService.cs" />
- <Compile Include="Dlna\SsdpMessageEventArgs.cs" />
+ <Compile Include="Drawing\IImageEncoder.cs" />
<Compile Include="Drawing\IImageProcessor.cs" />
<Compile Include="Drawing\ImageCollageOptions.cs" />
<Compile Include="Drawing\ImageProcessingOptions.cs" />
@@ -120,6 +96,7 @@
<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" />
@@ -139,7 +116,6 @@
<Compile Include="Entities\KeywordExtensions.cs" />
<Compile Include="Entities\IHasMediaSources.cs" />
<Compile Include="Entities\IHasMetascore.cs" />
- <Compile Include="Entities\IHasOriginalTitle.cs" />
<Compile Include="Entities\IHasProgramAttributes.cs" />
<Compile Include="Entities\IHasScreenshots.cs" />
<Compile Include="Entities\IHasSeries.cs" />
@@ -155,7 +131,6 @@
<Compile Include="Entities\ISupportsBoxSetGrouping.cs" />
<Compile Include="Entities\ISupportsPlaceHolders.cs" />
<Compile Include="Entities\ItemImageInfo.cs" />
- <Compile Include="Entities\IThemeMedia.cs" />
<Compile Include="Entities\LinkedChild.cs" />
<Compile Include="Entities\MusicVideo.cs" />
<Compile Include="Entities\IHasAwards.cs" />
@@ -167,9 +142,8 @@
<Compile Include="Entities\TagExtensions.cs" />
<Compile Include="Entities\UserView.cs" />
<Compile Include="Entities\UserViewBuilder.cs" />
+ <Compile Include="Extensions\StringExtensions.cs" />
<Compile Include="FileOrganization\IFileOrganizationService.cs" />
- <Compile Include="Health\IHealthMonitor.cs" />
- <Compile Include="IO\ThrottledStream.cs" />
<Compile Include="Library\DeleteOptions.cs" />
<Compile Include="Library\ILibraryPostScanTask.cs" />
<Compile Include="Library\IMediaSourceManager.cs" />
@@ -209,7 +183,6 @@
<Compile Include="LiveTv\TimerEventInfo.cs" />
<Compile Include="LiveTv\TimerInfo.cs" />
<Compile Include="LiveTv\TunerChannelMapping.cs" />
- <Compile Include="Localization\ILocalizationManager.cs" />
<Compile Include="MediaEncoding\ChapterImageRefreshOptions.cs" />
<Compile Include="MediaEncoding\EncodingJobOptions.cs" />
<Compile Include="MediaEncoding\IEncodingManager.cs" />
@@ -220,16 +193,11 @@
<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\IHasAuthorization.cs" />
- <Compile Include="Net\IAsyncStreamSource.cs" />
<Compile Include="Net\IHasResultFactory.cs" />
- <Compile Include="Net\IHasSession.cs" />
<Compile Include="Net\IHttpResultFactory.cs" />
<Compile Include="Net\IHttpServer.cs" />
- <Compile Include="Net\IRestfulService.cs" />
<Compile Include="Net\IServerManager.cs" />
<Compile Include="Net\IServiceRequest.cs" />
<Compile Include="Net\ISessionContext.cs" />
@@ -238,11 +206,10 @@
<Compile Include="Net\IWebSocketListener.cs" />
<Compile Include="Net\LoggedAttribute.cs" />
<Compile Include="Net\SecurityException.cs" />
- <Compile Include="Net\ServiceStackServiceRequest.cs" />
+ <Compile Include="Net\ServiceRequest.cs" />
<Compile Include="Net\StaticResultOptions.cs" />
<Compile Include="Net\WebSocketConnectEventArgs.cs" />
<Compile Include="Net\WebSocketMessageInfo.cs" />
- <Compile Include="News\INewsService.cs" />
<Compile Include="Notifications\INotificationManager.cs" />
<Compile Include="Notifications\INotificationService.cs" />
<Compile Include="Notifications\INotificationsRepository.cs" />
@@ -309,7 +276,6 @@
<Compile Include="Security\IAuthenticationRepository.cs" />
<Compile Include="Security\IEncryptionManager.cs" />
<Compile Include="Session\AuthenticationRequest.cs" />
- <Compile Include="Social\ISharingManager.cs" />
<Compile Include="Sorting\SortHelper.cs" />
<Compile Include="Subtitles\ISubtitleManager.cs" />
<Compile Include="Subtitles\ISubtitleProvider.cs" />
@@ -377,7 +343,6 @@
<Compile Include="Session\SessionInfo.cs" />
<Compile Include="Sorting\IBaseItemComparer.cs" />
<Compile Include="Sorting\IUserBaseItemComparer.cs" />
- <Compile Include="Providers\BaseItemXmlParser.cs" />
<Compile Include="Sorting\SortExtensions.cs" />
<Compile Include="Subtitles\SubtitleDownloadEventArgs.cs" />
<Compile Include="Subtitles\SubtitleResponse.cs" />
@@ -403,10 +368,7 @@
<Name>MediaBrowser.Model</Name>
</ProjectReference>
</ItemGroup>
- <ItemGroup>
- <None Include="packages.config" />
- </ItemGroup>
- <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent />
</PropertyGroup>
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.nuget.targets b/MediaBrowser.Controller/MediaBrowser.Controller.nuget.targets
new file mode 100644
index 0000000000..e69ce0e64f
--- /dev/null
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.nuget.targets
@@ -0,0 +1,6 @@
+<?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/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index 866a08aa1f..45aaa8e8e6 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -135,5 +135,8 @@ namespace MediaBrowser.Controller.MediaEncoding
Task UpdateEncoderPath(string path, string pathType);
bool SupportsEncoder(string encoder);
bool IsDefaultEncoderPath { get; }
+
+ void SetLogFilename(string name);
+ void ClearLogFilename();
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
index 96a3753e19..0b1ca08e51 100644
--- a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
+++ b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
@@ -4,7 +4,8 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
namespace MediaBrowser.Controller.MediaEncoding
{
diff --git a/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs b/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs
index 52199c5a21..8169cc7d9f 100644
--- a/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs
+++ b/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs
@@ -69,7 +69,8 @@ namespace MediaBrowser.Controller.MediaEncoding
// if the audio language is not understood by the user, load their preferred subs, if there are any
if (!ContainsOrdinal(preferredLanguages, audioTrackLanguage))
{
- stream = streams.Where(s => !s.IsForced).FirstOrDefault(s => ContainsOrdinal(preferredLanguages, s.Language));
+ stream = streams.Where(s => !s.IsForced).FirstOrDefault(s => ContainsOrdinal(preferredLanguages, s.Language)) ??
+ streams.FirstOrDefault(s => ContainsOrdinal(preferredLanguages, s.Language));
}
}
else if (mode == SubtitlePlaybackMode.Always)
diff --git a/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs b/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs
index b1bab79c5c..b025011d7e 100644
--- a/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs
+++ b/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs
@@ -1,13 +1,13 @@
-using ServiceStack.Web;
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Controller.Net
{
public class AuthenticatedAttribute : Attribute, IHasRequestFilter, IAuthenticationAttributes
{
- public IAuthService AuthService { get; set; }
+ public static IAuthService AuthService { get; set; }
/// <summary>
/// Gets or sets the roles.
@@ -26,7 +26,7 @@ namespace MediaBrowser.Controller.Net
/// </summary>
/// <value><c>true</c> if [allow before startup wizard]; otherwise, <c>false</c>.</value>
public bool AllowBeforeStartupWizard { get; set; }
-
+
/// <summary>
/// The request filter is executed before the service.
/// </summary>
@@ -35,21 +35,12 @@ namespace MediaBrowser.Controller.Net
/// <param name="requestDto">The request DTO</param>
public void RequestFilter(IRequest request, IResponse response, object requestDto)
{
- var serviceRequest = new ServiceStackServiceRequest(request);
+ var serviceRequest = new ServiceRequest(request);
AuthService.Authenticate(serviceRequest, this);
}
/// <summary>
- /// A new shallow copy of this filter is used on every request.
- /// </summary>
- /// <returns>IHasRequestFilter.</returns>
- public IHasRequestFilter Copy()
- {
- return this;
- }
-
- /// <summary>
/// Order in which Request Filters are executed.
/// &lt;0 Executed before global request filters
/// &gt;0 Executed after global request filters
@@ -60,7 +51,6 @@ namespace MediaBrowser.Controller.Net
get { return 0; }
}
-
public IEnumerable<string> GetRoles()
{
return (Roles ?? string.Empty).Split(',')
diff --git a/MediaBrowser.Controller/Net/IAsyncStreamSource.cs b/MediaBrowser.Controller/Net/IAsyncStreamSource.cs
deleted file mode 100644
index 0f41f60a55..0000000000
--- a/MediaBrowser.Controller/Net/IAsyncStreamSource.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using ServiceStack.Web;
-using System.IO;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Net
-{
- /// <summary>
- /// Interface IAsyncStreamSource
- /// Enables asynchronous writing to http resonse streams
- /// </summary>
- public interface IAsyncStreamSource
- {
- /// <summary>
- /// Asynchronously write to the response stream.
- /// </summary>
- Task WriteToAsync(Stream responseStream);
- }
-}
diff --git a/MediaBrowser.Controller/Net/IHasAuthorization.cs b/MediaBrowser.Controller/Net/IHasAuthorization.cs
deleted file mode 100644
index 6fc70159dd..0000000000
--- a/MediaBrowser.Controller/Net/IHasAuthorization.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-
-namespace MediaBrowser.Controller.Net
-{
- public interface IHasAuthorization
- {
- /// <summary>
- /// Gets or sets the authorization context.
- /// </summary>
- /// <value>The authorization context.</value>
- IAuthorizationContext AuthorizationContext { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Net/IHasResultFactory.cs b/MediaBrowser.Controller/Net/IHasResultFactory.cs
index 3988b8d615..03144e4b88 100644
--- a/MediaBrowser.Controller/Net/IHasResultFactory.cs
+++ b/MediaBrowser.Controller/Net/IHasResultFactory.cs
@@ -1,4 +1,4 @@
-using ServiceStack.Web;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Controller.Net
{
diff --git a/MediaBrowser.Controller/Net/IHasSession.cs b/MediaBrowser.Controller/Net/IHasSession.cs
deleted file mode 100644
index e762c1e844..0000000000
--- a/MediaBrowser.Controller/Net/IHasSession.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-
-namespace MediaBrowser.Controller.Net
-{
- public interface IHasSession
- {
- /// <summary>
- /// Gets or sets the session context.
- /// </summary>
- /// <value>The session context.</value>
- ISessionContext SessionContext { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Net/IHttpResultFactory.cs b/MediaBrowser.Controller/Net/IHttpResultFactory.cs
index ca453840fe..9f295c71d8 100644
--- a/MediaBrowser.Controller/Net/IHttpResultFactory.cs
+++ b/MediaBrowser.Controller/Net/IHttpResultFactory.cs
@@ -1,8 +1,10 @@
-using ServiceStack.Web;
-using System;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Controller.Net
{
@@ -20,8 +22,6 @@ namespace MediaBrowser.Controller.Net
/// <returns>System.Object.</returns>
object GetResult(object content, string contentType, IDictionary<string,string> responseHeaders = null);
- object GetAsyncStreamWriter(IAsyncStreamSource streamSource);
-
/// <summary>
/// Gets the optimized result.
/// </summary>
@@ -97,7 +97,7 @@ namespace MediaBrowser.Controller.Net
/// <param name="path">The path.</param>
/// <param name="fileShare">The file share.</param>
/// <returns>System.Object.</returns>
- Task<object> GetStaticFileResult(IRequest requestContext, string path, FileShare fileShare = FileShare.Read);
+ Task<object> GetStaticFileResult(IRequest requestContext, string path, FileShareMode fileShare = FileShareMode.Read);
/// <summary>
/// Gets the static file result.
diff --git a/MediaBrowser.Controller/Net/IHttpServer.cs b/MediaBrowser.Controller/Net/IHttpServer.cs
index 97c5dd31b6..f319244dae 100644
--- a/MediaBrowser.Controller/Net/IHttpServer.cs
+++ b/MediaBrowser.Controller/Net/IHttpServer.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Controller.Net
{
@@ -15,18 +16,10 @@ namespace MediaBrowser.Controller.Net
IEnumerable<string> UrlPrefixes { get; }
/// <summary>
- /// Gets the certificate path.
- /// </summary>
- /// <value>The certificate path.</value>
- string CertificatePath { get; }
-
- /// <summary>
/// Starts the specified server name.
/// </summary>
/// <param name="urlPrefixes">The URL prefixes.</param>
- /// <param name="certificatePath">If an https prefix is specified,
- /// the ssl certificate localtion on the file system.</param>
- void StartServer(IEnumerable<string> urlPrefixes, string certificatePath);
+ void StartServer(IEnumerable<string> urlPrefixes);
/// <summary>
/// Stops this instance.
@@ -46,7 +39,7 @@ namespace MediaBrowser.Controller.Net
/// <summary>
/// Inits this instance.
/// </summary>
- void Init(IEnumerable<IRestfulService> services);
+ void Init(IEnumerable<IService> services);
/// <summary>
/// If set, all requests will respond with this message
diff --git a/MediaBrowser.Controller/Net/IRestfulService.cs b/MediaBrowser.Controller/Net/IRestfulService.cs
deleted file mode 100644
index 7d07bb0941..0000000000
--- a/MediaBrowser.Controller/Net/IRestfulService.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using ServiceStack;
-
-namespace MediaBrowser.Controller.Net
-{
- /// <summary>
- /// Interface IRestfulService
- /// </summary>
- [Logged]
- public interface IRestfulService : IService
- {
- }
-}
diff --git a/MediaBrowser.Controller/Net/IServerManager.cs b/MediaBrowser.Controller/Net/IServerManager.cs
index 5191a62e31..202df29824 100644
--- a/MediaBrowser.Controller/Net/IServerManager.cs
+++ b/MediaBrowser.Controller/Net/IServerManager.cs
@@ -15,9 +15,7 @@ namespace MediaBrowser.Controller.Net
/// Starts this instance.
/// </summary>
/// <param name="urlPrefixes">The URL prefixes.</param>
- /// <param name="certificatePath">If an https prefix is specified,
- /// the ssl certificate localtion on the file system.</param>
- void Start(IEnumerable<string> urlPrefixes, string certificatePath);
+ void Start(IEnumerable<string> urlPrefixes);
/// <summary>
/// Sends a message to all clients currently connected via a web socket
diff --git a/MediaBrowser.Controller/Net/IServiceRequest.cs b/MediaBrowser.Controller/Net/IServiceRequest.cs
index e48247362b..ebc7e8d65d 100644
--- a/MediaBrowser.Controller/Net/IServiceRequest.cs
+++ b/MediaBrowser.Controller/Net/IServiceRequest.cs
@@ -1,14 +1,13 @@
using System.Collections.Generic;
-using System.Collections.Specialized;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Controller.Net
{
public interface IServiceRequest
{
- object OriginalRequest { get; }
string RemoteIp { get; }
- NameValueCollection Headers { get; }
- NameValueCollection QueryString { get; }
+ QueryParamCollection Headers { get; }
+ QueryParamCollection QueryString { get; }
IDictionary<string,object> Items { get; }
void AddResponseHeader(string name, string value);
}
diff --git a/MediaBrowser.Controller/Net/IWebSocketConnection.cs b/MediaBrowser.Controller/Net/IWebSocketConnection.cs
index e21df3c395..dad2388914 100644
--- a/MediaBrowser.Controller/Net/IWebSocketConnection.cs
+++ b/MediaBrowser.Controller/Net/IWebSocketConnection.cs
@@ -1,8 +1,8 @@
using MediaBrowser.Model.Net;
using System;
-using System.Collections.Specialized;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Controller.Net
{
@@ -34,7 +34,7 @@ namespace MediaBrowser.Controller.Net
/// Gets or sets the query string.
/// </summary>
/// <value>The query string.</value>
- NameValueCollection QueryString { get; set; }
+ QueryParamCollection QueryString { get; set; }
/// <summary>
/// Gets or sets the receive action.
diff --git a/MediaBrowser.Controller/Net/LoggedAttribute.cs b/MediaBrowser.Controller/Net/LoggedAttribute.cs
index d56a015a86..6a2a5e2e3b 100644
--- a/MediaBrowser.Controller/Net/LoggedAttribute.cs
+++ b/MediaBrowser.Controller/Net/LoggedAttribute.cs
@@ -2,17 +2,25 @@
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Logging;
-using ServiceStack.Web;
using System;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Controller.Net
{
- public class LoggedAttribute : Attribute, IHasRequestFilter
+ public class LoggedAttribute : IRequestFilter
{
- public ILogger Logger { get; set; }
- public IUserManager UserManager { get; set; }
- public ISessionManager SessionManager { get; set; }
- public IAuthorizationContext AuthorizationContext { get; set; }
+ 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.
@@ -20,9 +28,9 @@ namespace MediaBrowser.Controller.Net
/// <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)
+ public void Filter(IRequest request, IResponse response, object requestDto)
{
- var serviceRequest = new ServiceStackServiceRequest(request);
+ var serviceRequest = new ServiceRequest(request);
//This code is executed before the service
var auth = AuthorizationContext.GetAuthorizationInfo(serviceRequest);
@@ -51,25 +59,5 @@ namespace MediaBrowser.Controller.Net
}
}
}
-
- /// <summary>
- /// A new shallow copy of this filter is used on every request.
- /// </summary>
- /// <returns>IHasRequestFilter.</returns>
- public IHasRequestFilter Copy()
- {
- return 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; }
- }
}
}
diff --git a/MediaBrowser.Controller/Net/ServiceRequest.cs b/MediaBrowser.Controller/Net/ServiceRequest.cs
new file mode 100644
index 0000000000..1f72d0eb29
--- /dev/null
+++ b/MediaBrowser.Controller/Net/ServiceRequest.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using MediaBrowser.Model.Services;
+
+namespace MediaBrowser.Controller.Net
+{
+ public class ServiceRequest : IServiceRequest
+ {
+ private readonly IRequest _request;
+
+ public ServiceRequest(IRequest request)
+ {
+ _request = request;
+ }
+
+ public string RemoteIp
+ {
+ get { return _request.RemoteIp; }
+ }
+
+ public QueryParamCollection Headers
+ {
+ get { return _request.Headers; }
+ }
+
+ public QueryParamCollection QueryString
+ {
+ get { return _request.QueryString; }
+ }
+
+ public IDictionary<string, object> Items
+ {
+ get { return _request.Items; }
+ }
+
+ public void AddResponseHeader(string name, string value)
+ {
+ _request.Response.AddHeader(name, value);
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Net/ServiceStackServiceRequest.cs b/MediaBrowser.Controller/Net/ServiceStackServiceRequest.cs
deleted file mode 100644
index a33e9c1c6e..0000000000
--- a/MediaBrowser.Controller/Net/ServiceStackServiceRequest.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using ServiceStack.Web;
-using System;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-
-namespace MediaBrowser.Controller.Net
-{
- public class ServiceStackServiceRequest : IServiceRequest
- {
- private readonly IRequest _request;
-
- public ServiceStackServiceRequest(IRequest request)
- {
- _request = request;
- }
-
- public object OriginalRequest
- {
- get { return _request; }
- }
-
- public string RemoteIp
- {
- get { return _request.RemoteIp; }
- }
-
- private NameValueCollection _headers;
- public NameValueCollection Headers
- {
- get { return _headers ?? (_headers = Get(_request.Headers)); }
- }
-
- private NameValueCollection _query;
- public NameValueCollection QueryString
- {
- get { return _query ?? (_query = Get(_request.QueryString)); }
- }
-
- private NameValueCollection Get(INameValueCollection coll)
- {
- var nv = new NameValueCollection(StringComparer.OrdinalIgnoreCase);
-
- foreach (var key in coll.AllKeys)
- {
- nv[key] = coll[key];
- }
-
- return nv;
- //return coll.ToNameValueCollection();
- }
-
- public IDictionary<string, object> Items
- {
- get { return _request.Items; }
- }
-
- public void AddResponseHeader(string name, string value)
- {
- _request.Response.AddHeader(name, value);
- }
- }
-}
diff --git a/MediaBrowser.Controller/Net/StaticResultOptions.cs b/MediaBrowser.Controller/Net/StaticResultOptions.cs
index b5efc1b8fb..272fa8b3ba 100644
--- a/MediaBrowser.Controller/Net/StaticResultOptions.cs
+++ b/MediaBrowser.Controller/Net/StaticResultOptions.cs
@@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Controller.Net
{
@@ -31,11 +33,11 @@ namespace MediaBrowser.Controller.Net
{
public string Path { get; set; }
- public FileShare FileShare { get; set; }
+ public FileShareMode FileShare { get; set; }
public StaticFileResultOptions()
{
- FileShare = FileShare.Read;
+ FileShare = FileShareMode.Read;
}
}
}
diff --git a/MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs b/MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs
index ffeaf286e2..0ad80d3748 100644
--- a/MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs
+++ b/MediaBrowser.Controller/Net/WebSocketConnectEventArgs.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Specialized;
+using MediaBrowser.Model.Services;
namespace MediaBrowser.Controller.Net
{
@@ -17,7 +18,7 @@ namespace MediaBrowser.Controller.Net
/// Gets or sets the query string.
/// </summary>
/// <value>The query string.</value>
- public NameValueCollection QueryString { get; set; }
+ public QueryParamCollection QueryString { get; set; }
/// <summary>
/// Gets or sets the web socket.
/// </summary>
@@ -46,7 +47,7 @@ namespace MediaBrowser.Controller.Net
/// Gets or sets the query string.
/// </summary>
/// <value>The query string.</value>
- public NameValueCollection QueryString { get; set; }
+ public QueryParamCollection QueryString { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [allow connection].
/// </summary>
@@ -55,7 +56,7 @@ namespace MediaBrowser.Controller.Net
public WebSocketConnectingEventArgs()
{
- QueryString = new NameValueCollection();
+ QueryString = new QueryParamCollection();
AllowConnection = true;
}
}
diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs
index 87937869d5..58ae1f3b0e 100644
--- a/MediaBrowser.Controller/Persistence/IItemRepository.cs
+++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs
@@ -51,7 +51,7 @@ namespace MediaBrowser.Controller.Persistence
/// <param name="items">The items.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
- Task SaveItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken);
+ Task SaveItems(List<BaseItem> items, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the item.
@@ -147,7 +147,7 @@ namespace MediaBrowser.Controller.Persistence
/// </summary>
/// <param name="query">The query.</param>
/// <returns>QueryResult&lt;Tuple&lt;Guid, System.String&gt;&gt;.</returns>
- QueryResult<Tuple<Guid, string>> GetItemIdsWithPath(InternalItemsQuery query);
+ List<Tuple<Guid, string>> GetItemIdsWithPath(InternalItemsQuery query);
/// <summary>
/// Gets the item list.
@@ -163,6 +163,8 @@ namespace MediaBrowser.Controller.Persistence
/// <returns>Task.</returns>
Task 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);
diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs
index 6ab85f079c..02e736d999 100644
--- a/MediaBrowser.Controller/Playlists/Playlist.cs
+++ b/MediaBrowser.Controller/Playlists/Playlist.cs
@@ -5,7 +5,7 @@ using MediaBrowser.Model.Querying;
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
using System.Threading.Tasks;
using MediaBrowser.Controller.Providers;
diff --git a/MediaBrowser.Controller/Properties/AssemblyInfo.cs b/MediaBrowser.Controller/Properties/AssemblyInfo.cs
index a5abf79ee1..844b93f37d 100644
--- a/MediaBrowser.Controller/Properties/AssemblyInfo.cs
+++ b/MediaBrowser.Controller/Properties/AssemblyInfo.cs
@@ -18,9 +18,6 @@ using System.Runtime.InteropServices;
// 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("bc09905a-04ed-497d-b39b-27593401e715")]
-
// Version information for an assembly consists of the following four values:
//
// Major Version
diff --git a/MediaBrowser.Controller/Providers/DirectoryService.cs b/MediaBrowser.Controller/Providers/DirectoryService.cs
index ee2b28c60a..01218c293a 100644
--- a/MediaBrowser.Controller/Providers/DirectoryService.cs
+++ b/MediaBrowser.Controller/Providers/DirectoryService.cs
@@ -4,7 +4,9 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Controller.Providers
{
@@ -74,7 +76,7 @@ namespace MediaBrowser.Controller.Providers
entries[item.FullName] = item;
}
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
}
diff --git a/MediaBrowser.Controller/Providers/IDirectoryService.cs b/MediaBrowser.Controller/Providers/IDirectoryService.cs
index 09f9ba8c02..54ae7e12be 100644
--- a/MediaBrowser.Controller/Providers/IDirectoryService.cs
+++ b/MediaBrowser.Controller/Providers/IDirectoryService.cs
@@ -1,5 +1,7 @@
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Controller.Providers
{
diff --git a/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs b/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs
index 9b21a29724..691ce1ab07 100644
--- a/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs
+++ b/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs
@@ -12,6 +12,7 @@ namespace MediaBrowser.Controller.Providers
public List<ImageType> ReplaceImages { get; set; }
public bool IsAutomated { get; set; }
+ public bool ForceEnableInternetMetadata { get; set; }
public ImageRefreshOptions(IDirectoryService directoryService)
{
diff --git a/MediaBrowser.Controller/Providers/LocalImageInfo.cs b/MediaBrowser.Controller/Providers/LocalImageInfo.cs
index 14f292bece..63651fce85 100644
--- a/MediaBrowser.Controller/Providers/LocalImageInfo.cs
+++ b/MediaBrowser.Controller/Providers/LocalImageInfo.cs
@@ -1,5 +1,7 @@
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Controller.Providers
{
diff --git a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
index 87c3b36a27..7d9dab92b2 100644
--- a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
+++ b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
@@ -1,5 +1,7 @@
using System.Linq;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Providers;
diff --git a/MediaBrowser.Controller/Resolvers/IItemResolver.cs b/MediaBrowser.Controller/Resolvers/IItemResolver.cs
index bdff5fffad..fcb162b9e6 100644
--- a/MediaBrowser.Controller/Resolvers/IItemResolver.cs
+++ b/MediaBrowser.Controller/Resolvers/IItemResolver.cs
@@ -2,7 +2,9 @@
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Controller.Resolvers
{
diff --git a/MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs b/MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs
index 2c82882c68..06372395ec 100644
--- a/MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs
+++ b/MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs
@@ -1,5 +1,7 @@
-using CommonIO;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Controller.Resolvers
{
diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs
index b3e82f9258..343b15a04f 100644
--- a/MediaBrowser.Controller/Session/SessionInfo.cs
+++ b/MediaBrowser.Controller/Session/SessionInfo.cs
@@ -3,6 +3,7 @@ using MediaBrowser.Model.Session;
using System;
using System.Collections.Generic;
using System.Linq;
+using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Controller.Session
{
@@ -106,7 +107,9 @@ namespace MediaBrowser.Controller.Session
/// </summary>
/// <value>The now playing item.</value>
public BaseItemInfo NowPlayingItem { get; set; }
-
+
+ public BaseItem FullNowPlayingItem { get; set; }
+
/// <summary>
/// Gets or sets the device id.
/// </summary>
diff --git a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs
index 36ad27290d..3913e86f03 100644
--- a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs
+++ b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs
@@ -1,10 +1,10 @@
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Sync;
-using Interfaces.IO;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Controller.Sync
{
@@ -40,14 +40,8 @@ namespace MediaBrowser.Controller.Sync
/// <returns>Task&lt;Stream&gt;.</returns>
Task<Stream> GetFile(string id, SyncTarget target, IProgress<double> progress, CancellationToken cancellationToken);
- /// <summary>
- /// Gets the files.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="target">The target.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task&lt;QueryResult&lt;FileMetadata&gt;&gt;.</returns>
- Task<QueryResult<FileMetadata>> GetFiles(FileQuery query, SyncTarget target, CancellationToken cancellationToken);
+ Task<QueryResult<FileSystemMetadata>> GetFiles(string[] pathParts, SyncTarget target, CancellationToken cancellationToken);
+ Task<QueryResult<FileSystemMetadata>> GetFiles(SyncTarget target, CancellationToken cancellationToken);
}
public interface ISupportsDirectCopy
diff --git a/MediaBrowser.Controller/TV/ITVSeriesManager.cs b/MediaBrowser.Controller/TV/ITVSeriesManager.cs
index 3d6e87474c..771fa602a0 100644
--- a/MediaBrowser.Controller/TV/ITVSeriesManager.cs
+++ b/MediaBrowser.Controller/TV/ITVSeriesManager.cs
@@ -19,6 +19,6 @@ namespace MediaBrowser.Controller.TV
/// <param name="request">The request.</param>
/// <param name="parentsFolders">The parents folders.</param>
/// <returns>QueryResult&lt;BaseItem&gt;.</returns>
- QueryResult<BaseItem> GetNextUp(NextUpQuery request, IEnumerable<Folder> parentsFolders);
+ QueryResult<BaseItem> GetNextUp(NextUpQuery request, List<Folder> parentsFolders);
}
}
diff --git a/MediaBrowser.Controller/packages.config b/MediaBrowser.Controller/packages.config
deleted file mode 100644
index 84422d9da9..0000000000
--- a/MediaBrowser.Controller/packages.config
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
- <package id="CommonIO" version="1.0.0.9" targetFramework="net45" />
- <package id="Interfaces.IO" version="1.0.0.5" targetFramework="net45" />
- <package id="morelinq" version="1.4.0" targetFramework="net45" />
- <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
-</packages> \ No newline at end of file
diff --git a/MediaBrowser.Controller/project.json b/MediaBrowser.Controller/project.json
new file mode 100644
index 0000000000..fbbe9eaf32
--- /dev/null
+++ b/MediaBrowser.Controller/project.json
@@ -0,0 +1,17 @@
+{
+ "frameworks":{
+ "netstandard1.6":{
+ "dependencies":{
+ "NETStandard.Library":"1.6.0",
+ }
+ },
+ ".NETPortable,Version=v4.5,Profile=Profile7":{
+ "buildOptions": {
+ "define": [ ]
+ },
+ "frameworkAssemblies":{
+
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs b/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs
deleted file mode 100644
index 06856989f3..0000000000
--- a/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs
+++ /dev/null
@@ -1,289 +0,0 @@
-namespace MediaBrowser.Dlna.Channels
-{
- //public class DlnaChannel : IChannel, IDisposable
- //{
- // private readonly ILogger _logger;
- // private readonly IHttpClient _httpClient;
- // private readonly IServerConfigurationManager _config;
- // private List<Device> _servers = new List<Device>();
-
- // private readonly IDeviceDiscovery _deviceDiscovery;
- // private readonly SemaphoreSlim _syncLock = new SemaphoreSlim(1, 1);
- // private Func<List<string>> _localServersLookup;
-
- // public static DlnaChannel Current;
-
- // public DlnaChannel(ILogger logger, IHttpClient httpClient, IDeviceDiscovery deviceDiscovery, IServerConfigurationManager config)
- // {
- // _logger = logger;
- // _httpClient = httpClient;
- // _deviceDiscovery = deviceDiscovery;
- // _config = config;
- // Current = this;
- // }
-
- // public string Name
- // {
- // get { return "Devices"; }
- // }
-
- // public string Description
- // {
- // get { return string.Empty; }
- // }
-
- // public string DataVersion
- // {
- // get { return DateTime.UtcNow.Ticks.ToString(); }
- // }
-
- // public string HomePageUrl
- // {
- // get { return string.Empty; }
- // }
-
- // public ChannelParentalRating ParentalRating
- // {
- // get { return ChannelParentalRating.GeneralAudience; }
- // }
-
- // public InternalChannelFeatures GetChannelFeatures()
- // {
- // return new InternalChannelFeatures
- // {
- // ContentTypes = new List<ChannelMediaContentType>
- // {
- // ChannelMediaContentType.Song,
- // ChannelMediaContentType.Clip
- // },
-
- // MediaTypes = new List<ChannelMediaType>
- // {
- // ChannelMediaType.Audio,
- // ChannelMediaType.Video,
- // ChannelMediaType.Photo
- // }
- // };
- // }
-
- // public bool IsEnabledFor(string userId)
- // {
- // return true;
- // }
-
- // public Task<DynamicImageResponse> GetChannelImage(ImageType type, CancellationToken cancellationToken)
- // {
- // throw new NotImplementedException();
- // }
-
- // public IEnumerable<ImageType> GetSupportedChannelImages()
- // {
- // return new List<ImageType>
- // {
- // ImageType.Primary
- // };
- // }
-
- // public void Start(Func<List<string>> localServersLookup)
- // {
- // _localServersLookup = localServersLookup;
-
- // _deviceDiscovery.DeviceDiscovered -= deviceDiscovery_DeviceDiscovered;
- // _deviceDiscovery.DeviceLeft -= deviceDiscovery_DeviceLeft;
-
- // _deviceDiscovery.DeviceDiscovered += deviceDiscovery_DeviceDiscovered;
- // _deviceDiscovery.DeviceLeft += deviceDiscovery_DeviceLeft;
- // }
-
- // public async Task<ChannelItemResult> GetChannelItems(InternalChannelItemQuery query, CancellationToken cancellationToken)
- // {
- // if (string.IsNullOrWhiteSpace(query.FolderId))
- // {
- // return await GetServers(query, cancellationToken).ConfigureAwait(false);
- // }
-
- // return new ChannelItemResult();
-
- // //var idParts = query.FolderId.Split('|');
- // //var folderId = idParts.Length == 2 ? idParts[1] : null;
-
- // //var result = await new ContentDirectoryBrowser(_httpClient, _logger).Browse(new ContentDirectoryBrowseRequest
- // //{
- // // Limit = query.Limit,
- // // StartIndex = query.StartIndex,
- // // ParentId = folderId,
- // // ContentDirectoryUrl = ControlUrl
-
- // //}, cancellationToken).ConfigureAwait(false);
-
- // //items = result.Items.ToList();
-
- // //var list = items.ToList();
- // //var count = list.Count;
-
- // //list = ApplyPaging(list, query).ToList();
-
- // //return new ChannelItemResult
- // //{
- // // Items = list,
- // // TotalRecordCount = count
- // //};
- // }
-
- // public async Task<ChannelItemResult> GetServers(InternalChannelItemQuery query, CancellationToken cancellationToken)
- // {
- // await _syncLock.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- // try
- // {
- // var items = _servers.Select(i =>
- // {
- // var service = i.Properties.Services
- // .FirstOrDefault(s => string.Equals(s.ServiceType, "urn:schemas-upnp-org:service:ContentDirectory:1", StringComparison.OrdinalIgnoreCase));
-
- // var controlUrl = service == null ? null : (_servers[0].Properties.BaseUrl.TrimEnd('/') + "/" + service.ControlUrl.TrimStart('/'));
-
- // if (string.IsNullOrWhiteSpace(controlUrl))
- // {
- // return null;
- // }
-
- // return new ChannelItemInfo
- // {
- // Id = i.Properties.UUID,
- // Name = i.Properties.Name,
- // Type = ChannelItemType.Folder
- // };
-
- // }).Where(i => i != null).ToList();
-
- // return new ChannelItemResult
- // {
- // TotalRecordCount = items.Count,
- // Items = items
- // };
- // }
- // finally
- // {
- // _syncLock.Release();
- // }
- // }
-
- // async void deviceDiscovery_DeviceDiscovered(object sender, SsdpMessageEventArgs e)
- // {
- // string usn;
- // if (!e.Headers.TryGetValue("USN", out usn)) usn = string.Empty;
-
- // string nt;
- // if (!e.Headers.TryGetValue("NT", out nt)) nt = string.Empty;
-
- // string location;
- // if (!e.Headers.TryGetValue("Location", out location)) location = string.Empty;
-
- // if (!IsValid(nt, usn))
- // {
- // return;
- // }
-
- // if (_localServersLookup != null)
- // {
- // if (_localServersLookup().Any(i => usn.IndexOf(i, StringComparison.OrdinalIgnoreCase) != -1))
- // {
- // // Don't add the local Dlna server to this
- // return;
- // }
- // }
-
- // await _syncLock.WaitAsync().ConfigureAwait(false);
-
- // var serverList = _servers.ToList();
-
- // try
- // {
- // if (GetExistingServers(serverList, usn).Any())
- // {
- // return;
- // }
-
- // var device = await Device.CreateuPnpDeviceAsync(new Uri(location), _httpClient, _config, _logger)
- // .ConfigureAwait(false);
-
- // if (!serverList.Any(i => string.Equals(i.Properties.UUID, device.Properties.UUID, StringComparison.OrdinalIgnoreCase)))
- // {
- // serverList.Add(device);
- // }
- // }
- // catch (Exception ex)
- // {
-
- // }
- // finally
- // {
- // _syncLock.Release();
- // }
- // }
-
- // async void deviceDiscovery_DeviceLeft(object sender, SsdpMessageEventArgs e)
- // {
- // string usn;
- // if (!e.Headers.TryGetValue("USN", out usn)) usn = String.Empty;
-
- // string nt;
- // if (!e.Headers.TryGetValue("NT", out nt)) nt = String.Empty;
-
- // if (!IsValid(nt, usn))
- // {
- // return;
- // }
-
- // await _syncLock.WaitAsync().ConfigureAwait(false);
-
- // try
- // {
- // var serverList = _servers.ToList();
-
- // var matchingServers = GetExistingServers(serverList, usn);
- // if (matchingServers.Count > 0)
- // {
- // foreach (var device in matchingServers)
- // {
- // serverList.Remove(device);
- // }
-
- // _servers = serverList;
- // }
- // }
- // finally
- // {
- // _syncLock.Release();
- // }
- // }
-
- // private bool IsValid(string nt, string usn)
- // {
- // // It has to report that it's a media renderer
- // if (usn.IndexOf("ContentDirectory:", StringComparison.OrdinalIgnoreCase) == -1 &&
- // nt.IndexOf("ContentDirectory:", StringComparison.OrdinalIgnoreCase) == -1 &&
- // usn.IndexOf("MediaServer:", StringComparison.OrdinalIgnoreCase) == -1 &&
- // nt.IndexOf("MediaServer:", StringComparison.OrdinalIgnoreCase) == -1)
- // {
- // return false;
- // }
-
- // return true;
- // }
-
- // private List<Device> GetExistingServers(List<Device> allDevices, string usn)
- // {
- // return allDevices
- // .Where(i => usn.IndexOf(i.Properties.UUID, StringComparison.OrdinalIgnoreCase) != -1)
- // .ToList();
- // }
-
- // public void Dispose()
- // {
- // _deviceDiscovery.DeviceDiscovered -= deviceDiscovery_DeviceDiscovered;
- // _deviceDiscovery.DeviceLeft -= deviceDiscovery_DeviceLeft;
- // }
- //}
-}
diff --git a/MediaBrowser.Dlna/Server/Headers.cs b/MediaBrowser.Dlna/Server/Headers.cs
deleted file mode 100644
index 1e63771c2b..0000000000
--- a/MediaBrowser.Dlna/Server/Headers.cs
+++ /dev/null
@@ -1,176 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-
-namespace MediaBrowser.Dlna.Server
-{
- public class Headers : IDictionary<string, string>
- {
- private readonly bool _asIs = false;
- private readonly Dictionary<string, string> _dict = new Dictionary<string, string>();
- private readonly static Regex Validator = new Regex(@"^[a-z\d][a-z\d_.-]+$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
-
- public Headers(bool asIs)
- {
- _asIs = asIs;
- }
-
- public Headers()
- : this(asIs: false)
- {
- }
-
- public int Count
- {
- get
- {
- return _dict.Count;
- }
- }
- public string HeaderBlock
- {
- get
- {
- var hb = new StringBuilder();
- foreach (var h in this)
- {
- hb.AppendFormat("{0}: {1}\r\n", h.Key, h.Value);
- }
- return hb.ToString();
- }
- }
- public Stream HeaderStream
- {
- get
- {
- return new MemoryStream(Encoding.ASCII.GetBytes(HeaderBlock));
- }
- }
- public bool IsReadOnly
- {
- get
- {
- return false;
- }
- }
- public ICollection<string> Keys
- {
- get
- {
- return _dict.Keys;
- }
- }
- public ICollection<string> Values
- {
- get
- {
- return _dict.Values;
- }
- }
-
-
- public string this[string key]
- {
- get
- {
- return _dict[Normalize(key)];
- }
- set
- {
- _dict[Normalize(key)] = value;
- }
- }
-
-
- private string Normalize(string header)
- {
- if (!_asIs)
- {
- header = header.ToLower();
- }
- header = header.Trim();
- if (!Validator.IsMatch(header))
- {
- throw new ArgumentException("Invalid header: " + header);
- }
- return header;
- }
-
- System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
- {
- return _dict.GetEnumerator();
- }
-
- public void Add(KeyValuePair<string, string> item)
- {
- Add(item.Key, item.Value);
- }
-
- public void Add(string key, string value)
- {
- _dict.Add(Normalize(key), value);
- }
-
- public void Clear()
- {
- _dict.Clear();
- }
-
- public bool Contains(KeyValuePair<string, string> item)
- {
- var p = new KeyValuePair<string, string>(Normalize(item.Key), item.Value);
- return _dict.Contains(p);
- }
-
- public bool ContainsKey(string key)
- {
- return _dict.ContainsKey(Normalize(key));
- }
-
- public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex)
- {
- throw new NotImplementedException();
- }
-
- public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
- {
- return _dict.GetEnumerator();
- }
-
- public bool Remove(string key)
- {
- return _dict.Remove(Normalize(key));
- }
-
- public bool Remove(KeyValuePair<string, string> item)
- {
- return Remove(item.Key);
- }
-
- public override string ToString()
- {
- return string.Format("({0})", string.Join(", ", (from x in _dict
- select string.Format("{0}={1}", x.Key, x.Value))));
- }
-
- public bool TryGetValue(string key, out string value)
- {
- return _dict.TryGetValue(Normalize(key), out value);
- }
-
- public string GetValueOrDefault(string key, string defaultValue)
- {
- string val;
-
- if (TryGetValue(key, out val))
- {
- return val;
- }
-
- return defaultValue;
- }
- }
-}
diff --git a/MediaBrowser.Dlna/Service/BaseControlHandler.cs b/MediaBrowser.Dlna/Service/BaseControlHandler.cs
deleted file mode 100644
index c5de76eb5f..0000000000
--- a/MediaBrowser.Dlna/Service/BaseControlHandler.cs
+++ /dev/null
@@ -1,137 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Dlna.Server;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Xml;
-
-namespace MediaBrowser.Dlna.Service
-{
- public abstract class BaseControlHandler
- {
- private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
-
- protected readonly IServerConfigurationManager Config;
- protected readonly ILogger Logger;
-
- protected BaseControlHandler(IServerConfigurationManager config, ILogger logger)
- {
- Config = config;
- Logger = logger;
- }
-
- public ControlResponse ProcessControlRequest(ControlRequest request)
- {
- try
- {
- var enableDebugLogging = Config.GetDlnaConfiguration().EnableDebugLog;
-
- if (enableDebugLogging)
- {
- LogRequest(request);
- }
-
- var response = ProcessControlRequestInternal(request);
-
- if (enableDebugLogging)
- {
- LogResponse(response);
- }
-
- return response;
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error processing control request", ex);
-
- return new ControlErrorHandler().GetResponse(ex);
- }
- }
-
- private ControlResponse ProcessControlRequestInternal(ControlRequest request)
- {
- var soap = new XmlDocument();
- soap.LoadXml(request.InputXml);
- var sparams = new Headers();
- var body = soap.GetElementsByTagName("Body", NS_SOAPENV).Item(0);
-
- var method = body.FirstChild;
-
- foreach (var p in method.ChildNodes)
- {
- var e = p as XmlElement;
- if (e == null)
- {
- continue;
- }
- sparams.Add(e.LocalName, e.InnerText.Trim());
- }
-
- Logger.Debug("Received control request {0}", method.LocalName);
-
- var result = GetResult(method.LocalName, sparams);
-
- var env = new XmlDocument();
- env.AppendChild(env.CreateXmlDeclaration("1.0", "utf-8", string.Empty));
- var envelope = env.CreateElement("SOAP-ENV", "Envelope", NS_SOAPENV);
- env.AppendChild(envelope);
- envelope.SetAttribute("encodingStyle", NS_SOAPENV, "http://schemas.xmlsoap.org/soap/encoding/");
-
- var rbody = env.CreateElement("SOAP-ENV:Body", NS_SOAPENV);
- env.DocumentElement.AppendChild(rbody);
-
- var response = env.CreateElement(String.Format("u:{0}Response", method.LocalName), method.NamespaceURI);
- rbody.AppendChild(response);
-
- foreach (var i in result)
- {
- var ri = env.CreateElement(i.Key);
- ri.InnerText = i.Value;
- response.AppendChild(ri);
- }
-
- var xml = env.OuterXml.Replace("xmlns:m=", "xmlns:u=");
-
- var controlResponse = new ControlResponse
- {
- Xml = xml,
- IsSuccessful = true
- };
-
- //Logger.Debug(xml);
-
- controlResponse.Headers.Add("EXT", string.Empty);
-
- return controlResponse;
- }
-
- protected abstract IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams);
-
- private void LogRequest(ControlRequest request)
- {
- var builder = new StringBuilder();
-
- var headers = string.Join(", ", request.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray());
- builder.AppendFormat("Headers: {0}", headers);
- builder.AppendLine();
- builder.Append(request.InputXml);
-
- Logger.LogMultiline("Control request", LogSeverity.Debug, builder);
- }
-
- private void LogResponse(ControlResponse response)
- {
- var builder = new StringBuilder();
-
- var headers = string.Join(", ", response.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray());
- builder.AppendFormat("Headers: {0}", headers);
- builder.AppendLine();
- builder.Append(response.Xml);
-
- Logger.LogMultiline("Control response", LogSeverity.Debug, builder);
- }
- }
-}
diff --git a/MediaBrowser.Dlna/Service/ControlErrorHandler.cs b/MediaBrowser.Dlna/Service/ControlErrorHandler.cs
deleted file mode 100644
index 42b1fcbc99..0000000000
--- a/MediaBrowser.Dlna/Service/ControlErrorHandler.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using MediaBrowser.Controller.Dlna;
-using System;
-using System.Xml;
-
-namespace MediaBrowser.Dlna.Service
-{
- public class ControlErrorHandler
- {
- private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
-
- public ControlResponse GetResponse(Exception ex)
- {
- var env = new XmlDocument();
- env.AppendChild(env.CreateXmlDeclaration("1.0", "utf-8", "yes"));
- var envelope = env.CreateElement("SOAP-ENV", "Envelope", NS_SOAPENV);
- env.AppendChild(envelope);
- envelope.SetAttribute("encodingStyle", NS_SOAPENV, "http://schemas.xmlsoap.org/soap/encoding/");
-
- var rbody = env.CreateElement("SOAP-ENV:Body", NS_SOAPENV);
- env.DocumentElement.AppendChild(rbody);
-
- var fault = env.CreateElement("SOAP-ENV", "Fault", NS_SOAPENV);
- var faultCode = env.CreateElement("faultcode");
- faultCode.InnerText = "500";
- fault.AppendChild(faultCode);
- var faultString = env.CreateElement("faultstring");
- faultString.InnerText = ex.ToString();
- fault.AppendChild(faultString);
- var detail = env.CreateDocumentFragment();
- detail.InnerXml = "<detail><UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\"><errorCode>401</errorCode><errorDescription>Invalid Action</errorDescription></UPnPError></detail>";
- fault.AppendChild(detail);
- rbody.AppendChild(fault);
-
- return new ControlResponse
- {
- Xml = env.OuterXml,
- IsSuccessful = false
- };
- }
- }
-}
diff --git a/MediaBrowser.Dlna/packages.config b/MediaBrowser.Dlna/packages.config
deleted file mode 100644
index 4f2cbae7cb..0000000000
--- a/MediaBrowser.Dlna/packages.config
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
- <package id="CommonIO" version="1.0.0.9" targetFramework="net45" />
- <package id="morelinq" version="1.4.0" targetFramework="net45" />
- <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
-</packages> \ No newline at end of file
diff --git a/MediaBrowser.LocalMetadata/BaseXmlProvider.cs b/MediaBrowser.LocalMetadata/BaseXmlProvider.cs
index f23559e4be..50e9de727f 100644
--- a/MediaBrowser.LocalMetadata/BaseXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/BaseXmlProvider.cs
@@ -3,7 +3,9 @@ using MediaBrowser.Controller.Providers;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata
{
@@ -38,7 +40,7 @@ namespace MediaBrowser.LocalMetadata
{
result.HasMetadata = false;
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
result.HasMetadata = false;
}
@@ -94,7 +96,5 @@ namespace MediaBrowser.LocalMetadata
return "Emby Xml";
}
}
-
- internal static readonly SemaphoreSlim XmlParsingResourcePool = new SemaphoreSlim(4, 4);
}
}
diff --git a/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs b/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs
index 21b3cedae0..651273eb08 100644
--- a/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs
@@ -1,7 +1,9 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Images
{
diff --git a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs
index 716d40d8fd..e023268388 100644
--- a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs
@@ -6,7 +6,9 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Images
{
diff --git a/MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs b/MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs
index 6034008dee..8a6dde6c4a 100644
--- a/MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs
@@ -1,8 +1,10 @@
using System.Collections.Generic;
using System.IO;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Providers;
namespace MediaBrowser.LocalMetadata.Images
@@ -47,7 +49,7 @@ namespace MediaBrowser.LocalMetadata.Images
{
return new LocalImageProvider(_fileSystem).GetImages(item, path, directoryService);
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
return new List<LocalImageInfo>();
}
diff --git a/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs b/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs
index 1cec4d3051..b29182d96a 100644
--- a/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs
@@ -4,7 +4,9 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
using System.Collections.Generic;
using System.IO;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Images
{
@@ -67,7 +69,7 @@ namespace MediaBrowser.LocalMetadata.Images
{
return new LocalImageProvider(_fileSystem).GetImages(item, path, directoryService);
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
return new List<LocalImageInfo>();
}
diff --git a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
index ef9160b70a..2f7059fec5 100644
--- a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
@@ -7,7 +7,9 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Images
{
diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
index ac127458e3..f7b7fa5f26 100644
--- a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
+++ b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<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>
@@ -9,9 +9,11 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MediaBrowser.LocalMetadata</RootNamespace>
<AssemblyName>MediaBrowser.LocalMetadata</AssemblyName>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<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>
@@ -31,22 +33,6 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
- <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
- </Reference>
- <Reference Include="Patterns.Logging">
- <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.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.Xml" />
- </ItemGroup>
- <ItemGroup>
<Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link>
</Compile>
@@ -56,6 +42,7 @@
<Compile Include="Images\ImagesByNameImageProvider.cs" />
<Compile Include="Images\InternalMetadataFolderImageProvider.cs" />
<Compile Include="Images\LocalImageProvider.cs" />
+ <Compile Include="Parsers\BaseItemXmlParser.cs" />
<Compile Include="Parsers\BoxSetXmlParser.cs" />
<Compile Include="Parsers\EpisodeXmlParser.cs" />
<Compile Include="Parsers\GameSystemXmlParser.cs" />
@@ -72,17 +59,16 @@
<Compile Include="Providers\GameXmlProvider.cs" />
<Compile Include="Providers\MovieXmlProvider.cs" />
<Compile Include="Providers\MusicVideoXmlProvider.cs" />
- <Compile Include="Providers\PersonXmlProvider.cs" />
<Compile Include="Providers\PlaylistXmlProvider.cs" />
<Compile Include="Providers\SeriesXmlProvider.cs" />
<Compile Include="Providers\VideoXmlProvider.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" />
- <Compile Include="Savers\XmlSaverHelpers.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
@@ -98,10 +84,7 @@
<Name>MediaBrowser.Model</Name>
</ProjectReference>
</ItemGroup>
- <ItemGroup>
- <None Include="packages.config" />
- </ItemGroup>
- <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <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">
diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.nuget.targets b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.nuget.targets
new file mode 100644
index 0000000000..e69ce0e64f
--- /dev/null
+++ b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.nuget.targets
@@ -0,0 +1,6 @@
+<?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/Providers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
index 931af293cc..d66cdf801c 100644
--- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
@@ -1,7 +1,4 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using System;
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@@ -9,8 +6,14 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Xml;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Xml;
-namespace MediaBrowser.Controller.Providers
+namespace MediaBrowser.LocalMetadata.Parsers
{
/// <summary>
/// Provides a base class for parsing metadata xml
@@ -27,14 +30,19 @@ namespace MediaBrowser.Controller.Providers
private Dictionary<string, string> _validProviderIds;
+ protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; }
+ protected IFileSystem FileSystem { get; private set; }
+
/// <summary>
/// Initializes a new instance of the <see cref="BaseItemXmlParser{T}" /> class.
/// </summary>
/// <param name="logger">The logger.</param>
- public BaseItemXmlParser(ILogger logger, IProviderManager providerManager)
+ public BaseItemXmlParser(ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory, IFileSystem fileSystem)
{
Logger = logger;
ProviderManager = providerManager;
+ XmlReaderSettingsFactory = xmlReaderSettingsFactory;
+ FileSystem = fileSystem;
}
/// <summary>
@@ -56,15 +64,13 @@ namespace MediaBrowser.Controller.Providers
throw new ArgumentNullException();
}
- var settings = new XmlReaderSettings
- {
- CheckCharacters = false,
- IgnoreProcessingInstructions = true,
- IgnoreComments = true,
- ValidationType = ValidationType.None
- };
+ var settings = XmlReaderSettingsFactory.Create(false);
+
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
- _validProviderIds = _validProviderIds = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
+ _validProviderIds = _validProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var idInfos = ProviderManager.GetExternalIdInfos(item.Item);
@@ -96,21 +102,29 @@ namespace MediaBrowser.Controller.Providers
{
item.ResetPeople();
- using (var streamReader = new StreamReader(metadataFile, encoding))
+ using (Stream fileStream = FileSystem.OpenRead(metadataFile))
{
- // Use XmlReader for best performance
- using (var reader = XmlReader.Create(streamReader, settings))
+ using (var streamReader = new StreamReader(fileStream, encoding))
{
- reader.MoveToContent();
-
- // Loop through each element
- while (reader.Read())
+ // Use XmlReader for best performance
+ using (var reader = XmlReader.Create(streamReader, settings))
{
- cancellationToken.ThrowIfCancellationRequested();
+ reader.MoveToContent();
+ reader.Read();
- if (reader.NodeType == XmlNodeType.Element)
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
- FetchDataFromXmlNode(reader, item);
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ FetchDataFromXmlNode(reader, item);
+ }
+ else
+ {
+ reader.Read();
+ }
}
}
}
@@ -154,13 +168,9 @@ namespace MediaBrowser.Controller.Providers
{
var val = reader.ReadElementContentAsString();
- var hasOriginalTitle = item as IHasOriginalTitle;
- if (hasOriginalTitle != null)
+ if (!string.IsNullOrEmpty(val))
{
- if (!string.IsNullOrEmpty(hasOriginalTitle.OriginalTitle))
- {
- hasOriginalTitle.OriginalTitle = val;
- }
+ item.OriginalTitle = val;
}
break;
}
@@ -385,18 +395,32 @@ namespace MediaBrowser.Controller.Providers
case "TagLines":
{
- using (var subtree = reader.ReadSubtree())
+ if (!reader.IsEmptyElement)
+ {
+ using (var subtree = reader.ReadSubtree())
+ {
+ FetchFromTaglinesNode(subtree, item);
+ }
+ }
+ else
{
- FetchFromTaglinesNode(subtree, item);
+ reader.Read();
}
break;
}
case "Countries":
{
- using (var subtree = reader.ReadSubtree())
+ if (!reader.IsEmptyElement)
{
- FetchFromCountriesNode(subtree, item);
+ using (var subtree = reader.ReadSubtree())
+ {
+ FetchFromCountriesNode(subtree, item);
+ }
+ }
+ else
+ {
+ reader.Read();
}
break;
}
@@ -488,7 +512,7 @@ namespace MediaBrowser.Controller.Providers
case "Director":
{
- foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new Entities.PersonInfo { Name = v.Trim(), Type = PersonType.Director }))
+ foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new Controller.Entities.PersonInfo { Name = v.Trim(), Type = PersonType.Director }))
{
if (string.IsNullOrWhiteSpace(p.Name))
{
@@ -500,7 +524,7 @@ namespace MediaBrowser.Controller.Providers
}
case "Writer":
{
- foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new Entities.PersonInfo { Name = v.Trim(), Type = PersonType.Writer }))
+ foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new Controller.Entities.PersonInfo { Name = v.Trim(), Type = PersonType.Writer }))
{
if (string.IsNullOrWhiteSpace(p.Name))
{
@@ -520,12 +544,12 @@ namespace MediaBrowser.Controller.Providers
{
// This is one of the mis-named "Actors" full nodes created by MB2
// Create a reader and pass it to the persons node processor
- FetchDataFromPersonsNode(new XmlTextReader(new StringReader("<Persons>" + actors + "</Persons>")), itemResult);
+ FetchDataFromPersonsNode(XmlReader.Create(new StringReader("<Persons>" + actors + "</Persons>")), itemResult);
}
else
{
// Old-style piped string
- foreach (var p in SplitNames(actors).Select(v => new Entities.PersonInfo { Name = v.Trim(), Type = PersonType.Actor }))
+ foreach (var p in SplitNames(actors).Select(v => new Controller.Entities.PersonInfo { Name = v.Trim(), Type = PersonType.Actor }))
{
if (string.IsNullOrWhiteSpace(p.Name))
{
@@ -539,7 +563,7 @@ namespace MediaBrowser.Controller.Providers
case "GuestStars":
{
- foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new Entities.PersonInfo { Name = v.Trim(), Type = PersonType.GuestStar }))
+ foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new Controller.Entities.PersonInfo { Name = v.Trim(), Type = PersonType.GuestStar }))
{
if (string.IsNullOrWhiteSpace(p.Name))
{
@@ -582,14 +606,21 @@ namespace MediaBrowser.Controller.Providers
case "Trailers":
{
- using (var subtree = reader.ReadSubtree())
+ if (!reader.IsEmptyElement)
{
- var hasTrailers = item as IHasTrailers;
- if (hasTrailers != null)
+ using (var subtree = reader.ReadSubtree())
{
- FetchDataFromTrailersNode(subtree, hasTrailers);
+ var hasTrailers = item as IHasTrailers;
+ if (hasTrailers != null)
+ {
+ FetchDataFromTrailersNode(subtree, hasTrailers);
+ }
}
}
+ else
+ {
+ reader.Read();
+ }
break;
}
@@ -689,70 +720,112 @@ namespace MediaBrowser.Controller.Providers
case "Genres":
{
- using (var subtree = reader.ReadSubtree())
+ if (!reader.IsEmptyElement)
{
- FetchFromGenresNode(subtree, item);
+ using (var subtree = reader.ReadSubtree())
+ {
+ FetchFromGenresNode(subtree, item);
+ }
+ }
+ else
+ {
+ reader.Read();
}
break;
}
case "Tags":
{
- using (var subtree = reader.ReadSubtree())
+ if (!reader.IsEmptyElement)
+ {
+ using (var subtree = reader.ReadSubtree())
+ {
+ FetchFromTagsNode(subtree, item);
+ }
+ }
+ else
{
- FetchFromTagsNode(subtree, item);
+ reader.Read();
}
break;
}
case "PlotKeywords":
{
- using (var subtree = reader.ReadSubtree())
+ if (!reader.IsEmptyElement)
{
- FetchFromKeywordsNode(subtree, item);
+ using (var subtree = reader.ReadSubtree())
+ {
+ FetchFromKeywordsNode(subtree, item);
+ }
+ }
+ else
+ {
+ reader.Read();
}
break;
}
case "Persons":
{
- using (var subtree = reader.ReadSubtree())
+ if (!reader.IsEmptyElement)
{
- FetchDataFromPersonsNode(subtree, itemResult);
+ using (var subtree = reader.ReadSubtree())
+ {
+ FetchDataFromPersonsNode(subtree, itemResult);
+ }
+ }
+ else
+ {
+ reader.Read();
}
break;
}
case "Studios":
{
- using (var subtree = reader.ReadSubtree())
+ if (!reader.IsEmptyElement)
{
- FetchFromStudiosNode(subtree, item);
+ using (var subtree = reader.ReadSubtree())
+ {
+ FetchFromStudiosNode(subtree, item);
+ }
+ }
+ else
+ {
+ reader.Read();
}
break;
}
case "Shares":
{
- using (var subtree = reader.ReadSubtree())
+ if (!reader.IsEmptyElement)
{
- var hasShares = item as IHasShares;
- if (hasShares != null)
+ using (var subtree = reader.ReadSubtree())
{
- FetchFromSharesNode(subtree, hasShares);
+ var hasShares = item as IHasShares;
+ if (hasShares != null)
+ {
+ FetchFromSharesNode(subtree, hasShares);
+ }
}
}
+ else
+ {
+ reader.Read();
+ }
break;
}
case "Format3D":
{
+ var val = reader.ReadElementContentAsString();
+
var video = item as Video;
if (video != null)
{
- var val = reader.ReadElementContentAsString();
-
if (string.Equals("HSBS", val, StringComparison.OrdinalIgnoreCase))
{
video.Video3DFormat = Video3DFormat.HalfSideBySide;
@@ -803,8 +876,10 @@ namespace MediaBrowser.Controller.Providers
private void FetchFromSharesNode(XmlReader reader, IHasShares item)
{
reader.MoveToContent();
+ reader.Read();
- while (reader.Read())
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -812,6 +887,11 @@ namespace MediaBrowser.Controller.Providers
{
case "Share":
{
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ continue;
+ }
using (var subtree = reader.ReadSubtree())
{
var share = GetShareFromNode(subtree);
@@ -828,6 +908,10 @@ namespace MediaBrowser.Controller.Providers
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
}
@@ -836,8 +920,10 @@ namespace MediaBrowser.Controller.Providers
var share = new Share();
reader.MoveToContent();
+ reader.Read();
- while (reader.Read())
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -860,6 +946,10 @@ namespace MediaBrowser.Controller.Providers
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
return share;
@@ -868,8 +958,10 @@ namespace MediaBrowser.Controller.Providers
private void FetchFromCountriesNode(XmlReader reader, T item)
{
reader.MoveToContent();
+ reader.Read();
- while (reader.Read())
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -890,6 +982,10 @@ namespace MediaBrowser.Controller.Providers
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
}
@@ -901,8 +997,10 @@ namespace MediaBrowser.Controller.Providers
private void FetchFromTaglinesNode(XmlReader reader, T item)
{
reader.MoveToContent();
+ reader.Read();
- while (reader.Read())
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -918,12 +1016,15 @@ namespace MediaBrowser.Controller.Providers
}
break;
}
-
default:
reader.Skip();
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
}
@@ -935,8 +1036,10 @@ namespace MediaBrowser.Controller.Providers
private void FetchFromGenresNode(XmlReader reader, T item)
{
reader.MoveToContent();
+ reader.Read();
- while (reader.Read())
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -958,14 +1061,20 @@ namespace MediaBrowser.Controller.Providers
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
}
private void FetchFromTagsNode(XmlReader reader, BaseItem item)
{
reader.MoveToContent();
+ reader.Read();
- while (reader.Read())
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -987,14 +1096,20 @@ namespace MediaBrowser.Controller.Providers
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
}
private void FetchFromKeywordsNode(XmlReader reader, BaseItem item)
{
reader.MoveToContent();
+ reader.Read();
- while (reader.Read())
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -1016,6 +1131,10 @@ namespace MediaBrowser.Controller.Providers
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
}
@@ -1027,8 +1146,10 @@ namespace MediaBrowser.Controller.Providers
private void FetchDataFromPersonsNode(XmlReader reader, MetadataResult<T> item)
{
reader.MoveToContent();
+ reader.Read();
- while (reader.Read())
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -1037,6 +1158,11 @@ namespace MediaBrowser.Controller.Providers
case "Person":
case "Actor":
{
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ continue;
+ }
using (var subtree = reader.ReadSubtree())
{
foreach (var person in GetPersonsFromXmlNode(subtree))
@@ -1056,14 +1182,20 @@ namespace MediaBrowser.Controller.Providers
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
}
private void FetchDataFromTrailersNode(XmlReader reader, IHasTrailers item)
{
reader.MoveToContent();
+ reader.Read();
- while (reader.Read())
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -1085,87 +1217,11 @@ namespace MediaBrowser.Controller.Providers
break;
}
}
- }
- }
-
- protected List<ChapterInfo> FetchChaptersFromXmlNode(BaseItem item, XmlReader reader)
- {
- using (reader)
- {
- return GetChaptersFromXmlNode(reader)
- .Where(i => i.StartPositionTicks >= 0)
- .ToList();
- }
- }
-
- private IEnumerable<ChapterInfo> GetChaptersFromXmlNode(XmlReader reader)
- {
- var chapters = new List<ChapterInfo>();
-
- reader.MoveToContent();
-
- while (reader.Read())
- {
- if (reader.NodeType == XmlNodeType.Element)
- {
- switch (reader.Name)
- {
- case "Chapter":
- {
- using (var subtree = reader.ReadSubtree())
- {
- chapters.Add(GetChapterInfoFromXmlNode(subtree));
- }
- break;
- }
-
- default:
- reader.Skip();
- break;
- }
- }
- }
-
- return chapters;
- }
-
- private ChapterInfo GetChapterInfoFromXmlNode(XmlReader reader)
- {
- var chapter = new ChapterInfo();
-
- reader.MoveToContent();
-
- while (reader.Read())
- {
- if (reader.NodeType == XmlNodeType.Element)
+ else
{
- switch (reader.Name)
- {
- case "StartPositionMs":
- {
- var val = reader.ReadElementContentAsString();
-
- var ms = long.Parse(val, _usCulture);
-
- chapter.StartPositionTicks = TimeSpan.FromMilliseconds(ms).Ticks;
-
- break;
- }
-
- case "Name":
- {
- chapter.Name = reader.ReadElementContentAsString();
- break;
- }
-
- default:
- reader.Skip();
- break;
- }
+ reader.Read();
}
}
-
- return chapter;
}
/// <summary>
@@ -1176,8 +1232,10 @@ namespace MediaBrowser.Controller.Providers
private void FetchFromStudiosNode(XmlReader reader, T item)
{
reader.MoveToContent();
+ reader.Read();
- while (reader.Read())
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -1199,6 +1257,10 @@ namespace MediaBrowser.Controller.Providers
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
}
@@ -1215,8 +1277,10 @@ namespace MediaBrowser.Controller.Providers
int? sortOrder = null;
reader.MoveToContent();
+ reader.Read();
- while (reader.Read())
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -1267,6 +1331,10 @@ namespace MediaBrowser.Controller.Providers
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
var personInfo = new PersonInfo
@@ -1282,14 +1350,16 @@ namespace MediaBrowser.Controller.Providers
protected LinkedChild GetLinkedChild(XmlReader reader)
{
- reader.MoveToContent();
-
var linkedItem = new LinkedChild
{
Type = LinkedChildType.Manual
};
- while (reader.Read())
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -1306,6 +1376,10 @@ namespace MediaBrowser.Controller.Providers
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
// This is valid
@@ -1319,11 +1393,13 @@ namespace MediaBrowser.Controller.Providers
protected Share GetShare(XmlReader reader)
{
- reader.MoveToContent();
-
var item = new Share();
- while (reader.Read())
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -1340,12 +1416,17 @@ namespace MediaBrowser.Controller.Providers
item.CanEdit = string.Equals(reader.ReadElementContentAsString(), "true", StringComparison.OrdinalIgnoreCase);
break;
}
-
default:
- reader.Skip();
- break;
+ {
+ reader.Skip();
+ break;
+ }
}
}
+ else
+ {
+ reader.Read();
+ }
}
// This is valid
diff --git a/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs
index 9ebb357c6c..9dcfa2f765 100644
--- a/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs
@@ -4,16 +4,13 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System.Collections.Generic;
using System.Xml;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.LocalMetadata.Parsers
{
public class BoxSetXmlParser : BaseItemXmlParser<BoxSet>
{
- public BoxSetXmlParser(ILogger logger, IProviderManager providerManager)
- : base(logger, providerManager)
- {
- }
-
protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult<BoxSet> item)
{
switch (reader.Name)
@@ -34,11 +31,13 @@ namespace MediaBrowser.LocalMetadata.Parsers
private void FetchFromCollectionItemsNode(XmlReader reader, MetadataResult<BoxSet> item)
{
- reader.MoveToContent();
-
var list = new List<LinkedChild>();
- while (reader.Read())
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -58,15 +57,24 @@ namespace MediaBrowser.LocalMetadata.Parsers
break;
}
-
default:
- reader.Skip();
- break;
+ {
+ reader.Skip();
+ break;
+ }
}
}
+ else
+ {
+ reader.Read();
+ }
}
item.Item.LinkedChildren = list;
}
+
+ public BoxSetXmlParser(ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory, IFileSystem fileSystem) : base(logger, providerManager, xmlReaderSettingsFactory, fileSystem)
+ {
+ }
}
}
diff --git a/MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs
index 71f6d3fe9f..9adc4e5a9e 100644
--- a/MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs
@@ -8,7 +8,10 @@ using System.Globalization;
using System.IO;
using System.Threading;
using System.Xml;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.LocalMetadata.Parsers
{
@@ -20,8 +23,8 @@ namespace MediaBrowser.LocalMetadata.Parsers
private List<LocalImageInfo> _imagesFound;
private readonly IFileSystem _fileSystem;
- public EpisodeXmlParser(ILogger logger, IFileSystem fileSystem, IProviderManager providerManager)
- : base(logger, providerManager)
+ public EpisodeXmlParser(ILogger logger, IFileSystem fileSystem, IProviderManager providerManager, IXmlReaderSettingsFactory xmlSettings)
+ : base(logger, providerManager, xmlSettings, fileSystem)
{
_fileSystem = fileSystem;
}
diff --git a/MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs
index 75df539587..8bf09e5463 100644
--- a/MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs
@@ -5,16 +5,13 @@ using MediaBrowser.Model.Logging;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.LocalMetadata.Parsers
{
public class GameSystemXmlParser : BaseItemXmlParser<GameSystem>
{
- public GameSystemXmlParser(ILogger logger, IProviderManager providerManager)
- : base(logger, providerManager)
- {
- }
-
private readonly Task _cachedTask = Task.FromResult(true);
public Task FetchAsync(MetadataResult<GameSystem> item, string metadataFile, CancellationToken cancellationToken)
{
@@ -62,5 +59,9 @@ namespace MediaBrowser.LocalMetadata.Parsers
break;
}
}
+
+ public GameSystemXmlParser(ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory, IFileSystem fileSystem) : base(logger, providerManager, xmlReaderSettingsFactory, fileSystem)
+ {
+ }
}
}
diff --git a/MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs
index 956b8baef9..abce0582c6 100644
--- a/MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs
@@ -5,7 +5,9 @@ using System.Xml;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.LocalMetadata.Parsers
{
@@ -16,11 +18,6 @@ namespace MediaBrowser.LocalMetadata.Parsers
{
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- public GameXmlParser(ILogger logger, IProviderManager providerManager)
- : base(logger, providerManager)
- {
- }
-
private readonly Task _cachedTask = Task.FromResult(true);
public Task FetchAsync(MetadataResult<Game> item, string metadataFile, CancellationToken cancellationToken)
{
@@ -83,5 +80,9 @@ namespace MediaBrowser.LocalMetadata.Parsers
break;
}
}
+
+ public GameXmlParser(ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory, IFileSystem fileSystem) : base(logger, providerManager, xmlReaderSettingsFactory, fileSystem)
+ {
+ }
}
}
diff --git a/MediaBrowser.LocalMetadata/Parsers/MovieXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/MovieXmlParser.cs
index 6e78d365ed..08c895c43f 100644
--- a/MediaBrowser.LocalMetadata/Parsers/MovieXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/MovieXmlParser.cs
@@ -3,6 +3,8 @@ using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System.Xml;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.LocalMetadata.Parsers
{
@@ -12,11 +14,6 @@ namespace MediaBrowser.LocalMetadata.Parsers
public class BaseVideoXmlParser<T> : BaseItemXmlParser<T>
where T : Video
{
- public BaseVideoXmlParser(ILogger logger, IProviderManager providerManager)
- : base(logger, providerManager)
- {
- }
-
/// <summary>
/// Fetches the data from XML node.
/// </summary>
@@ -46,19 +43,22 @@ namespace MediaBrowser.LocalMetadata.Parsers
break;
}
}
+
+ public BaseVideoXmlParser(ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory, IFileSystem fileSystem) : base(logger, providerManager, xmlReaderSettingsFactory, fileSystem)
+ {
+ }
}
public class MovieXmlParser : BaseVideoXmlParser<Movie>
{
- public MovieXmlParser(ILogger logger, IProviderManager providerManager) : base(logger, providerManager)
+ public MovieXmlParser(ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory, IFileSystem fileSystem) : base(logger, providerManager, xmlReaderSettingsFactory, fileSystem)
{
}
}
public class VideoXmlParser : BaseVideoXmlParser<Video>
{
- public VideoXmlParser(ILogger logger, IProviderManager providerManager)
- : base(logger, providerManager)
+ public VideoXmlParser(ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory, IFileSystem fileSystem) : base(logger, providerManager, xmlReaderSettingsFactory, fileSystem)
{
}
}
diff --git a/MediaBrowser.LocalMetadata/Parsers/MusicVideoXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/MusicVideoXmlParser.cs
index 5f0b447e87..f612d1108a 100644
--- a/MediaBrowser.LocalMetadata/Parsers/MusicVideoXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/MusicVideoXmlParser.cs
@@ -3,21 +3,14 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System;
using System.Xml;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.LocalMetadata.Parsers
{
public class MusicVideoXmlParser : BaseVideoXmlParser<MusicVideo>
{
/// <summary>
- /// Initializes a new instance of the <see cref="BaseItemXmlParser{T}" /> class.
- /// </summary>
- /// <param name="logger">The logger.</param>
- public MusicVideoXmlParser(ILogger logger, IProviderManager providerManager)
- : base(logger, providerManager)
- {
- }
-
- /// <summary>
/// Fetches the data from XML node.
/// </summary>
/// <param name="reader">The reader.</param>
@@ -50,5 +43,9 @@ namespace MediaBrowser.LocalMetadata.Parsers
break;
}
}
+
+ public MusicVideoXmlParser(ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory, IFileSystem fileSystem) : base(logger, providerManager, xmlReaderSettingsFactory, fileSystem)
+ {
+ }
}
}
diff --git a/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs
index de46c0a86d..695fe2b12d 100644
--- a/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs
@@ -6,16 +6,13 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.LocalMetadata.Parsers
{
public class PlaylistXmlParser : BaseItemXmlParser<Playlist>
{
- public PlaylistXmlParser(ILogger logger, IProviderManager providerManager)
- : base(logger, providerManager)
- {
- }
-
protected override void FetchDataFromXmlNode(XmlReader reader, MetadataResult<Playlist> result)
{
var item = result.Item;
@@ -46,17 +43,31 @@ namespace MediaBrowser.LocalMetadata.Parsers
case "PlaylistItems":
- using (var subReader = reader.ReadSubtree())
+ if (!reader.IsEmptyElement)
+ {
+ using (var subReader = reader.ReadSubtree())
+ {
+ FetchFromCollectionItemsNode(subReader, item);
+ }
+ }
+ else
{
- FetchFromCollectionItemsNode(subReader, item);
+ reader.Read();
}
break;
case "Shares":
- using (var subReader = reader.ReadSubtree())
+ if (!reader.IsEmptyElement)
+ {
+ using (var subReader = reader.ReadSubtree())
+ {
+ FetchFromSharesNode(subReader, item);
+ }
+ }
+ else
{
- FetchFromSharesNode(subReader, item);
+ reader.Read();
}
break;
@@ -68,11 +79,13 @@ namespace MediaBrowser.LocalMetadata.Parsers
private void FetchFromCollectionItemsNode(XmlReader reader, Playlist item)
{
- reader.MoveToContent();
-
var list = new List<LinkedChild>();
- while (reader.Read())
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -80,6 +93,12 @@ namespace MediaBrowser.LocalMetadata.Parsers
{
case "PlaylistItem":
{
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ continue;
+ }
+
using (var subReader = reader.ReadSubtree())
{
var child = GetLinkedChild(subReader);
@@ -92,12 +111,17 @@ namespace MediaBrowser.LocalMetadata.Parsers
break;
}
-
default:
- reader.Skip();
- break;
+ {
+ reader.Skip();
+ break;
+ }
}
}
+ else
+ {
+ reader.Read();
+ }
}
item.LinkedChildren = list;
@@ -105,11 +129,13 @@ namespace MediaBrowser.LocalMetadata.Parsers
private void FetchFromSharesNode(XmlReader reader, Playlist item)
{
- reader.MoveToContent();
-
var list = new List<Share>();
- while (reader.Read())
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -117,6 +143,12 @@ namespace MediaBrowser.LocalMetadata.Parsers
{
case "Share":
{
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ continue;
+ }
+
using (var subReader = reader.ReadSubtree())
{
var child = GetShare(subReader);
@@ -129,15 +161,24 @@ namespace MediaBrowser.LocalMetadata.Parsers
break;
}
-
default:
- reader.Skip();
- break;
+ {
+ 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/Parsers/SeriesXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs
index 7b7fb4751b..2ddd843789 100644
--- a/MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs
@@ -4,7 +4,9 @@ using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.LocalMetadata.Parsers
{
@@ -14,15 +16,6 @@ namespace MediaBrowser.LocalMetadata.Parsers
public class SeriesXmlParser : BaseItemXmlParser<Series>
{
/// <summary>
- /// Initializes a new instance of the <see cref="BaseItemXmlParser{T}" /> class.
- /// </summary>
- /// <param name="logger">The logger.</param>
- public SeriesXmlParser(ILogger logger, IProviderManager providerManager)
- : base(logger, providerManager)
- {
- }
-
- /// <summary>
/// Fetches the data from XML node.
/// </summary>
/// <param name="reader">The reader.</param>
@@ -116,5 +109,9 @@ namespace MediaBrowser.LocalMetadata.Parsers
break;
}
}
+
+ public SeriesXmlParser(ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory, IFileSystem fileSystem) : base(logger, providerManager, xmlReaderSettingsFactory, fileSystem)
+ {
+ }
}
}
diff --git a/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs
index 3acb2b74cb..88c51caaa5 100644
--- a/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs
@@ -4,7 +4,10 @@ using MediaBrowser.LocalMetadata.Parsers;
using MediaBrowser.Model.Logging;
using System.IO;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.LocalMetadata.Providers
{
@@ -15,17 +18,19 @@ namespace MediaBrowser.LocalMetadata.Providers
{
private readonly ILogger _logger;
private readonly IProviderManager _providerManager;
+ protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; }
- public BoxSetXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
+ public BoxSetXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
: base(fileSystem)
{
_logger = logger;
_providerManager = providerManager;
+ XmlReaderSettingsFactory = xmlReaderSettingsFactory;
}
protected override void Fetch(MetadataResult<BoxSet> result, string path, CancellationToken cancellationToken)
{
- new BoxSetXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken);
+ new BoxSetXmlParser(_logger, _providerManager, XmlReaderSettingsFactory, FileSystem).Fetch(result, path, cancellationToken);
}
protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)
diff --git a/MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs
index 493df8c6a1..7e0f1707f9 100644
--- a/MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs
@@ -6,7 +6,10 @@ using MediaBrowser.Model.Logging;
using System.Collections.Generic;
using System.IO;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.LocalMetadata.Providers
{
@@ -14,12 +17,14 @@ namespace MediaBrowser.LocalMetadata.Providers
{
private readonly ILogger _logger;
private readonly IProviderManager _providerManager;
+ private readonly IXmlReaderSettingsFactory _xmlSettings;
- public EpisodeXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
+ public EpisodeXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlSettings)
: base(fileSystem)
{
_logger = logger;
_providerManager = providerManager;
+ _xmlSettings = xmlSettings;
}
protected override void Fetch(MetadataResult<Episode> result, string path, CancellationToken cancellationToken)
@@ -27,7 +32,7 @@ namespace MediaBrowser.LocalMetadata.Providers
var images = new List<LocalImageInfo>();
var chapters = new List<ChapterInfo>();
- new EpisodeXmlParser(_logger, FileSystem, _providerManager).Fetch(result, images, path, cancellationToken);
+ new EpisodeXmlParser(_logger, FileSystem, _providerManager, _xmlSettings).Fetch(result, images, path, cancellationToken);
result.Images = images;
}
diff --git a/MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs
index 7ac41e5cc0..45cd8eb938 100644
--- a/MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs
@@ -1,9 +1,13 @@
using System.IO;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+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
{
@@ -14,17 +18,19 @@ namespace MediaBrowser.LocalMetadata.Providers
{
private readonly ILogger _logger;
private readonly IProviderManager _providerManager;
+ protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; }
- public FolderXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
+ 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).Fetch(result, path, cancellationToken);
+ new BaseItemXmlParser<Folder>(_logger, _providerManager, XmlReaderSettingsFactory, FileSystem).Fetch(result, path, cancellationToken);
}
protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)
diff --git a/MediaBrowser.LocalMetadata/Providers/GameSystemXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/GameSystemXmlProvider.cs
index 942befb83d..8054b1204b 100644
--- a/MediaBrowser.LocalMetadata/Providers/GameSystemXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/GameSystemXmlProvider.cs
@@ -1,10 +1,13 @@
using System.IO;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+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
{
@@ -12,17 +15,19 @@ namespace MediaBrowser.LocalMetadata.Providers
{
private readonly ILogger _logger;
private readonly IProviderManager _providerManager;
+ private readonly IXmlReaderSettingsFactory _xmlSettings;
- public GameSystemXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
+ public GameSystemXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlSettings)
: base(fileSystem)
{
_logger = logger;
_providerManager = providerManager;
+ _xmlSettings = xmlSettings;
}
protected override void Fetch(MetadataResult<GameSystem> result, string path, CancellationToken cancellationToken)
{
- new GameSystemXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken);
+ new GameSystemXmlParser(_logger, _providerManager, _xmlSettings, FileSystem).Fetch(result, path, cancellationToken);
}
protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)
diff --git a/MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs
index c562df7fb2..888eff4164 100644
--- a/MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs
@@ -4,7 +4,10 @@ using MediaBrowser.LocalMetadata.Parsers;
using MediaBrowser.Model.Logging;
using System.IO;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.LocalMetadata.Providers
{
@@ -12,17 +15,19 @@ namespace MediaBrowser.LocalMetadata.Providers
{
private readonly ILogger _logger;
private readonly IProviderManager _providerManager;
+ private readonly IXmlReaderSettingsFactory _xmlSettings;
- public GameXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
+ public GameXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlSettings)
: base(fileSystem)
{
_logger = logger;
_providerManager = providerManager;
+ _xmlSettings = xmlSettings;
}
protected override void Fetch(MetadataResult<Game> result, string path, CancellationToken cancellationToken)
{
- new GameXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken);
+ new GameXmlParser(_logger, _providerManager, _xmlSettings, FileSystem).Fetch(result, path, cancellationToken);
}
protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)
diff --git a/MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs
index 333ea28230..0e59db75f6 100644
--- a/MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs
@@ -4,7 +4,10 @@ using MediaBrowser.LocalMetadata.Parsers;
using MediaBrowser.Model.Logging;
using System.IO;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.LocalMetadata.Providers
{
@@ -12,17 +15,19 @@ namespace MediaBrowser.LocalMetadata.Providers
{
private readonly ILogger _logger;
private readonly IProviderManager _providerManager;
+ protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; }
- public MovieXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
+ public MovieXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
: base(fileSystem)
{
_logger = logger;
_providerManager = providerManager;
+ XmlReaderSettingsFactory = xmlReaderSettingsFactory;
}
protected override void Fetch(MetadataResult<Movie> result, string path, CancellationToken cancellationToken)
{
- new MovieXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken);
+ new MovieXmlParser(_logger, _providerManager, XmlReaderSettingsFactory, FileSystem).Fetch(result, path, cancellationToken);
}
protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)
diff --git a/MediaBrowser.LocalMetadata/Providers/MusicVideoXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/MusicVideoXmlProvider.cs
index 49d8c09cca..e0bbb119f2 100644
--- a/MediaBrowser.LocalMetadata/Providers/MusicVideoXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/MusicVideoXmlProvider.cs
@@ -1,9 +1,12 @@
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+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
{
@@ -11,17 +14,19 @@ namespace MediaBrowser.LocalMetadata.Providers
{
private readonly ILogger _logger;
private readonly IProviderManager _providerManager;
+ protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; }
- public MusicVideoXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
+ public MusicVideoXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
: base(fileSystem)
{
_logger = logger;
_providerManager = providerManager;
+ XmlReaderSettingsFactory = xmlReaderSettingsFactory;
}
protected override void Fetch(MetadataResult<MusicVideo> result, string path, CancellationToken cancellationToken)
{
- new MusicVideoXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken);
+ new MusicVideoXmlParser(_logger, _providerManager, XmlReaderSettingsFactory, FileSystem).Fetch(result, path, cancellationToken);
}
protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)
diff --git a/MediaBrowser.LocalMetadata/Providers/PersonXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/PersonXmlProvider.cs
deleted file mode 100644
index 2ccb8968b3..0000000000
--- a/MediaBrowser.LocalMetadata/Providers/PersonXmlProvider.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System.IO;
-using System.Threading;
-using CommonIO;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Logging;
-
-namespace MediaBrowser.LocalMetadata.Providers
-{
- public class PersonXmlProvider : BaseXmlProvider<Person>
- {
- private readonly ILogger _logger;
- private readonly IProviderManager _providerManager;
-
- public PersonXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
- : base(fileSystem)
- {
- _logger = logger;
- _providerManager = providerManager;
- }
-
- protected override void Fetch(MetadataResult<Person> result, string path, CancellationToken cancellationToken)
- {
- new BaseItemXmlParser<Person>(_logger, _providerManager).Fetch(result, path, cancellationToken);
- }
-
- protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)
- {
- return directoryService.GetFile(Path.Combine(info.Path, "person.xml"));
- }
- }
-}
diff --git a/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs
index 149a3142de..cd839f9cc2 100644
--- a/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs
@@ -4,7 +4,10 @@ using MediaBrowser.LocalMetadata.Parsers;
using MediaBrowser.Model.Logging;
using System.IO;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.LocalMetadata.Providers
{
@@ -12,17 +15,19 @@ namespace MediaBrowser.LocalMetadata.Providers
{
private readonly ILogger _logger;
private readonly IProviderManager _providerManager;
+ protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; }
- public PlaylistXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
+ public PlaylistXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
: base(fileSystem)
{
_logger = logger;
_providerManager = providerManager;
+ XmlReaderSettingsFactory = xmlReaderSettingsFactory;
}
protected override void Fetch(MetadataResult<Playlist> result, string path, CancellationToken cancellationToken)
{
- new PlaylistXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken);
+ new PlaylistXmlParser(_logger, _providerManager, XmlReaderSettingsFactory, FileSystem).Fetch(result, path, cancellationToken);
}
protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)
diff --git a/MediaBrowser.LocalMetadata/Providers/SeriesXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/SeriesXmlProvider.cs
index 26d3c75391..ce006ef382 100644
--- a/MediaBrowser.LocalMetadata/Providers/SeriesXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/SeriesXmlProvider.cs
@@ -1,10 +1,13 @@
using System.IO;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Providers;
using MediaBrowser.LocalMetadata.Parsers;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.LocalMetadata.Providers
{
@@ -15,17 +18,19 @@ namespace MediaBrowser.LocalMetadata.Providers
{
private readonly ILogger _logger;
private readonly IProviderManager _providerManager;
+ protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; }
- public SeriesXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
+ public SeriesXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
: base(fileSystem)
{
_logger = logger;
_providerManager = providerManager;
+ XmlReaderSettingsFactory = xmlReaderSettingsFactory;
}
protected override void Fetch(MetadataResult<Series> result, string path, CancellationToken cancellationToken)
{
- new SeriesXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken);
+ new SeriesXmlParser(_logger, _providerManager, XmlReaderSettingsFactory, FileSystem).Fetch(result, path, cancellationToken);
}
protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)
diff --git a/MediaBrowser.LocalMetadata/Providers/VideoXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/VideoXmlProvider.cs
index 50f3bcda40..4a7149657d 100644
--- a/MediaBrowser.LocalMetadata/Providers/VideoXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/VideoXmlProvider.cs
@@ -3,7 +3,10 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.LocalMetadata.Parsers;
using MediaBrowser.Model.Logging;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.LocalMetadata.Providers
{
@@ -11,17 +14,19 @@ namespace MediaBrowser.LocalMetadata.Providers
{
private readonly ILogger _logger;
private readonly IProviderManager _providerManager;
+ protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; }
- public VideoXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager)
+ public VideoXmlProvider(IFileSystem fileSystem, ILogger logger, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
: base(fileSystem)
{
_logger = logger;
_providerManager = providerManager;
+ XmlReaderSettingsFactory = xmlReaderSettingsFactory;
}
protected override void Fetch(MetadataResult<Video> result, string path, CancellationToken cancellationToken)
{
- new VideoXmlParser(_logger, _providerManager).Fetch(result, path, cancellationToken);
+ new VideoXmlParser(_logger, _providerManager, XmlReaderSettingsFactory, FileSystem).Fetch(result, path, cancellationToken);
}
protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)
diff --git a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
new file mode 100644
index 0000000000..b52bae2ce7
--- /dev/null
+++ b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
@@ -0,0 +1,752 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Security;
+using System.Text;
+using System.Threading;
+using System.Xml;
+using MediaBrowser.Common.Extensions;
+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.Persistence;
+using MediaBrowser.Controller.Playlists;
+using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Xml;
+
+namespace MediaBrowser.LocalMetadata.Savers
+{
+ public abstract class BaseXmlSaver : IMetadataFileSaver
+ {
+ private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
+
+ private static readonly Dictionary<string, string> CommonTags = new[] {
+
+ "Added",
+ "AspectRatio",
+ "AudioDbAlbumId",
+ "AudioDbArtistId",
+ "AwardSummary",
+ "BirthDate",
+ "Budget",
+
+ // Deprecated. No longer saving in this field.
+ "certification",
+
+ "Chapters",
+ "ContentRating",
+ "Countries",
+ "CustomRating",
+ "CriticRating",
+ "CriticRatingSummary",
+ "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",
+ "Metascore",
+
+ // Deprecated. No longer saving in this field.
+ "MPAARating",
+
+ "MPAADescription",
+
+ "MusicBrainzArtistId",
+ "MusicBrainzAlbumArtistId",
+ "MusicBrainzAlbumId",
+ "MusicBrainzReleaseGroupId",
+
+ // Deprecated. No longer saving in this field.
+ "MusicbrainzId",
+
+ "Overview",
+ "ShortOverview",
+ "Persons",
+ "PlotKeywords",
+ "PremiereDate",
+ "ProductionYear",
+ "Rating",
+ "Revenue",
+ "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",
+ "Type",
+ "TVRageId",
+ "VoteCount",
+ "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;
+ ConfigurationManager = configurationManager;
+ LibraryManager = libraryManager;
+ UserManager = userManager;
+ UserDataManager = userDataManager;
+ Logger = logger;
+ XmlReaderSettingsFactory = xmlReaderSettingsFactory;
+ }
+
+ protected IFileSystem FileSystem { get; private set; }
+ protected IServerConfigurationManager ConfigurationManager { get; private set; }
+ protected ILibraryManager LibraryManager { get; private set; }
+ protected IUserManager UserManager { get; private set; }
+ protected IUserDataManager UserDataManager { get; private set; }
+ protected ILogger Logger { get; private set; }
+ protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; }
+
+ protected ItemUpdateType MinimumUpdateType
+ {
+ get
+ {
+ return ItemUpdateType.MetadataDownload;
+ }
+ }
+
+ public string Name
+ {
+ get
+ {
+ return XmlProviderUtils.Name;
+ }
+ }
+
+ public string GetSavePath(IHasMetadata item)
+ {
+ return GetLocalSavePath(item);
+ }
+
+ /// <summary>
+ /// Gets the save path.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <returns>System.String.</returns>
+ protected abstract string GetLocalSavePath(IHasMetadata 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)
+ {
+ return "Item";
+ }
+
+ /// <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>
+ public abstract bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType);
+
+ protected virtual List<string> GetTagsUsed()
+ {
+ return new List<string>();
+ }
+
+ public void Save(IHasMetadata item, CancellationToken cancellationToken)
+ {
+ var path = GetSavePath(item);
+
+ using (var memoryStream = new MemoryStream())
+ {
+ Save(item, memoryStream, path);
+
+ memoryStream.Position = 0;
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ SaveToFile(memoryStream, path);
+ }
+ }
+
+ private void SaveToFile(Stream stream, string path)
+ {
+ FileSystem.CreateDirectory(Path.GetDirectoryName(path));
+
+ var file = FileSystem.GetFileInfo(path);
+
+ var wasHidden = false;
+
+ // This will fail if the file is hidden
+ if (file.Exists)
+ {
+ if (file.IsHidden)
+ {
+ FileSystem.SetHidden(path, false);
+ wasHidden = true;
+ }
+ if (file.IsReadOnly)
+ {
+ FileSystem.SetReadOnly(path, false);
+ }
+ }
+
+ using (var filestream = FileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
+ {
+ stream.CopyTo(filestream);
+ }
+
+ if (wasHidden || ConfigurationManager.Configuration.SaveMetadataHidden)
+ {
+ FileSystem.SetHidden(path, true);
+ }
+ }
+
+ private void Save(IHasMetadata item, Stream stream, string xmlPath)
+ {
+ var settings = new XmlWriterSettings
+ {
+ Indent = true,
+ Encoding = Encoding.UTF8,
+ CloseOutput = false
+ };
+
+ using (XmlWriter writer = XmlWriter.Create(stream, settings))
+ {
+ var root = GetRootElementName(item);
+
+ writer.WriteStartDocument(true);
+
+ writer.WriteStartElement(root);
+
+ var baseItem = item as BaseItem;
+
+ if (baseItem != null)
+ {
+ AddCommonNodes(baseItem, writer, LibraryManager, UserManager, UserDataManager, FileSystem, ConfigurationManager);
+ }
+
+ 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);
+
+ public const string DateAddedFormat = "yyyy-MM-dd HH:mm:ss";
+
+ /// <summary>
+ /// 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)
+ {
+ var writtenProviderIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
+
+ if (!string.IsNullOrEmpty(item.OfficialRating))
+ {
+ writer.WriteElementString("ContentRating", item.OfficialRating);
+ }
+
+ if (!string.IsNullOrEmpty(item.OfficialRatingDescription))
+ {
+ writer.WriteElementString("MPAADescription", item.OfficialRatingDescription);
+ }
+
+ writer.WriteElementString("Added", item.DateCreated.ToLocalTime().ToString("G"));
+
+ writer.WriteElementString("LockData", item.IsLocked.ToString().ToLower());
+
+ if (item.LockedFields.Count > 0)
+ {
+ writer.WriteElementString("LockedFields", string.Join("|", item.LockedFields.Select(i => i.ToString()).ToArray()));
+ }
+
+ if (!string.IsNullOrEmpty(item.DisplayMediaType))
+ {
+ writer.WriteElementString("Type", item.DisplayMediaType);
+ }
+
+ if (item.CriticRating.HasValue)
+ {
+ writer.WriteElementString("CriticRating", item.CriticRating.Value.ToString(UsCulture));
+ }
+
+ if (!string.IsNullOrEmpty(item.CriticRatingSummary))
+ {
+ writer.WriteElementString("CriticRatingSummary", item.CriticRatingSummary);
+ }
+
+ if (!string.IsNullOrEmpty(item.Overview))
+ {
+ writer.WriteElementString("Overview", item.Overview);
+ }
+
+ if (!string.IsNullOrEmpty(item.OriginalTitle))
+ {
+ writer.WriteElementString("OriginalTitle", item.OriginalTitle);
+ }
+ if (!string.IsNullOrEmpty(item.ShortOverview))
+ {
+ writer.WriteElementString("ShortOverview", item.ShortOverview);
+ }
+ if (!string.IsNullOrEmpty(item.CustomRating))
+ {
+ writer.WriteElementString("CustomRating", item.CustomRating);
+ }
+
+ if (!string.IsNullOrEmpty(item.Name) && !(item is Episode))
+ {
+ writer.WriteElementString("LocalTitle", item.Name);
+ }
+
+ if (!string.IsNullOrEmpty(item.ForcedSortName))
+ {
+ writer.WriteElementString("SortTitle", item.ForcedSortName);
+ }
+
+ if (item.PremiereDate.HasValue)
+ {
+ if (item is Person)
+ {
+ writer.WriteElementString("BirthDate", item.PremiereDate.Value.ToLocalTime().ToString("yyyy-MM-dd"));
+ }
+ else if (!(item is Episode))
+ {
+ writer.WriteElementString("PremiereDate", item.PremiereDate.Value.ToLocalTime().ToString("yyyy-MM-dd"));
+ }
+ }
+
+ if (item.EndDate.HasValue)
+ {
+ if (item is Person)
+ {
+ writer.WriteElementString("DeathDate", item.EndDate.Value.ToLocalTime().ToString("yyyy-MM-dd"));
+ }
+ else if (!(item is Episode))
+ {
+ writer.WriteElementString("EndDate", item.EndDate.Value.ToLocalTime().ToString("yyyy-MM-dd"));
+ }
+ }
+
+ var hasTrailers = item as IHasTrailers;
+ if (hasTrailers != null)
+ {
+ if (hasTrailers.RemoteTrailers.Count > 0)
+ {
+ writer.WriteStartElement("Trailers");
+
+ foreach (var trailer in hasTrailers.RemoteTrailers)
+ {
+ writer.WriteElementString("Trailer", trailer.Url);
+ }
+
+ writer.WriteEndElement();
+ }
+ }
+
+ if (item.ProductionLocations.Count > 0)
+ {
+ writer.WriteStartElement("Countries");
+
+ foreach (var name in item.ProductionLocations)
+ {
+ writer.WriteElementString("Country", name);
+ }
+
+ writer.WriteEndElement();
+ }
+
+ var hasDisplayOrder = item as IHasDisplayOrder;
+ if (hasDisplayOrder != null && !string.IsNullOrEmpty(hasDisplayOrder.DisplayOrder))
+ {
+ writer.WriteElementString("DisplayOrder", hasDisplayOrder.DisplayOrder);
+ }
+
+ var hasMetascore = item as IHasMetascore;
+ if (hasMetascore != null && hasMetascore.Metascore.HasValue)
+ {
+ writer.WriteElementString("Metascore", hasMetascore.Metascore.Value.ToString(UsCulture));
+ }
+
+ var hasAwards = item as IHasAwards;
+ if (hasAwards != null && !string.IsNullOrEmpty(hasAwards.AwardSummary))
+ {
+ writer.WriteElementString("AwardSummary", hasAwards.AwardSummary);
+ }
+
+ var hasBudget = item as IHasBudget;
+ if (hasBudget != null)
+ {
+ if (hasBudget.Budget.HasValue)
+ {
+ writer.WriteElementString("Budget", hasBudget.Budget.Value.ToString(UsCulture));
+ }
+
+ if (hasBudget.Revenue.HasValue)
+ {
+ writer.WriteElementString("Revenue", hasBudget.Revenue.Value.ToString(UsCulture));
+ }
+ }
+
+ if (item.CommunityRating.HasValue)
+ {
+ writer.WriteElementString("Rating", item.CommunityRating.Value.ToString(UsCulture));
+ }
+ if (item.VoteCount.HasValue)
+ {
+ writer.WriteElementString("VoteCount", item.VoteCount.Value.ToString(UsCulture));
+ }
+
+ if (item.ProductionYear.HasValue && !(item is Person))
+ {
+ 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)
+ {
+ if (!string.IsNullOrEmpty(hasAspectRatio.AspectRatio))
+ {
+ writer.WriteElementString("AspectRatio", hasAspectRatio.AspectRatio);
+ }
+ }
+
+ if (!string.IsNullOrEmpty(item.PreferredMetadataLanguage))
+ {
+ writer.WriteElementString("Language", item.PreferredMetadataLanguage);
+ }
+ if (!string.IsNullOrEmpty(item.PreferredMetadataCountryCode))
+ {
+ writer.WriteElementString("CountryCode", item.PreferredMetadataCountryCode);
+ }
+
+ // Use original runtime here, actual file runtime later in MediaInfo
+ var runTimeTicks = item.RunTimeTicks;
+
+ if (runTimeTicks.HasValue)
+ {
+ var timespan = TimeSpan.FromTicks(runTimeTicks.Value);
+
+ writer.WriteElementString("RunningTime", Convert.ToInt32(timespan.TotalMinutes).ToString(UsCulture));
+ }
+
+ if (item.ProviderIds != null)
+ {
+ foreach (var providerKey in item.ProviderIds.Keys)
+ {
+ var providerId = item.ProviderIds[providerKey];
+ if (!string.IsNullOrEmpty(providerId))
+ {
+ writer.WriteElementString(providerKey + "Id", providerId);
+ }
+ }
+ }
+
+ if (!string.IsNullOrWhiteSpace(item.Tagline))
+ {
+ writer.WriteStartElement("Taglines");
+ writer.WriteElementString("Tagline", item.Tagline);
+ writer.WriteEndElement();
+ }
+
+ if (item.Genres.Count > 0)
+ {
+ writer.WriteStartElement("Genres");
+
+ foreach (var genre in item.Genres)
+ {
+ writer.WriteElementString("Genre", genre);
+ }
+
+ writer.WriteEndElement();
+ }
+
+ if (item.Studios.Count > 0)
+ {
+ writer.WriteStartElement("Studios");
+
+ foreach (var studio in item.Studios)
+ {
+ writer.WriteElementString("Studio", studio);
+ }
+
+ writer.WriteEndElement();
+ }
+
+ if (item.Tags.Count > 0)
+ {
+ writer.WriteStartElement("Tags");
+
+ foreach (var tag in item.Tags)
+ {
+ writer.WriteElementString("Tag", tag);
+ }
+
+ writer.WriteEndElement();
+ }
+
+ if (item.Keywords.Count > 0)
+ {
+ writer.WriteStartElement("PlotKeywords");
+
+ foreach (var tag in item.Keywords)
+ {
+ writer.WriteElementString("PlotKeyword", tag);
+ }
+
+ writer.WriteEndElement();
+ }
+
+ var people = libraryManager.GetPeople(item);
+
+ if (people.Count > 0)
+ {
+ writer.WriteStartElement("Persons");
+
+ foreach (var person in people)
+ {
+ writer.WriteStartElement("Person");
+ writer.WriteElementString("Name", person.Name);
+ writer.WriteElementString("Type", person.Type);
+ writer.WriteElementString("Role", person.Role);
+
+ if (person.SortOrder.HasValue)
+ {
+ writer.WriteElementString("SortOrder", person.SortOrder.Value.ToString(UsCulture));
+ }
+
+ writer.WriteEndElement();
+ }
+
+ writer.WriteEndElement();
+ }
+
+ var boxset = item as BoxSet;
+ if (boxset != null)
+ {
+ AddLinkedChildren(boxset, writer, "CollectionItems", "CollectionItem");
+ }
+
+ var playlist = item as Playlist;
+ if (playlist != null)
+ {
+ AddLinkedChildren(playlist, writer, "PlaylistItems", "PlaylistItem");
+ }
+
+ var hasShares = item as IHasShares;
+ if (hasShares != null)
+ {
+ AddShares(hasShares, writer);
+ }
+
+ AddMediaInfo(item, writer);
+ }
+
+ public static void AddShares(IHasShares item, XmlWriter writer)
+ {
+ writer.WriteStartElement("Shares");
+
+ foreach (var share in item.Shares)
+ {
+ writer.WriteStartElement("Share");
+
+ writer.WriteElementString("UserId", share.UserId);
+ writer.WriteElementString("CanEdit", share.CanEdit.ToString().ToLower());
+
+ writer.WriteEndElement();
+ }
+
+ writer.WriteEndElement();
+ }
+
+ /// <summary>
+ /// Appends the media info.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ public static void AddMediaInfo<T>(T item, XmlWriter writer)
+ where T : BaseItem
+ {
+ var video = item as Video;
+
+ if (video != null)
+ {
+ if (video.Video3DFormat.HasValue)
+ {
+ switch (video.Video3DFormat.Value)
+ {
+ case Video3DFormat.FullSideBySide:
+ writer.WriteElementString("Format3D", "FSBS");
+ break;
+ case Video3DFormat.FullTopAndBottom:
+ writer.WriteElementString("Format3D", "FTAB");
+ break;
+ case Video3DFormat.HalfSideBySide:
+ writer.WriteElementString("Format3D", "HSBS");
+ break;
+ case Video3DFormat.HalfTopAndBottom:
+ writer.WriteElementString("Format3D", "HTAB");
+ break;
+ case Video3DFormat.MVC:
+ writer.WriteElementString("Format3D", "MVC");
+ break;
+ }
+ }
+ }
+ }
+
+ public static void AddLinkedChildren(Folder item, XmlWriter writer, string pluralNodeName, string singularNodeName)
+ {
+ var items = item.LinkedChildren
+ .Where(i => i.Type == LinkedChildType.Manual)
+ .ToList();
+
+ if (items.Count == 0)
+ {
+ return;
+ }
+
+ writer.WriteStartElement(pluralNodeName);
+
+ foreach (var link in items)
+ {
+ if (!string.IsNullOrWhiteSpace(link.Path))
+ {
+ writer.WriteStartElement(singularNodeName);
+ writer.WriteElementString("Path", link.Path);
+ writer.WriteEndElement();
+ }
+ }
+
+ writer.WriteEndElement();
+ }
+
+ private static bool IsPersonType(PersonInfo person, string type)
+ {
+ return string.Equals(person.Type, type, StringComparison.OrdinalIgnoreCase) || string.Equals(person.Role, type, StringComparison.OrdinalIgnoreCase);
+ }
+
+ private void 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 b307ded972..8167f60022 100644
--- a/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs
@@ -6,38 +6,18 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
-using CommonIO;
+using System.Xml;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.LocalMetadata.Savers
{
- public class BoxSetXmlSaver : IMetadataFileSaver
+ public class BoxSetXmlSaver : BaseXmlSaver
{
- public string Name
- {
- get
- {
- return XmlProviderUtils.Name;
- }
- }
-
- private readonly IServerConfigurationManager _config;
- private readonly ILibraryManager _libraryManager;
- private readonly IFileSystem _fileSystem;
-
- public BoxSetXmlSaver(IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem)
- {
- _config = config;
- _libraryManager = libraryManager;
- _fileSystem = fileSystem;
- }
-
- /// <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>
- public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
+ public override bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
{
if (!item.SupportsLocalMetadata)
{
@@ -47,35 +27,17 @@ namespace MediaBrowser.LocalMetadata.Savers
return item is BoxSet && updateType >= ItemUpdateType.MetadataDownload;
}
- /// <summary>
- /// Saves the specified item.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public void Save(IHasMetadata item, CancellationToken cancellationToken)
+ protected override void WriteCustomElements(IHasMetadata item, XmlWriter writer)
{
- var builder = new StringBuilder();
-
- builder.Append("<Item>");
-
- XmlSaverHelpers.AddCommonNodes((BoxSet)item, _libraryManager, builder);
-
- builder.Append("</Item>");
-
- var xmlFilePath = GetSavePath(item);
-
- XmlSaverHelpers.Save(builder, xmlFilePath, new List<string>(), _config, _fileSystem);
}
- /// <summary>
- /// Gets the save path.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>System.String.</returns>
- public string GetSavePath(IHasMetadata item)
+ protected override string GetLocalSavePath(IHasMetadata item)
{
return Path.Combine(item.Path, "collection.xml");
}
+
+ public BoxSetXmlSaver(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/FolderXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/FolderXmlSaver.cs
index 8dad16fc24..b51bd5b911 100644
--- a/MediaBrowser.LocalMetadata/Savers/FolderXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/FolderXmlSaver.cs
@@ -9,38 +9,28 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
-using CommonIO;
+using System.Xml;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.LocalMetadata.Savers
{
- public class FolderXmlSaver : IMetadataFileSaver
+ public class FolderXmlSaver : BaseXmlSaver
{
- public string Name
+ protected override string GetLocalSavePath(IHasMetadata item)
{
- get
- {
- return XmlProviderUtils.Name;
- }
+ return Path.Combine(item.Path, "folder.xml");
}
- private readonly IServerConfigurationManager _config;
- private readonly ILibraryManager _libraryManager;
- private readonly IFileSystem _fileSystem;
-
- public FolderXmlSaver(IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem)
+ protected override string GetRootElementName(IHasMetadata item)
{
- _config = config;
- _libraryManager = libraryManager;
- _fileSystem = fileSystem;
+ return "Item";
}
- /// <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>
- public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
+ public override bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
{
if (!item.SupportsLocalMetadata)
{
@@ -61,35 +51,12 @@ namespace MediaBrowser.LocalMetadata.Savers
return false;
}
- /// <summary>
- /// Saves the specified item.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public void Save(IHasMetadata item, CancellationToken cancellationToken)
+ protected override void WriteCustomElements(IHasMetadata item, XmlWriter writer)
{
- var builder = new StringBuilder();
-
- builder.Append("<Item>");
-
- XmlSaverHelpers.AddCommonNodes((Folder)item, _libraryManager, builder);
-
- builder.Append("</Item>");
-
- var xmlFilePath = GetSavePath(item);
-
- XmlSaverHelpers.Save(builder, xmlFilePath, new List<string>(), _config, _fileSystem);
}
- /// <summary>
- /// Gets the save path.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>System.String.</returns>
- public string GetSavePath(IHasMetadata item)
+ public FolderXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger, xmlReaderSettingsFactory)
{
- return Path.Combine(item.Path, "folder.xml");
}
}
}
diff --git a/MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs
index ddfaedba68..59b69746a4 100644
--- a/MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs
@@ -3,41 +3,20 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using System.Collections.Generic;
using System.IO;
-using System.Security;
-using System.Text;
-using System.Threading;
-using CommonIO;
+using System.Xml;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.LocalMetadata.Savers
{
- public class GameSystemXmlSaver : IMetadataFileSaver
+ public class GameSystemXmlSaver : BaseXmlSaver
{
- public string Name
+ public GameSystemXmlSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger, xmlReaderSettingsFactory)
{
- get
- {
- return XmlProviderUtils.Name;
- }
}
- private readonly IServerConfigurationManager _config;
- private readonly ILibraryManager _libraryManager;
- private readonly IFileSystem _fileSystem;
-
- public GameSystemXmlSaver(IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem)
- {
- _config = config;
- _libraryManager = libraryManager;
- _fileSystem = fileSystem;
- }
-
- /// <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>
- public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
+ public override bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
{
if (!item.SupportsLocalMetadata)
{
@@ -47,42 +26,34 @@ namespace MediaBrowser.LocalMetadata.Savers
return item is GameSystem && updateType >= ItemUpdateType.MetadataDownload;
}
- /// <summary>
- /// Saves the specified item.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public void Save(IHasMetadata item, CancellationToken cancellationToken)
+ protected override List<string> GetTagsUsed()
{
- var gameSystem = (GameSystem)item;
+ var list = new List<string>
+ {
+ "GameSystem"
+ };
- var builder = new StringBuilder();
+ return list;
+ }
- builder.Append("<Item>");
+ protected override void WriteCustomElements(IHasMetadata item, XmlWriter writer)
+ {
+ var gameSystem = (GameSystem)item;
if (!string.IsNullOrEmpty(gameSystem.GameSystemName))
{
- builder.Append("<GameSystem>" + SecurityElement.Escape(gameSystem.GameSystemName) + "</GameSystem>");
+ writer.WriteElementString("GameSystem", gameSystem.GameSystemName);
}
-
- XmlSaverHelpers.AddCommonNodes(gameSystem, _libraryManager, builder);
-
- builder.Append("</Item>");
-
- var xmlFilePath = GetSavePath(item);
-
- XmlSaverHelpers.Save(builder, xmlFilePath, new List<string>(), _config, _fileSystem);
}
- /// <summary>
- /// Gets the save path.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>System.String.</returns>
- public string GetSavePath(IHasMetadata item)
+ protected override string GetLocalSavePath(IHasMetadata item)
{
return Path.Combine(item.Path, "gamesystem.xml");
}
+
+ protected override string GetRootElementName(IHasMetadata item)
+ {
+ return "Item";
+ }
}
}
diff --git a/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs
index 5592c068cd..26c4b4a930 100644
--- a/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs
@@ -4,44 +4,21 @@ using MediaBrowser.Controller.Library;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
-using System.Security;
-using System.Text;
-using System.Threading;
-using CommonIO;
+using System.Xml;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.LocalMetadata.Savers
{
/// <summary>
/// Saves game.xml for games
/// </summary>
- public class GameXmlSaver : IMetadataFileSaver
+ public class GameXmlSaver : BaseXmlSaver
{
- public string Name
- {
- get
- {
- return XmlProviderUtils.Name;
- }
- }
-
- private readonly IServerConfigurationManager _config;
- private readonly ILibraryManager _libraryManager;
- private readonly IFileSystem _fileSystem;
-
- public GameXmlSaver(IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem)
- {
- _config = config;
- _libraryManager = libraryManager;
- _fileSystem = fileSystem;
- }
+ private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
- /// <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>
- public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
+ public override bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
{
if (!item.SupportsLocalMetadata)
{
@@ -51,52 +28,41 @@ namespace MediaBrowser.LocalMetadata.Savers
return item is Game && updateType >= ItemUpdateType.MetadataDownload;
}
- private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
- /// <summary>
- /// Saves the specified item.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public void Save(IHasMetadata item, CancellationToken cancellationToken)
+ protected override List<string> GetTagsUsed()
{
- var builder = new StringBuilder();
+ var list = new List<string>
+ {
+ "GameSystem",
+ "Players"
+ };
- builder.Append("<Item>");
+ return list;
+ }
+ protected override void WriteCustomElements(IHasMetadata item, XmlWriter writer)
+ {
var game = (Game)item;
- if (game.PlayersSupported.HasValue)
+ if (!string.IsNullOrEmpty(game.GameSystem))
{
- builder.Append("<Players>" + SecurityElement.Escape(game.PlayersSupported.Value.ToString(UsCulture)) + "</Players>");
+ writer.WriteElementString("GameSystem", game.GameSystem);
}
-
- if (!string.IsNullOrEmpty(game.GameSystem))
+ if (game.PlayersSupported.HasValue)
{
- builder.Append("<GameSystem>" + SecurityElement.Escape(game.GameSystem) + "</GameSystem>");
+ writer.WriteElementString("Players", game.PlayersSupported.Value.ToString(UsCulture));
}
-
- XmlSaverHelpers.AddCommonNodes(game, _libraryManager, builder);
-
- builder.Append("</Item>");
-
- var xmlFilePath = GetSavePath(item);
-
- XmlSaverHelpers.Save(builder, xmlFilePath, new List<string>
- {
- "Players",
- "GameSystem",
- "NesBox",
- "NesBoxRom"
- }, _config, _fileSystem);
}
- public string GetSavePath(IHasMetadata item)
+ protected override string GetLocalSavePath(IHasMetadata item)
{
return GetGameSavePath((Game)item);
}
+ protected override string GetRootElementName(IHasMetadata item)
+ {
+ return "Item";
+ }
+
public static string GetGameSavePath(Game item)
{
if (item.DetectIsInMixedFolder())
@@ -106,5 +72,9 @@ namespace MediaBrowser.LocalMetadata.Savers
return Path.Combine(item.ContainingFolderPath, "game.xml");
}
+
+ public GameXmlSaver(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/PersonXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/PersonXmlSaver.cs
index 295e648814..8409e483d0 100644
--- a/MediaBrowser.LocalMetadata/Savers/PersonXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/PersonXmlSaver.cs
@@ -3,92 +3,55 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using System.Collections.Generic;
using System.IO;
-using System.Security;
-using System.Text;
-using System.Threading;
-using CommonIO;
+using System.Xml;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.LocalMetadata.Savers
{
- /// <summary>
- /// Class PersonXmlSaver
- /// </summary>
- public class PersonXmlSaver : IMetadataFileSaver
- {
- public string Name
- {
- get
- {
- return XmlProviderUtils.Name;
- }
- }
-
- private readonly IServerConfigurationManager _config;
- private readonly ILibraryManager _libraryManager;
- private readonly IFileSystem _fileSystem;
-
- public PersonXmlSaver(IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem)
- {
- _config = config;
- _libraryManager = libraryManager;
- _fileSystem = fileSystem;
- }
-
- /// <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>
- public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
- {
- if (!item.SupportsLocalMetadata)
- {
- return false;
- }
-
- return item is Person && updateType >= ItemUpdateType.MetadataDownload;
- }
-
- /// <summary>
- /// Saves the specified item.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public void Save(IHasMetadata item, CancellationToken cancellationToken)
- {
- var person = (Person)item;
-
- var builder = new StringBuilder();
-
- builder.Append("<Item>");
-
- XmlSaverHelpers.AddCommonNodes(person, _libraryManager, builder);
-
- if (person.ProductionLocations.Count > 0)
- {
- builder.Append("<PlaceOfBirth>" + SecurityElement.Escape(person.ProductionLocations[0]) + "</PlaceOfBirth>");
- }
-
- builder.Append("</Item>");
-
- var xmlFilePath = GetSavePath(item);
-
- XmlSaverHelpers.Save(builder, xmlFilePath, new List<string>
- {
- "PlaceOfBirth"
- }, _config, _fileSystem);
- }
-
- /// <summary>
- /// Gets the save path.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>System.String.</returns>
- public string GetSavePath(IHasMetadata item)
- {
- return Path.Combine(item.Path, "person.xml");
- }
- }
+ ///// <summary>
+ ///// Class PersonXmlSaver
+ ///// </summary>
+ //public class PersonXmlSaver : BaseXmlSaver
+ //{
+ // public override bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
+ // {
+ // if (!item.SupportsLocalMetadata)
+ // {
+ // return false;
+ // }
+
+ // return item is Person && updateType >= ItemUpdateType.MetadataDownload;
+ // }
+
+ // protected override List<string> GetTagsUsed()
+ // {
+ // var list = new List<string>
+ // {
+ // "PlaceOfBirth"
+ // };
+
+ // return list;
+ // }
+
+ // protected override void WriteCustomElements(IHasMetadata item, XmlWriter writer)
+ // {
+ // var person = (Person)item;
+
+ // if (person.ProductionLocations.Count > 0)
+ // {
+ // writer.WriteElementString("PlaceOfBirth", person.ProductionLocations[0]);
+ // }
+ // }
+
+ // protected override string GetLocalSavePath(IHasMetadata item)
+ // {
+ // return Path.Combine(item.Path, "person.xml");
+ // }
+
+ // public PersonXmlSaver(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/PlaylistXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs
index 8862f9c038..ef28dde36e 100644
--- a/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs
@@ -4,41 +4,16 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Playlists;
using System.Collections.Generic;
using System.IO;
-using System.Security;
-using System.Text;
-using System.Threading;
-using CommonIO;
+using System.Xml;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.LocalMetadata.Savers
{
- public class PlaylistXmlSaver : IMetadataFileSaver
+ public class PlaylistXmlSaver : BaseXmlSaver
{
- public string Name
- {
- get
- {
- return XmlProviderUtils.Name;
- }
- }
-
- private readonly IServerConfigurationManager _config;
- private readonly ILibraryManager _libraryManager;
- private readonly IFileSystem _fileSystem;
-
- public PlaylistXmlSaver(IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem)
- {
- _config = config;
- _libraryManager = libraryManager;
- _fileSystem = fileSystem;
- }
-
- /// <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>
- public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
+ public override bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
{
if (!item.SupportsLocalMetadata)
{
@@ -48,47 +23,34 @@ namespace MediaBrowser.LocalMetadata.Savers
return item is Playlist && updateType >= ItemUpdateType.MetadataImport;
}
- /// <summary>
- /// Saves the specified item.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public void Save(IHasMetadata item, CancellationToken cancellationToken)
+ protected override List<string> GetTagsUsed()
{
- var playlist = (Playlist)item;
-
- var builder = new StringBuilder();
-
- builder.Append("<Item>");
-
- if (!string.IsNullOrEmpty(playlist.PlaylistMediaType))
+ var list = new List<string>
{
- builder.Append("<PlaylistMediaType>" + SecurityElement.Escape(playlist.PlaylistMediaType) + "</PlaylistMediaType>");
- }
-
- XmlSaverHelpers.AddCommonNodes(playlist, _libraryManager, builder);
+ "OwnerUserId",
+ "PlaylistMediaType"
+ };
- builder.Append("</Item>");
+ return list;
+ }
- var xmlFilePath = GetSavePath(item);
+ protected override void WriteCustomElements(IHasMetadata item, XmlWriter writer)
+ {
+ var game = (Playlist)item;
- XmlSaverHelpers.Save(builder, xmlFilePath, new List<string>
+ if (!string.IsNullOrEmpty(game.PlaylistMediaType))
{
- "OwnerUserId",
- "PlaylistMediaType"
-
- }, _config, _fileSystem);
+ writer.WriteElementString("PlaylistMediaType", game.PlaylistMediaType);
+ }
}
- /// <summary>
- /// Gets the save path.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>System.String.</returns>
- public string GetSavePath(IHasMetadata item)
+ protected override string GetLocalSavePath(IHasMetadata item)
{
return Path.Combine(item.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.LocalMetadata/Savers/XmlSaverHelpers.cs b/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs
deleted file mode 100644
index 3148405583..0000000000
--- a/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs
+++ /dev/null
@@ -1,647 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Controller.Playlists;
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Security;
-using System.Text;
-using System.Xml;
-using CommonIO;
-
-namespace MediaBrowser.LocalMetadata.Savers
-{
- /// <summary>
- /// Class XmlHelpers
- /// </summary>
- public static class XmlSaverHelpers
- {
- private static readonly Dictionary<string, string> CommonTags = new[] {
-
- "Added",
- "AspectRatio",
- "AudioDbAlbumId",
- "AudioDbArtistId",
- "AwardSummary",
- "BirthDate",
- "Budget",
-
- // Deprecated. No longer saving in this field.
- "certification",
-
- "Chapters",
- "ContentRating",
- "Countries",
- "CustomRating",
- "CriticRating",
- "CriticRatingSummary",
- "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",
- "Metascore",
-
- // Deprecated. No longer saving in this field.
- "MPAARating",
-
- "MPAADescription",
-
- "MusicBrainzArtistId",
- "MusicBrainzAlbumArtistId",
- "MusicBrainzAlbumId",
- "MusicBrainzReleaseGroupId",
-
- // Deprecated. No longer saving in this field.
- "MusicbrainzId",
-
- "Overview",
- "ShortOverview",
- "Persons",
- "PlotKeywords",
- "PremiereDate",
- "ProductionYear",
- "Rating",
- "Revenue",
- "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",
- "Type",
- "TVRageId",
- "VoteCount",
- "Website",
- "Zap2ItId",
- "CollectionItems",
- "PlaylistItems",
- "Shares"
-
- }.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
-
- /// <summary>
- /// The us culture
- /// </summary>
- private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
- /// <summary>
- /// Saves the specified XML.
- /// </summary>
- /// <param name="xml">The XML.</param>
- /// <param name="path">The path.</param>
- /// <param name="xmlTagsUsed">The XML tags used.</param>
- public static void Save(StringBuilder xml, string path, List<string> xmlTagsUsed, IServerConfigurationManager config, IFileSystem fileSystem)
- {
- if (fileSystem.FileExists(path))
- {
- var position = xml.ToString().LastIndexOf("</", StringComparison.OrdinalIgnoreCase);
- xml.Insert(position, GetCustomTags(path, xmlTagsUsed));
- }
-
- var xmlDocument = new XmlDocument();
- xmlDocument.LoadXml(xml.ToString());
-
- //Add the new node to the document.
- xmlDocument.InsertBefore(xmlDocument.CreateXmlDeclaration("1.0", "UTF-8", "yes"), xmlDocument.DocumentElement);
-
- fileSystem.CreateDirectory(Path.GetDirectoryName(path));
-
- var wasHidden = false;
-
- var file = new FileInfo(path);
-
- // This will fail if the file is hidden
- if (file.Exists)
- {
- if ((file.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
- {
- file.Attributes &= ~FileAttributes.Hidden;
-
- wasHidden = true;
- }
- }
-
- using (var filestream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
- {
- using (var streamWriter = new StreamWriter(filestream, Encoding.UTF8))
- {
- xmlDocument.Save(streamWriter);
- }
- }
-
- if (wasHidden || config.Configuration.SaveMetadataHidden)
- {
- file.Refresh();
-
- // Add back the attribute
- file.Attributes |= FileAttributes.Hidden;
- }
- }
-
- /// <summary>
- /// Gets the custom tags.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="xmlTagsUsed">The XML tags used.</param>
- /// <returns>System.String.</returns>
- private static string GetCustomTags(string path, List<string> xmlTagsUsed)
- {
- var settings = new XmlReaderSettings
- {
- CheckCharacters = false,
- IgnoreProcessingInstructions = true,
- IgnoreComments = true,
- ValidationType = ValidationType.None
- };
-
- var builder = new StringBuilder();
-
- using (var streamReader = new StreamReader(path, Encoding.UTF8))
- {
- // Use XmlReader for best performance
- using (var reader = XmlReader.Create(streamReader, settings))
- {
- reader.MoveToContent();
-
- // Loop through each element
- while (reader.Read())
- {
- if (reader.NodeType == XmlNodeType.Element)
- {
- var name = reader.Name;
-
- if (!CommonTags.ContainsKey(name) && !xmlTagsUsed.Contains(name, StringComparer.OrdinalIgnoreCase))
- {
- builder.AppendLine(reader.ReadOuterXml());
- }
- else
- {
- reader.Skip();
- }
- }
- }
- }
- }
-
- return builder.ToString();
- }
-
- /// <summary>
- /// Adds the common nodes.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="builder">The builder.</param>
- public static void AddCommonNodes(BaseItem item, ILibraryManager libraryManager, StringBuilder builder)
- {
- if (!string.IsNullOrEmpty(item.OfficialRating))
- {
- builder.Append("<ContentRating>" + SecurityElement.Escape(item.OfficialRating) + "</ContentRating>");
- }
-
- if (!string.IsNullOrEmpty(item.OfficialRatingDescription))
- {
- builder.Append("<MPAADescription>" + SecurityElement.Escape(item.OfficialRatingDescription) + "</MPAADescription>");
- }
-
- builder.Append("<Added>" + SecurityElement.Escape(item.DateCreated.ToLocalTime().ToString("G")) + "</Added>");
-
- builder.Append("<LockData>" + item.IsLocked.ToString().ToLower() + "</LockData>");
-
- if (item.LockedFields.Count > 0)
- {
- builder.Append("<LockedFields>" + string.Join("|", item.LockedFields.Select(i => i.ToString()).ToArray()) + "</LockedFields>");
- }
-
- if (!string.IsNullOrEmpty(item.DisplayMediaType))
- {
- builder.Append("<Type>" + SecurityElement.Escape(item.DisplayMediaType) + "</Type>");
- }
-
- if (item.CriticRating.HasValue)
- {
- builder.Append("<CriticRating>" + SecurityElement.Escape(item.CriticRating.Value.ToString(UsCulture)) + "</CriticRating>");
- }
-
- if (!string.IsNullOrEmpty(item.CriticRatingSummary))
- {
- builder.Append("<CriticRatingSummary><![CDATA[" + item.CriticRatingSummary + "]]></CriticRatingSummary>");
- }
-
- if (!string.IsNullOrEmpty(item.Overview))
- {
- builder.Append("<Overview><![CDATA[" + item.Overview + "]]></Overview>");
- }
-
- var hasOriginalTitle = item as IHasOriginalTitle;
- if (hasOriginalTitle != null)
- {
- if (!string.IsNullOrEmpty(hasOriginalTitle.OriginalTitle))
- {
- builder.Append("<OriginalTitle>" + SecurityElement.Escape(hasOriginalTitle.OriginalTitle) + "</OriginalTitle>");
- }
- }
-
- if (!string.IsNullOrEmpty(item.ShortOverview))
- {
- builder.Append("<ShortOverview><![CDATA[" + item.ShortOverview + "]]></ShortOverview>");
- }
-
- if (!string.IsNullOrEmpty(item.CustomRating))
- {
- builder.Append("<CustomRating>" + SecurityElement.Escape(item.CustomRating) + "</CustomRating>");
- }
-
- if (!string.IsNullOrEmpty(item.Name) && !(item is Episode))
- {
- builder.Append("<LocalTitle>" + SecurityElement.Escape(item.Name) + "</LocalTitle>");
- }
-
- if (!string.IsNullOrEmpty(item.ForcedSortName))
- {
- builder.Append("<SortTitle>" + SecurityElement.Escape(item.ForcedSortName) + "</SortTitle>");
- }
-
- if (item.PremiereDate.HasValue)
- {
- if (item is Person)
- {
- builder.Append("<BirthDate>" + SecurityElement.Escape(item.PremiereDate.Value.ToLocalTime().ToString("yyyy-MM-dd")) + "</BirthDate>");
- }
- else if (!(item is Episode))
- {
- builder.Append("<PremiereDate>" + SecurityElement.Escape(item.PremiereDate.Value.ToLocalTime().ToString("yyyy-MM-dd")) + "</PremiereDate>");
- }
- }
-
- if (item.EndDate.HasValue)
- {
- if (item is Person)
- {
- builder.Append("<DeathDate>" + SecurityElement.Escape(item.EndDate.Value.ToString("yyyy-MM-dd")) + "</DeathDate>");
- }
- else if (!(item is Episode))
- {
- builder.Append("<EndDate>" + SecurityElement.Escape(item.EndDate.Value.ToString("yyyy-MM-dd")) + "</EndDate>");
- }
- }
-
- var hasTrailers = item as IHasTrailers;
- if (hasTrailers != null)
- {
- if (hasTrailers.RemoteTrailers.Count > 0)
- {
- builder.Append("<Trailers>");
-
- foreach (var trailer in hasTrailers.RemoteTrailers)
- {
- builder.Append("<Trailer>" + SecurityElement.Escape(trailer.Url) + "</Trailer>");
- }
-
- builder.Append("</Trailers>");
- }
- }
-
- //if (hasProductionLocations.ProductionLocations.Count > 0)
- //{
- // builder.Append("<Countries>");
-
- // foreach (var name in hasProductionLocations.ProductionLocations)
- // {
- // builder.Append("<Country>" + SecurityElement.Escape(name) + "</Country>");
- // }
-
- // builder.Append("</Countries>");
- //}
-
- var hasDisplayOrder = item as IHasDisplayOrder;
- if (hasDisplayOrder != null && !string.IsNullOrEmpty(hasDisplayOrder.DisplayOrder))
- {
- builder.Append("<DisplayOrder>" + SecurityElement.Escape(hasDisplayOrder.DisplayOrder) + "</DisplayOrder>");
- }
-
- var hasMetascore = item as IHasMetascore;
- if (hasMetascore != null && hasMetascore.Metascore.HasValue)
- {
- builder.Append("<Metascore>" + SecurityElement.Escape(hasMetascore.Metascore.Value.ToString(UsCulture)) + "</Metascore>");
- }
-
- var hasAwards = item as IHasAwards;
- if (hasAwards != null && !string.IsNullOrEmpty(hasAwards.AwardSummary))
- {
- builder.Append("<AwardSummary>" + SecurityElement.Escape(hasAwards.AwardSummary) + "</AwardSummary>");
- }
-
- var hasBudget = item as IHasBudget;
- if (hasBudget != null)
- {
- if (hasBudget.Budget.HasValue)
- {
- builder.Append("<Budget>" + SecurityElement.Escape(hasBudget.Budget.Value.ToString(UsCulture)) + "</Budget>");
- }
-
- if (hasBudget.Revenue.HasValue)
- {
- builder.Append("<Revenue>" + SecurityElement.Escape(hasBudget.Revenue.Value.ToString(UsCulture)) + "</Revenue>");
- }
- }
-
- if (item.CommunityRating.HasValue)
- {
- builder.Append("<Rating>" + SecurityElement.Escape(item.CommunityRating.Value.ToString(UsCulture)) + "</Rating>");
- }
- if (item.VoteCount.HasValue)
- {
- builder.Append("<VoteCount>" + SecurityElement.Escape(item.VoteCount.Value.ToString(UsCulture)) + "</VoteCount>");
- }
-
- if (item.ProductionYear.HasValue && !(item is Person))
- {
- builder.Append("<ProductionYear>" + SecurityElement.Escape(item.ProductionYear.Value.ToString(UsCulture)) + "</ProductionYear>");
- }
-
- if (!string.IsNullOrEmpty(item.HomePageUrl))
- {
- builder.Append("<Website>" + SecurityElement.Escape(item.HomePageUrl) + "</Website>");
- }
-
- var hasAspectRatio = item as IHasAspectRatio;
- if (hasAspectRatio != null)
- {
- if (!string.IsNullOrEmpty(hasAspectRatio.AspectRatio))
- {
- builder.Append("<AspectRatio>" + SecurityElement.Escape(hasAspectRatio.AspectRatio) + "</AspectRatio>");
- }
- }
-
- if (!string.IsNullOrEmpty(item.PreferredMetadataLanguage))
- {
- builder.Append("<Language>" + SecurityElement.Escape(item.PreferredMetadataLanguage) + "</Language>");
- }
- if (!string.IsNullOrEmpty(item.PreferredMetadataCountryCode))
- {
- builder.Append("<CountryCode>" + SecurityElement.Escape(item.PreferredMetadataCountryCode) + "</CountryCode>");
- }
-
- // Use original runtime here, actual file runtime later in MediaInfo
- var runTimeTicks = item.RunTimeTicks;
-
- if (runTimeTicks.HasValue)
- {
- var timespan = TimeSpan.FromTicks(runTimeTicks.Value);
-
- builder.Append("<RunningTime>" + Convert.ToInt32(timespan.TotalMinutes).ToString(UsCulture) + "</RunningTime>");
- }
-
- if (item.ProviderIds != null)
- {
- foreach (var providerKey in item.ProviderIds.Keys)
- {
- var providerId = item.ProviderIds[providerKey];
- if (!string.IsNullOrEmpty(providerId))
- {
- builder.Append(string.Format("<{0}>{1}</{0}>", providerKey + "Id", SecurityElement.Escape(providerId)));
- }
- }
- }
-
- if (!string.IsNullOrWhiteSpace(item.Tagline))
- {
- builder.Append("<Taglines>");
- builder.Append("<Tagline>" + SecurityElement.Escape(item.Tagline) + "</Tagline>");
- builder.Append("</Taglines>");
- }
-
- if (item.Genres.Count > 0)
- {
- builder.Append("<Genres>");
-
- foreach (var genre in item.Genres)
- {
- builder.Append("<Genre>" + SecurityElement.Escape(genre) + "</Genre>");
- }
-
- builder.Append("</Genres>");
- }
-
- if (item.Studios.Count > 0)
- {
- builder.Append("<Studios>");
-
- foreach (var studio in item.Studios)
- {
- builder.Append("<Studio>" + SecurityElement.Escape(studio) + "</Studio>");
- }
-
- builder.Append("</Studios>");
- }
-
- if (item.Tags.Count > 0)
- {
- builder.Append("<Tags>");
-
- foreach (var tag in item.Tags)
- {
- builder.Append("<Tag>" + SecurityElement.Escape(tag) + "</Tag>");
- }
-
- builder.Append("</Tags>");
- }
-
- if (item.Keywords.Count > 0)
- {
- builder.Append("<PlotKeywords>");
-
- foreach (var tag in item.Keywords)
- {
- builder.Append("<PlotKeyword>" + SecurityElement.Escape(tag) + "</PlotKeyword>");
- }
-
- builder.Append("</PlotKeywords>");
- }
-
- var people = libraryManager.GetPeople(item);
-
- if (people.Count > 0)
- {
- builder.Append("<Persons>");
-
- foreach (var person in people)
- {
- builder.Append("<Person>");
- builder.Append("<Name>" + SecurityElement.Escape(person.Name) + "</Name>");
- builder.Append("<Type>" + SecurityElement.Escape(person.Type) + "</Type>");
- builder.Append("<Role>" + SecurityElement.Escape(person.Role) + "</Role>");
-
- if (person.SortOrder.HasValue)
- {
- builder.Append("<SortOrder>" + SecurityElement.Escape(person.SortOrder.Value.ToString(UsCulture)) + "</SortOrder>");
- }
-
- builder.Append("</Person>");
- }
-
- builder.Append("</Persons>");
- }
-
- var boxset = item as BoxSet;
- if (boxset != null)
- {
- AddLinkedChildren(boxset, builder, "CollectionItems", "CollectionItem");
- }
-
- var playlist = item as Playlist;
- if (playlist != null)
- {
- AddLinkedChildren(playlist, builder, "PlaylistItems", "PlaylistItem");
- }
-
- var hasShares = item as IHasShares;
- if (hasShares != null)
- {
- AddShares(hasShares, builder);
- }
- }
-
- public static void AddShares(IHasShares item, StringBuilder builder)
- {
- builder.Append("<Shares>");
-
- foreach (var share in item.Shares)
- {
- builder.Append("<Share>");
-
- builder.Append("<UserId>" + SecurityElement.Escape(share.UserId) + "</UserId>");
- builder.Append("<CanEdit>" + SecurityElement.Escape(share.CanEdit.ToString().ToLower()) + "</CanEdit>");
-
- builder.Append("</Share>");
- }
-
- builder.Append("</Shares>");
- }
-
- public static void AddChapters(Video item, StringBuilder builder, IItemRepository repository)
- {
- var chapters = repository.GetChapters(item.Id);
-
- builder.Append("<Chapters>");
-
- foreach (var chapter in chapters)
- {
- builder.Append("<Chapter>");
- builder.Append("<Name>" + SecurityElement.Escape(chapter.Name) + "</Name>");
-
- var time = TimeSpan.FromTicks(chapter.StartPositionTicks);
- var ms = Convert.ToInt64(time.TotalMilliseconds);
-
- builder.Append("<StartPositionMs>" + SecurityElement.Escape(ms.ToString(UsCulture)) + "</StartPositionMs>");
- builder.Append("</Chapter>");
- }
-
- builder.Append("</Chapters>");
- }
-
- /// <summary>
- /// Appends the media info.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- public static void AddMediaInfo<T>(T item, StringBuilder builder, IItemRepository itemRepository)
- where T : BaseItem
- {
- var video = item as Video;
-
- if (video != null)
- {
- //AddChapters(video, builder, itemRepository);
-
- if (video.Video3DFormat.HasValue)
- {
- switch (video.Video3DFormat.Value)
- {
- case Video3DFormat.FullSideBySide:
- builder.Append("<Format3D>FSBS</Format3D>");
- break;
- case Video3DFormat.FullTopAndBottom:
- builder.Append("<Format3D>FTAB</Format3D>");
- break;
- case Video3DFormat.HalfSideBySide:
- builder.Append("<Format3D>HSBS</Format3D>");
- break;
- case Video3DFormat.HalfTopAndBottom:
- builder.Append("<Format3D>HTAB</Format3D>");
- break;
- case Video3DFormat.MVC:
- builder.Append("<Format3D>MVC</Format3D>");
- break;
- }
- }
- }
- }
-
- public static void AddLinkedChildren(Folder item, StringBuilder builder, string pluralNodeName, string singularNodeName)
- {
- var items = item.LinkedChildren
- .Where(i => i.Type == LinkedChildType.Manual)
- .ToList();
-
- if (items.Count == 0)
- {
- return;
- }
-
- builder.Append("<" + pluralNodeName + ">");
- foreach (var link in items)
- {
- builder.Append("<" + singularNodeName + ">");
-
- if (!string.IsNullOrWhiteSpace(link.Path))
- {
- builder.Append("<Path>" + SecurityElement.Escape((link.Path)) + "</Path>");
- }
-
- builder.Append("</" + singularNodeName + ">");
- }
- builder.Append("</" + pluralNodeName + ">");
- }
- }
-}
diff --git a/MediaBrowser.LocalMetadata/packages.config b/MediaBrowser.LocalMetadata/packages.config
deleted file mode 100644
index ccef6d6862..0000000000
--- a/MediaBrowser.LocalMetadata/packages.config
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
- <package id="CommonIO" version="1.0.0.9" targetFramework="net45" />
- <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
-</packages> \ No newline at end of file
diff --git a/MediaBrowser.LocalMetadata/project.json b/MediaBrowser.LocalMetadata/project.json
new file mode 100644
index 0000000000..fbbe9eaf32
--- /dev/null
+++ b/MediaBrowser.LocalMetadata/project.json
@@ -0,0 +1,17 @@
+{
+ "frameworks":{
+ "netstandard1.6":{
+ "dependencies":{
+ "NETStandard.Library":"1.6.0",
+ }
+ },
+ ".NETPortable,Version=v4.5,Profile=Profile7":{
+ "buildOptions": {
+ "define": [ ]
+ },
+ "frameworkAssemblies":{
+
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs
index e435c00f23..bf7343f3d2 100644
--- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs
+++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs
@@ -4,6 +4,8 @@ using MediaBrowser.Model.MediaInfo;
using System;
using System.Collections.Generic;
using System.Linq;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Text;
namespace MediaBrowser.MediaEncoding.BdInfo
{
@@ -12,6 +14,15 @@ namespace MediaBrowser.MediaEncoding.BdInfo
/// </summary>
public class BdInfoExaminer : IBlurayExaminer
{
+ private readonly IFileSystem _fileSystem;
+ private readonly ITextEncoding _textEncoding;
+
+ public BdInfoExaminer(IFileSystem fileSystem, ITextEncoding textEncoding)
+ {
+ _fileSystem = fileSystem;
+ _textEncoding = textEncoding;
+ }
+
/// <summary>
/// Gets the disc info.
/// </summary>
@@ -19,7 +30,7 @@ namespace MediaBrowser.MediaEncoding.BdInfo
/// <returns>BlurayDiscInfo.</returns>
public BlurayDiscInfo GetDiscInfo(string path)
{
- var bdrom = new BDROM(path);
+ var bdrom = new BDROM(path, _fileSystem, _textEncoding);
bdrom.Scan();
diff --git a/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs b/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs
index 42a28d313c..5beab746d0 100644
--- a/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs
+++ b/MediaBrowser.MediaEncoding/Configuration/EncodingConfigurationFactory.cs
@@ -2,7 +2,9 @@
using MediaBrowser.Model.Configuration;
using System.Collections.Generic;
using System.IO;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.MediaEncoding.Configuration
{
@@ -48,7 +50,7 @@ namespace MediaBrowser.MediaEncoding.Configuration
// Validate
if (!_fileSystem.DirectoryExists(newPath))
{
- throw new DirectoryNotFoundException(string.Format("{0} does not exist.", newPath));
+ throw new FileNotFoundException(string.Format("{0} does not exist.", newPath));
}
}
}
diff --git a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs
index f42582270f..5a554d26fb 100644
--- a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs
@@ -7,13 +7,13 @@ using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.Diagnostics;
namespace MediaBrowser.MediaEncoding.Encoder
{
public class AudioEncoder : BaseEncoder
{
- public AudioEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager)
+ public AudioEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IProcessFactory processFactory) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager, processFactory)
{
}
@@ -114,5 +114,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
get { return false; }
}
+
}
}
diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
index c7b78aae3a..b8087fded2 100644
--- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
@@ -11,13 +11,12 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.Diagnostics;
using MediaBrowser.Model.Dlna;
namespace MediaBrowser.MediaEncoding.Encoder
@@ -33,6 +32,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
protected readonly ISessionManager SessionManager;
protected readonly ISubtitleEncoder SubtitleEncoder;
protected readonly IMediaSourceManager MediaSourceManager;
+ protected IProcessFactory ProcessFactory;
protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
@@ -44,7 +44,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
ILibraryManager libraryManager,
ISessionManager sessionManager,
ISubtitleEncoder subtitleEncoder,
- IMediaSourceManager mediaSourceManager)
+ IMediaSourceManager mediaSourceManager, IProcessFactory processFactory)
{
MediaEncoder = mediaEncoder;
Logger = logger;
@@ -55,6 +55,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
SessionManager = sessionManager;
SubtitleEncoder = subtitleEncoder;
MediaSourceManager = mediaSourceManager;
+ ProcessFactory = processFactory;
}
public async Task<EncodingJob> Start(EncodingJobOptions options,
@@ -73,27 +74,23 @@ namespace MediaBrowser.MediaEncoding.Encoder
var commandLineArgs = await GetCommandLineArguments(encodingJob).ConfigureAwait(false);
- var process = new Process
+ var process = ProcessFactory.Create(new ProcessOptions
{
- StartInfo = new ProcessStartInfo
- {
- CreateNoWindow = true,
- UseShellExecute = false,
-
- // Must consume both stdout and stderr or deadlocks may occur
- //RedirectStandardOutput = true,
- RedirectStandardError = true,
- RedirectStandardInput = true,
+ CreateNoWindow = true,
+ UseShellExecute = false,
- FileName = MediaEncoder.EncoderPath,
- Arguments = commandLineArgs,
+ // Must consume both stdout and stderr or deadlocks may occur
+ //RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ RedirectStandardInput = true,
- WindowStyle = ProcessWindowStyle.Hidden,
- ErrorDialog = false
- },
+ FileName = MediaEncoder.EncoderPath,
+ Arguments = commandLineArgs,
+ IsHidden = true,
+ ErrorDialog = false,
EnableRaisingEvents = true
- };
+ });
var workingDirectory = GetWorkingDirectory(options);
if (!string.IsNullOrWhiteSpace(workingDirectory))
@@ -110,7 +107,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
FileSystem.CreateDirectory(Path.GetDirectoryName(logFilePath));
// FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
- encodingJob.LogFileStream = FileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true);
+ encodingJob.LogFileStream = FileSystem.GetFileStream(logFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true);
var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(commandLineLogMessage + Environment.NewLine + Environment.NewLine);
await encodingJob.LogFileStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationToken).ConfigureAwait(false);
@@ -147,7 +144,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
return encodingJob;
}
- private void Cancel(Process process, EncodingJob job)
+ private void Cancel(IProcess process, EncodingJob job)
{
Logger.Info("Killing ffmpeg process for {0}", job.OutputFilePath);
@@ -162,7 +159,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// </summary>
/// <param name="process">The process.</param>
/// <param name="job">The job.</param>
- private void OnFfMpegProcessExited(Process process, EncodingJob job)
+ private void OnFfMpegProcessExited(IProcess process, EncodingJob job)
{
job.HasExited = true;
@@ -215,7 +212,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
try
{
- job.TaskCompletionSource.TrySetException(new ApplicationException("Encoding failed"));
+ job.TaskCompletionSource.TrySetException(new Exception("Encoding failed"));
}
catch
{
@@ -773,7 +770,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) &&
- !string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
{
param = "-pix_fmt yuv420p " + param;
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
index 7acff8fc86..b37e783b86 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Globalization;
+using MediaBrowser.Model.Diagnostics;
using MediaBrowser.Model.Logging;
namespace MediaBrowser.MediaEncoding.Encoder
@@ -8,10 +10,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
public class EncoderValidator
{
private readonly ILogger _logger;
+ private readonly IProcessFactory _processFactory;
- public EncoderValidator(ILogger logger)
+ public EncoderValidator(ILogger logger, IProcessFactory processFactory)
{
_logger = logger;
+ _processFactory = processFactory;
}
public Tuple<List<string>, List<string>> Validate(string encoderPath)
@@ -26,15 +30,19 @@ namespace MediaBrowser.MediaEncoding.Encoder
return new Tuple<List<string>, List<string>>(decoders, encoders);
}
- public bool ValidateVersion(string encoderAppPath)
+ public bool ValidateVersion(string encoderAppPath, bool logOutput)
{
string output = string.Empty;
try
{
output = GetProcessOutput(encoderAppPath, "-version");
}
- catch
+ catch (Exception ex)
{
+ if (logOutput)
+ {
+ _logger.ErrorException("Error validating encoder", ex);
+ }
}
if (string.IsNullOrWhiteSpace(output))
@@ -42,11 +50,27 @@ namespace MediaBrowser.MediaEncoding.Encoder
return false;
}
+ if (logOutput)
+ {
+ _logger.Info("ffmpeg info: {0}", output);
+ }
+
if (output.IndexOf("Libav developers", StringComparison.OrdinalIgnoreCase) != -1)
{
return false;
}
+ output = " " + output + " ";
+
+ for (var i = 2013; i <= 2015; i++)
+ {
+ var yearString = i.ToString(CultureInfo.InvariantCulture);
+ if (output.IndexOf(" " + yearString + " ", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ return false;
+ }
+ }
+
return true;
}
@@ -57,8 +81,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
output = GetProcessOutput(encoderAppPath, "-decoders");
}
- catch
+ catch (Exception )
{
+ //_logger.ErrorException("Error detecting available decoders", ex);
}
var found = new List<string>();
@@ -140,19 +165,16 @@ namespace MediaBrowser.MediaEncoding.Encoder
private string GetProcessOutput(string path, string arguments)
{
- var process = new Process
+ var process = _processFactory.Create(new ProcessOptions
{
- StartInfo = new ProcessStartInfo
- {
- CreateNoWindow = true,
- UseShellExecute = false,
- FileName = path,
- Arguments = arguments,
- WindowStyle = ProcessWindowStyle.Hidden,
- ErrorDialog = false,
- RedirectStandardOutput = true
- }
- };
+ CreateNoWindow = true,
+ UseShellExecute = false,
+ FileName = path,
+ Arguments = arguments,
+ IsHidden = true,
+ ErrorDialog = false,
+ RedirectStandardOutput = true
+ });
_logger.Info("Running {0} {1}", path, arguments);
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs
index 0c7ff1b760..b5ff5cbb6a 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs
@@ -11,6 +11,7 @@ using MediaBrowser.Model.Net;
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs
index f811a8d48a..e197bdb6fd 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs
@@ -606,9 +606,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
// 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) && videoStream.Level == -99)
+ if (string.Equals(videoStream.Codec, "mpeg4", StringComparison.OrdinalIgnoreCase))
{
- return false;
+ if (videoStream.Level == -99 || videoStream.Level == 15)
+ {
+ return false;
+ }
}
}
return true;
diff --git a/MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs b/MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs
index 71306e0ec0..42048ab9e2 100644
--- a/MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/FontConfigLoader.cs
@@ -3,9 +3,11 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
@@ -86,9 +88,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <returns>Task.</returns>
private async Task DownloadFontFile(string fontsDirectory, string fontFilename, IProgress<double> progress)
{
- var existingFile = Directory
- .EnumerateFiles(_appPaths.ProgramDataPath, fontFilename, SearchOption.AllDirectories)
- .FirstOrDefault();
+ var existingFile = _fileSystem
+ .GetFilePaths(_appPaths.ProgramDataPath, true)
+ .FirstOrDefault(i => string.Equals(fontFilename, Path.GetFileName(i), StringComparison.OrdinalIgnoreCase));
if (existingFile != null)
{
@@ -168,8 +170,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
var bytes = Encoding.UTF8.GetBytes(contents);
- using (var fileStream = _fileSystem.GetFileStream(fontConfigFile, FileMode.Create, FileAccess.Write,
- FileShare.Read, true))
+ using (var fileStream = _fileSystem.GetFileStream(fontConfigFile, FileOpenMode.Create, FileAccessMode.Write,
+ FileShareMode.Read, true))
{
await fileStream.WriteAsync(bytes, 0, bytes.Length);
}
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 3409903738..23e63dad0f 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -14,18 +14,17 @@ using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Serialization;
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Diagnostics;
+using MediaBrowser.Model.System;
namespace MediaBrowser.MediaEncoding.Encoder
{
@@ -80,14 +79,21 @@ namespace MediaBrowser.MediaEncoding.Encoder
protected readonly Func<IMediaSourceManager> MediaSourceManager;
private readonly IHttpClient _httpClient;
private readonly IZipClient _zipClient;
- private readonly IMemoryStreamProvider _memoryStreamProvider;
+ private readonly IProcessFactory _processFactory;
+ private readonly IMemoryStreamFactory _memoryStreamProvider;
private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>();
private readonly bool _hasExternalEncoder;
- private string _originalFFMpegPath;
- private string _originalFFProbePath;
+ private readonly string _originalFFMpegPath;
+ private readonly string _originalFFProbePath;
+ private readonly int DefaultImageExtractionTimeoutMs;
+ private readonly bool EnableEncoderFontFile;
- public MediaEncoder(ILogger logger, IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, bool hasExternalEncoder, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, Func<ISubtitleEncoder> subtitleEncoder, Func<IMediaSourceManager> mediaSourceManager, IHttpClient httpClient, IZipClient zipClient, IMemoryStreamProvider memoryStreamProvider)
+ private readonly IEnvironmentInfo _environmentInfo;
+
+ public MediaEncoder(ILogger logger, IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, bool hasExternalEncoder, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, Func<ISubtitleEncoder> subtitleEncoder, Func<IMediaSourceManager> mediaSourceManager, IHttpClient httpClient, IZipClient zipClient, IMemoryStreamFactory memoryStreamProvider, IProcessFactory processFactory,
+ int defaultImageExtractionTimeoutMs,
+ bool enableEncoderFontFile, IEnvironmentInfo environmentInfo)
{
_logger = logger;
_jsonSerializer = jsonSerializer;
@@ -103,18 +109,80 @@ namespace MediaBrowser.MediaEncoding.Encoder
_httpClient = httpClient;
_zipClient = zipClient;
_memoryStreamProvider = memoryStreamProvider;
+ _processFactory = processFactory;
+ DefaultImageExtractionTimeoutMs = defaultImageExtractionTimeoutMs;
+ EnableEncoderFontFile = enableEncoderFontFile;
+ _environmentInfo = environmentInfo;
FFProbePath = ffProbePath;
FFMpegPath = ffMpegPath;
_originalFFProbePath = ffProbePath;
_originalFFMpegPath = ffMpegPath;
_hasExternalEncoder = hasExternalEncoder;
+
+ SetEnvironmentVariable();
+ }
+
+ private readonly object _logLock = new object();
+ public void SetLogFilename(string name)
+ {
+ lock (_logLock)
+ {
+ try
+ {
+ _environmentInfo.SetProcessEnvironmentVariable("FFREPORT", "file=" + name + ":level=32");
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error setting FFREPORT environment variable", ex);
+ }
+ }
+ }
+
+ public void ClearLogFilename()
+ {
+ lock (_logLock)
+ {
+ try
+ {
+ _environmentInfo.SetProcessEnvironmentVariable("FFREPORT", null);
+ }
+ catch (Exception ex)
+ {
+ //_logger.ErrorException("Error setting FFREPORT environment variable", ex);
+ }
+ }
+ }
+
+ private void SetEnvironmentVariable()
+ {
+ try
+ {
+ //_environmentInfo.SetProcessEnvironmentVariable("FFREPORT", "file=program-YYYYMMDD-HHMMSS.txt:level=32");
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error setting FFREPORT environment variable", ex);
+ }
+ try
+ {
+ //_environmentInfo.SetUserEnvironmentVariable("FFREPORT", "file=program-YYYYMMDD-HHMMSS.txt:level=32");
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error setting FFREPORT environment variable", ex);
+ }
}
public string EncoderLocationType
{
get
{
+ if (_hasExternalEncoder)
+ {
+ return "External";
+ }
+
if (string.IsNullOrWhiteSpace(FFMpegPath))
{
return null;
@@ -157,12 +225,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (!string.IsNullOrWhiteSpace(FFMpegPath))
{
- var result = new EncoderValidator(_logger).Validate(FFMpegPath);
+ var result = new EncoderValidator(_logger, _processFactory).Validate(FFMpegPath);
SetAvailableDecoders(result.Item1);
SetAvailableEncoders(result.Item2);
- if (Environment.OSVersion.Platform == PlatformID.Win32NT)
+ if (EnableEncoderFontFile)
{
var directory = Path.GetDirectoryName(FFMpegPath);
@@ -179,6 +247,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
ConfigureEncoderPaths();
+ if (_hasExternalEncoder)
+ {
+ LogPaths();
+ return;
+ }
+
// If the path was passed in, save it into config now.
var encodingOptions = GetEncodingOptions();
var appPath = encodingOptions.EncoderAppPath;
@@ -188,7 +262,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (!string.IsNullOrWhiteSpace(valueToSave))
{
// if using system variable, don't save this.
- if (IsSystemInstalledPath(valueToSave))
+ if (IsSystemInstalledPath(valueToSave) || _hasExternalEncoder)
{
valueToSave = null;
}
@@ -203,6 +277,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
public async Task UpdateEncoderPath(string path, string pathType)
{
+ if (_hasExternalEncoder)
+ {
+ return;
+ }
+
Tuple<string, string> newPaths;
if (string.Equals(pathType, "system", StringComparison.OrdinalIgnoreCase))
@@ -218,7 +297,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
throw new ArgumentNullException("path");
}
- if (!File.Exists(path) && !Directory.Exists(path))
+ if (!FileSystem.FileExists(path) && !FileSystem.DirectoryExists(path))
{
throw new ResourceNotFoundException();
}
@@ -240,7 +319,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
path = newPaths.Item1;
- if (!ValidateVersion(path))
+ if (!ValidateVersion(path, true))
{
throw new ResourceNotFoundException("ffmpeg version 3.0 or greater is required.");
}
@@ -252,13 +331,18 @@ namespace MediaBrowser.MediaEncoding.Encoder
Init();
}
- private bool ValidateVersion(string path)
+ private bool ValidateVersion(string path, bool logOutput)
{
- return new EncoderValidator(_logger).ValidateVersion(path);
+ return new EncoderValidator(_logger, _processFactory).ValidateVersion(path, logOutput);
}
private void ConfigureEncoderPaths()
{
+ if (_hasExternalEncoder)
+ {
+ return;
+ }
+
var appPath = GetEncodingOptions().EncoderAppPath;
if (string.IsNullOrWhiteSpace(appPath))
@@ -287,12 +371,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (!string.IsNullOrWhiteSpace(appPath))
{
- if (Directory.Exists(appPath))
+ if (FileSystem.DirectoryExists(appPath))
{
return GetPathsFromDirectory(appPath);
}
- if (File.Exists(appPath))
+ if (FileSystem.FileExists(appPath))
{
return new Tuple<string, string>(appPath, GetProbePathFromEncoderPath(appPath));
}
@@ -306,7 +390,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
string encoderPath = null;
string probePath = null;
- if (_hasExternalEncoder && ValidateVersion(_originalFFMpegPath))
+ if (_hasExternalEncoder && ValidateVersion(_originalFFMpegPath, true))
{
encoderPath = _originalFFMpegPath;
probePath = _originalFFProbePath;
@@ -314,7 +398,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (string.IsNullOrWhiteSpace(encoderPath))
{
- if (ValidateVersion("ffmpeg") && ValidateVersion("ffprobe"))
+ if (ValidateVersion("ffmpeg", true) && ValidateVersion("ffprobe", false))
{
encoderPath = "ffmpeg";
probePath = "ffprobe";
@@ -328,16 +412,16 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
// Since we can't predict the file extension, first try directly within the folder
// If that doesn't pan out, then do a recursive search
- var files = Directory.GetFiles(path);
+ var files = FileSystem.GetFilePaths(path);
var excludeExtensions = new[] { ".c" };
var ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
var ffprobePath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
- if (string.IsNullOrWhiteSpace(ffmpegPath) || !File.Exists(ffmpegPath))
+ if (string.IsNullOrWhiteSpace(ffmpegPath) || !FileSystem.FileExists(ffmpegPath))
{
- files = Directory.GetFiles(path, "*", SearchOption.AllDirectories);
+ files = FileSystem.GetFilePaths(path, true);
ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
@@ -352,7 +436,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
private string GetProbePathFromEncoderPath(string appPath)
{
- return Directory.GetFiles(Path.GetDirectoryName(appPath))
+ return FileSystem.GetFilePaths(Path.GetDirectoryName(appPath))
.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase));
}
@@ -432,7 +516,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (request.AnalyzeDurationSections > 0)
{
analyzeDuration = "-analyzeduration " +
- (request.AnalyzeDurationSections*1000000).ToString(CultureInfo.InvariantCulture);
+ (request.AnalyzeDurationSections * 1000000).ToString(CultureInfo.InvariantCulture);
}
else
{
@@ -495,7 +579,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <param name="videoType">Type of the video.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{MediaInfoResult}.</returns>
- /// <exception cref="System.ApplicationException">ffprobe failed - streams and format are both null.</exception>
private async Task<MediaInfo> GetMediaInfoInternal(string inputPath,
string primaryPath,
MediaProtocol protocol,
@@ -509,31 +592,24 @@ namespace MediaBrowser.MediaEncoding.Encoder
? "{0} -i {1} -threads 0 -v info -print_format json -show_streams -show_chapters -show_format"
: "{0} -i {1} -threads 0 -v info -print_format json -show_streams -show_format";
- var process = new Process
+ var process = _processFactory.Create(new ProcessOptions
{
- StartInfo = new ProcessStartInfo
- {
- CreateNoWindow = true,
- UseShellExecute = false,
-
- // Must consume both or ffmpeg may hang due to deadlocks. See comments below.
- RedirectStandardOutput = true,
- //RedirectStandardError = true,
- RedirectStandardInput = false,
- FileName = FFProbePath,
- Arguments = string.Format(args,
- probeSizeArgument, inputPath).Trim(),
+ CreateNoWindow = true,
+ UseShellExecute = false,
- WindowStyle = ProcessWindowStyle.Hidden,
- ErrorDialog = false
- },
+ // Must consume both or ffmpeg may hang due to deadlocks. See comments below.
+ RedirectStandardOutput = true,
+ FileName = FFProbePath,
+ Arguments = string.Format(args, probeSizeArgument, inputPath).Trim(),
+ IsHidden = true,
+ ErrorDialog = false,
EnableRaisingEvents = true
- };
+ });
_logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
- using (var processWrapper = new ProcessWrapper(process, this, _logger, false))
+ using (var processWrapper = new ProcessWrapper(process, this, _logger))
{
await _ffProbeResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
@@ -558,7 +634,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (result.streams == null && result.format == null)
{
- throw new ApplicationException("ffprobe failed - streams and format are both null.");
+ throw new Exception("ffprobe failed - streams and format are both null.");
}
if (result.streams != null)
@@ -595,7 +671,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
catch
{
- StopProcess(processWrapper, 100, true);
+ StopProcess(processWrapper, 100);
throw;
}
@@ -644,31 +720,25 @@ namespace MediaBrowser.MediaEncoding.Encoder
var args = "{0} -i {1} -map 0:v:{2} -an -filter:v idet -frames:v 500 -an -f null /dev/null";
- var process = new Process
+ var process = _processFactory.Create(new ProcessOptions
{
- StartInfo = new ProcessStartInfo
- {
- CreateNoWindow = true,
- UseShellExecute = false,
-
- // Must consume both or ffmpeg may hang due to deadlocks. See comments below.
- //RedirectStandardOutput = true,
- RedirectStandardError = true,
- RedirectStandardInput = false,
- FileName = FFMpegPath,
- Arguments = string.Format(args, probeSizeArgument, inputPath, videoStream.Index.ToString(CultureInfo.InvariantCulture)).Trim(),
+ CreateNoWindow = true,
+ UseShellExecute = false,
- WindowStyle = ProcessWindowStyle.Hidden,
- ErrorDialog = false
- },
+ // Must consume both or ffmpeg may hang due to deadlocks. See comments below.
+ RedirectStandardError = true,
+ FileName = FFMpegPath,
+ Arguments = string.Format(args, probeSizeArgument, inputPath, videoStream.Index.ToString(CultureInfo.InvariantCulture)).Trim(),
+ IsHidden = true,
+ ErrorDialog = false,
EnableRaisingEvents = true
- };
+ });
_logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
var idetFoundInterlaced = false;
- using (var processWrapper = new ProcessWrapper(process, this, _logger, false))
+ using (var processWrapper = new ProcessWrapper(process, this, _logger))
{
try
{
@@ -711,7 +781,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
catch
{
- StopProcess(processWrapper, 100, true);
+ StopProcess(processWrapper, 100);
throw;
}
@@ -864,7 +934,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
var tempExtractPath = Path.Combine(ConfigurationManager.ApplicationPaths.TempDirectory, Guid.NewGuid() + ".jpg");
- Directory.CreateDirectory(Path.GetDirectoryName(tempExtractPath));
+ FileSystem.CreateDirectory(Path.GetDirectoryName(tempExtractPath));
// apply some filters to thumbnail extracted below (below) crop any black lines that we made and get the correct ar then scale to width 600.
// This filter chain may have adverse effects on recorded tv thumbnails if ar changes during presentation ex. commercials @ diff ar
@@ -898,7 +968,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
var mapArg = imageStreamIndex.HasValue ? (" -map 0:v:" + imageStreamIndex.Value.ToString(CultureInfo.InvariantCulture)) : string.Empty;
var enableThumbnail = !new List<string> { "wtv" }.Contains(container ?? string.Empty, StringComparer.OrdinalIgnoreCase);
- var thumbnail = enableThumbnail ? ",thumbnail=30" : string.Empty;
+ var thumbnail = enableThumbnail ? ",thumbnail=24" : string.Empty;
// Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case.
var args = useIFrame ? string.Format("-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}{4}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, thumbnail) :
@@ -916,22 +986,19 @@ namespace MediaBrowser.MediaEncoding.Encoder
args = string.Format("-ss {0} ", GetTimeParameter(offset.Value)) + args;
}
- var process = new Process
+ var process = _processFactory.Create(new ProcessOptions
{
- StartInfo = new ProcessStartInfo
- {
- CreateNoWindow = true,
- UseShellExecute = false,
- FileName = FFMpegPath,
- Arguments = args,
- WindowStyle = ProcessWindowStyle.Hidden,
- ErrorDialog = false
- }
- };
+ CreateNoWindow = true,
+ UseShellExecute = false,
+ FileName = FFMpegPath,
+ Arguments = args,
+ IsHidden = true,
+ ErrorDialog = false
+ });
_logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
- using (var processWrapper = new ProcessWrapper(process, this, _logger, false))
+ using (var processWrapper = new ProcessWrapper(process, this, _logger))
{
await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
@@ -944,14 +1011,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
var timeoutMs = ConfigurationManager.Configuration.ImageExtractionTimeoutMs;
if (timeoutMs <= 0)
{
- timeoutMs = Environment.Is64BitOperatingSystem ? (Environment.ProcessorCount > 2 ? 14000 : 20000) : 40000;
+ timeoutMs = DefaultImageExtractionTimeoutMs;
}
ranToCompletion = process.WaitForExit(timeoutMs);
if (!ranToCompletion)
{
- StopProcess(processWrapper, 1000, false);
+ StopProcess(processWrapper, 1000);
}
}
@@ -961,7 +1028,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1;
- var file = new FileInfo(tempExtractPath);
+ var file = FileSystem.GetFileInfo(tempExtractPath);
if (exitCode == -1 || !file.Exists || file.Length == 0)
{
@@ -969,7 +1036,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
_logger.Error(msg);
- throw new ApplicationException(msg);
+ throw new Exception(msg);
}
return tempExtractPath;
@@ -1022,19 +1089,15 @@ namespace MediaBrowser.MediaEncoding.Encoder
args = probeSize + " " + args;
}
- var process = new Process
+ var process = _processFactory.Create(new ProcessOptions
{
- StartInfo = new ProcessStartInfo
- {
- CreateNoWindow = true,
- UseShellExecute = false,
- FileName = FFMpegPath,
- Arguments = args,
- WindowStyle = ProcessWindowStyle.Hidden,
- ErrorDialog = false,
- RedirectStandardInput = true
- }
- };
+ CreateNoWindow = true,
+ UseShellExecute = false,
+ FileName = FFMpegPath,
+ Arguments = args,
+ IsHidden = true,
+ ErrorDialog = false
+ });
_logger.Info(process.StartInfo.FileName + " " + process.StartInfo.Arguments);
@@ -1042,7 +1105,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
bool ranToCompletion = false;
- using (var processWrapper = new ProcessWrapper(process, this, _logger, true))
+ using (var processWrapper = new ProcessWrapper(process, this, _logger))
{
try
{
@@ -1065,7 +1128,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
cancellationToken.ThrowIfCancellationRequested();
- var jpegCount = Directory.GetFiles(targetDirectory)
+ var jpegCount = FileSystem.GetFilePaths(targetDirectory)
.Count(i => string.Equals(Path.GetExtension(i), ".jpg", StringComparison.OrdinalIgnoreCase));
isResponsive = (jpegCount > lastCount);
@@ -1074,7 +1137,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (!ranToCompletion)
{
- StopProcess(processWrapper, 1000, false);
+ StopProcess(processWrapper, 1000);
}
}
finally
@@ -1090,7 +1153,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
_logger.Error(msg);
- throw new ApplicationException(msg);
+ throw new Exception(msg);
}
}
}
@@ -1107,7 +1170,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
LibraryManager,
SessionManager,
SubtitleEncoder(),
- MediaSourceManager())
+ MediaSourceManager(),
+ _processFactory)
.Start(options, progress, cancellationToken).ConfigureAwait(false);
await job.TaskCompletionSource.Task.ConfigureAwait(false);
@@ -1127,7 +1191,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
LibraryManager,
SessionManager,
SubtitleEncoder(),
- MediaSourceManager())
+ MediaSourceManager(),
+ _processFactory)
.Start(options, progress, cancellationToken).ConfigureAwait(false);
await job.TaskCompletionSource.Task.ConfigureAwait(false);
@@ -1144,40 +1209,25 @@ namespace MediaBrowser.MediaEncoding.Encoder
_runningProcesses.Add(process);
}
}
- private void StopProcess(ProcessWrapper process, int waitTimeMs, bool enableForceKill)
+ private void StopProcess(ProcessWrapper process, int waitTimeMs)
{
try
{
- _logger.Info("Killing ffmpeg process");
-
- if (process.IsRedirectingStdin)
+ if (process.Process.WaitForExit(waitTimeMs))
{
- try
- {
- process.Process.StandardInput.WriteLine("q");
- }
- catch (Exception)
- {
- _logger.Error("Error sending q command to process");
- }
+ return;
}
+ }
+ catch (Exception ex)
+ {
+ _logger.Error("Error in WaitForExit", ex);
+ }
- try
- {
- if (process.Process.WaitForExit(waitTimeMs))
- {
- return;
- }
- }
- catch (Exception ex)
- {
- _logger.Error("Error in WaitForExit", ex);
- }
+ try
+ {
+ _logger.Info("Killing ffmpeg process");
- if (enableForceKill)
- {
- process.Process.Kill();
- }
+ process.Process.Kill();
}
catch (Exception ex)
{
@@ -1198,14 +1248,17 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
if (!process.HasExited)
{
- StopProcess(process, 500, true);
+ StopProcess(process, 500);
}
}
}
public string EscapeSubtitleFilterPath(string path)
{
- return path.Replace('\\', '/').Replace(":/", "\\:/").Replace("'", "'\\\\\\''");
+ // https://ffmpeg.org/ffmpeg-filters.html#Notes-on-filtergraph-escaping
+ // We need to double escape
+
+ return path.Replace('\\', '/').Replace(":", "\\:").Replace("'", "'\\\\\\''");
}
/// <summary>
@@ -1231,25 +1284,23 @@ namespace MediaBrowser.MediaEncoding.Encoder
private class ProcessWrapper : IDisposable
{
- public readonly Process Process;
+ public readonly IProcess Process;
public bool HasExited;
public int? ExitCode;
private readonly MediaEncoder _mediaEncoder;
private readonly ILogger _logger;
- public bool IsRedirectingStdin { get; private set; }
- public ProcessWrapper(Process process, MediaEncoder mediaEncoder, ILogger logger, bool isRedirectingStdin)
+ public ProcessWrapper(IProcess process, MediaEncoder mediaEncoder, ILogger logger)
{
Process = process;
_mediaEncoder = mediaEncoder;
_logger = logger;
Process.Exited += Process_Exited;
- IsRedirectingStdin = isRedirectingStdin;
}
void Process_Exited(object sender, EventArgs e)
{
- var process = (Process)sender;
+ var process = (IProcess)sender;
HasExited = true;
@@ -1269,7 +1320,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
DisposeProcess(process);
}
- private void DisposeProcess(Process process)
+ private void DisposeProcess(IProcess process)
{
try
{
diff --git a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs
index 457fbe2c26..cbbca479a0 100644
--- a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs
@@ -8,13 +8,13 @@ using MediaBrowser.Model.Logging;
using System;
using System.IO;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.Diagnostics;
namespace MediaBrowser.MediaEncoding.Encoder
{
public class VideoEncoder : BaseEncoder
{
- public VideoEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager)
+ public VideoEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IProcessFactory processFactory) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager, processFactory)
{
}
@@ -191,5 +191,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
get { return true; }
}
+
}
} \ No newline at end of file
diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
index 1b55995778..63e789a59d 100644
--- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
+++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<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>
@@ -9,9 +9,10 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MediaBrowser.MediaEncoding</RootNamespace>
<AssemblyName>MediaBrowser.MediaEncoding</AssemblyName>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<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>
@@ -36,28 +37,9 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
- <Reference Include="BDInfo">
- <HintPath>..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\BDInfo.dll</HintPath>
- </Reference>
- <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
- </Reference>
- <Reference Include="DvdLib">
- <HintPath>..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\DvdLib.dll</HintPath>
- </Reference>
- <Reference Include="Patterns.Logging">
- <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.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.Xml" />
- <Reference Include="UniversalDetector">
- <HintPath>..\ThirdParty\UniversalDetector\UniversalDetector.dll</HintPath>
+ <Reference Include="UniversalDetector, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+ <HintPath>..\packages\UniversalDetector.1.0.1\lib\portable-net45+sl4+wp71+win8+wpa81\UniversalDetector.dll</HintPath>
+ <Private>True</Private>
</Reference>
</ItemGroup>
<ItemGroup>
@@ -80,9 +62,11 @@
<Compile Include="Probing\InternalMediaInfoResult.cs" />
<Compile Include="Probing\ProbeResultNormalizer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Subtitles\ConfigurationExtension.cs" />
<Compile Include="Subtitles\ISubtitleParser.cs" />
<Compile Include="Subtitles\ISubtitleWriter.cs" />
<Compile Include="Subtitles\JsonWriter.cs" />
+ <Compile Include="Subtitles\OpenSubtitleDownloader.cs" />
<Compile Include="Subtitles\ParserValues.cs" />
<Compile Include="Subtitles\SrtParser.cs" />
<Compile Include="Subtitles\SrtWriter.cs" />
@@ -93,6 +77,10 @@
<Compile Include="Subtitles\VttWriter.cs" />
</ItemGroup>
<ItemGroup>
+ <ProjectReference Include="..\BDInfo\BDInfo.csproj">
+ <Project>{88ae38df-19d7-406f-a6a9-09527719a21e}</Project>
+ <Name>BDInfo</Name>
+ </ProjectReference>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
<Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
<Name>MediaBrowser.Common</Name>
@@ -105,14 +93,15 @@
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
<Name>MediaBrowser.Model</Name>
</ProjectReference>
+ <ProjectReference Include="..\OpenSubtitlesHandler\OpenSubtitlesHandler.csproj">
+ <Project>{4a4402d4-e910-443b-b8fc-2c18286a2ca0}</Project>
+ <Name>OpenSubtitlesHandler</Name>
+ </ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Probing\whitelist.txt" />
- </ItemGroup>
- <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <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">
diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.nuget.targets b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.nuget.targets
new file mode 100644
index 0000000000..e69ce0e64f
--- /dev/null
+++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.nuget.targets
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Target Name="EmitMSBuildWarning" BeforeTargets="Build">
+ <Warning Text="Packages containing MSBuild targets and props files cannot be fully installed in projects targeting multiple frameworks. The MSBuild targets and props files have been ignored." />
+ </Target>
+</Project> \ No newline at end of file
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index b8c110e145..de055146a8 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -8,10 +8,11 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
@@ -22,9 +23,9 @@ namespace MediaBrowser.MediaEncoding.Probing
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
- private readonly IMemoryStreamProvider _memoryStreamProvider;
+ private readonly IMemoryStreamFactory _memoryStreamProvider;
- public ProbeResultNormalizer(ILogger logger, IFileSystem fileSystem, IMemoryStreamProvider memoryStreamProvider)
+ public ProbeResultNormalizer(ILogger logger, IFileSystem fileSystem, IMemoryStreamFactory memoryStreamProvider)
{
_logger = logger;
_fileSystem = fileSystem;
@@ -187,7 +188,13 @@ namespace MediaBrowser.MediaEncoding.Probing
private void FetchFromItunesInfo(string xml, MediaInfo info)
{
// Make things simpler and strip out the dtd
- xml = xml.Substring(xml.IndexOf("<plist", StringComparison.OrdinalIgnoreCase));
+ var plistIndex = xml.IndexOf("<plist", StringComparison.OrdinalIgnoreCase);
+
+ if (plistIndex != -1)
+ {
+ xml = xml.Substring(plistIndex);
+ }
+
xml = "<?xml version=\"1.0\"?>" + xml;
// <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>cast</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>name</key>\n\t\t\t<string>Blender Foundation</string>\n\t\t</dict>\n\t\t<dict>\n\t\t\t<key>name</key>\n\t\t\t<string>Janus Bager Kristensen</string>\n\t\t</dict>\n\t</array>\n\t<key>directors</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>name</key>\n\t\t\t<string>Sacha Goedegebure</string>\n\t\t</dict>\n\t</array>\n\t<key>studio</key>\n\t<string>Blender Foundation</string>\n</dict>\n</plist>\n
@@ -195,44 +202,63 @@ namespace MediaBrowser.MediaEncoding.Probing
{
using (var streamReader = new StreamReader(stream))
{
- // Use XmlReader for best performance
- using (var reader = XmlReader.Create(streamReader))
+ try
{
- reader.MoveToContent();
-
- // Loop through each element
- while (reader.Read())
+ // Use XmlReader for best performance
+ using (var reader = XmlReader.Create(streamReader))
{
- if (reader.NodeType == XmlNodeType.Element)
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
- switch (reader.Name)
+ if (reader.NodeType == XmlNodeType.Element)
{
- case "dict":
- using (var subtree = reader.ReadSubtree())
- {
- ReadFromDictNode(subtree, info);
- }
- break;
- default:
- reader.Skip();
- break;
+ switch (reader.Name)
+ {
+ case "dict":
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ continue;
+ }
+ using (var subtree = reader.ReadSubtree())
+ {
+ ReadFromDictNode(subtree, info);
+ }
+ break;
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ else
+ {
+ reader.Read();
}
}
}
}
+ catch (XmlException)
+ {
+ // I've seen probe examples where the iTunMOVI value is just "<"
+ // So we should not allow this to fail the entire probing operation
+ }
}
}
}
private void ReadFromDictNode(XmlReader reader, MediaInfo info)
{
- reader.MoveToContent();
-
string currentKey = null;
List<NameValuePair> pairs = new List<NameValuePair>();
+ reader.MoveToContent();
+ reader.Read();
+
// Loop through each element
- while (reader.Read())
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -258,9 +284,14 @@ namespace MediaBrowser.MediaEncoding.Probing
}
break;
case "array":
- if (!string.IsNullOrWhiteSpace(currentKey))
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ continue;
+ }
+ using (var subtree = reader.ReadSubtree())
{
- using (var subtree = reader.ReadSubtree())
+ if (!string.IsNullOrWhiteSpace(currentKey))
{
pairs.AddRange(ReadValueArray(subtree));
}
@@ -271,23 +302,35 @@ namespace MediaBrowser.MediaEncoding.Probing
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
}
private List<NameValuePair> ReadValueArray(XmlReader reader)
{
- reader.MoveToContent();
List<NameValuePair> pairs = new List<NameValuePair>();
+ reader.MoveToContent();
+ reader.Read();
+
// Loop through each element
- while (reader.Read())
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "dict":
+
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ continue;
+ }
using (var subtree = reader.ReadSubtree())
{
var dict = GetNameValuePair(subtree);
@@ -302,6 +345,10 @@ namespace MediaBrowser.MediaEncoding.Probing
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
return pairs;
@@ -359,13 +406,14 @@ namespace MediaBrowser.MediaEncoding.Probing
private NameValuePair GetNameValuePair(XmlReader reader)
{
- reader.MoveToContent();
-
string name = null;
string value = null;
+ reader.MoveToContent();
+ reader.Read();
+
// Loop through each element
- while (reader.Read())
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -382,6 +430,10 @@ namespace MediaBrowser.MediaEncoding.Probing
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
if (string.IsNullOrWhiteSpace(name) ||
@@ -780,24 +832,24 @@ namespace MediaBrowser.MediaEncoding.Probing
}
}
- var conductor = FFProbeHelpers.GetDictionaryValue(tags, "conductor");
- if (!string.IsNullOrWhiteSpace(conductor))
- {
- foreach (var person in Split(conductor, false))
- {
- audio.People.Add(new BaseItemPerson { Name = person, Type = PersonType.Conductor });
- }
- }
-
- var lyricist = FFProbeHelpers.GetDictionaryValue(tags, "lyricist");
+ //var conductor = FFProbeHelpers.GetDictionaryValue(tags, "conductor");
+ //if (!string.IsNullOrWhiteSpace(conductor))
+ //{
+ // foreach (var person in Split(conductor, false))
+ // {
+ // audio.People.Add(new BaseItemPerson { Name = person, Type = PersonType.Conductor });
+ // }
+ //}
+
+ //var lyricist = FFProbeHelpers.GetDictionaryValue(tags, "lyricist");
+ //if (!string.IsNullOrWhiteSpace(lyricist))
+ //{
+ // foreach (var person in Split(lyricist, false))
+ // {
+ // audio.People.Add(new BaseItemPerson { Name = person, Type = PersonType.Lyricist });
+ // }
+ //}
- if (!string.IsNullOrWhiteSpace(lyricist))
- {
- foreach (var person in Split(lyricist, false))
- {
- audio.People.Add(new BaseItemPerson { Name = person, Type = PersonType.Lyricist });
- }
- }
// Check for writer some music is tagged that way as alternative to composer/lyricist
var writer = FFProbeHelpers.GetDictionaryValue(tags, "writer");
@@ -972,27 +1024,10 @@ namespace MediaBrowser.MediaEncoding.Probing
{
if (_splitWhiteList == null)
{
- var file = GetType().Namespace + ".whitelist.txt";
-
- using (var stream = GetType().Assembly.GetManifestResourceStream(file))
- {
- using (var reader = new StreamReader(stream))
- {
- var list = new List<string>();
-
- while (!reader.EndOfStream)
+ _splitWhiteList = new List<string>
{
- var val = reader.ReadLine();
-
- if (!string.IsNullOrWhiteSpace(val))
- {
- list.Add(val);
- }
- }
-
- _splitWhiteList = list;
- }
- }
+ "AC/DC"
+ };
}
return _splitWhiteList;
@@ -1247,7 +1282,7 @@ namespace MediaBrowser.MediaEncoding.Probing
{
var packetBuffer = new byte['Å'];
- using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
+ using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
{
fs.Read(packetBuffer, 0, packetBuffer.Length);
}
diff --git a/MediaBrowser.MediaEncoding/Probing/whitelist.txt b/MediaBrowser.MediaEncoding/Probing/whitelist.txt
deleted file mode 100644
index 1fd3665512..0000000000
--- a/MediaBrowser.MediaEncoding/Probing/whitelist.txt
+++ /dev/null
@@ -1 +0,0 @@
-AC/DC \ No newline at end of file
diff --git a/MediaBrowser.Providers/Subtitles/ConfigurationExtension.cs b/MediaBrowser.MediaEncoding/Subtitles/ConfigurationExtension.cs
index f520915d8b..973c653a47 100644
--- a/MediaBrowser.Providers/Subtitles/ConfigurationExtension.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/ConfigurationExtension.cs
@@ -1,8 +1,8 @@
-using MediaBrowser.Common.Configuration;
+using System.Collections.Generic;
+using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Providers;
-using System.Collections.Generic;
-namespace MediaBrowser.Providers.Subtitles
+namespace MediaBrowser.MediaEncoding.Subtitles
{
public static class ConfigurationExtension
{
diff --git a/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs b/MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs
index 271f8170fc..e26a6392c9 100644
--- a/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/OpenSubtitleDownloader.cs
@@ -1,4 +1,11 @@
-using MediaBrowser.Common.Configuration;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
@@ -7,19 +14,13 @@ using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
using OpenSubtitlesHandler;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-namespace MediaBrowser.Providers.Subtitles
+namespace MediaBrowser.MediaEncoding.Subtitles
{
public class OpenSubtitleDownloader : ISubtitleProvider, IDisposable
{
@@ -31,14 +32,16 @@ namespace MediaBrowser.Providers.Subtitles
private readonly IEncryptionManager _encryption;
private readonly IJsonSerializer _json;
+ private readonly IFileSystem _fileSystem;
- public OpenSubtitleDownloader(ILogManager logManager, IHttpClient httpClient, IServerConfigurationManager config, IEncryptionManager encryption, IJsonSerializer json)
+ public OpenSubtitleDownloader(ILogManager logManager, IHttpClient httpClient, IServerConfigurationManager config, IEncryptionManager encryption, IJsonSerializer json, IFileSystem fileSystem)
{
_logger = logManager.GetLogger(GetType().Name);
_httpClient = httpClient;
_config = config;
_encryption = encryption;
_json = json;
+ _fileSystem = fileSystem;
_config.NamedConfigurationUpdating += _config_NamedConfigurationUpdating;
@@ -133,7 +136,7 @@ namespace MediaBrowser.Providers.Subtitles
if ((DateTime.UtcNow - _lastRateLimitException).TotalHours < 1)
{
- throw new ApplicationException("OpenSubtitles rate limit reached");
+ throw new Exception("OpenSubtitles rate limit reached");
}
var resultDownLoad = await OpenSubtitles.DownloadSubtitlesAsync(downloadsList, cancellationToken).ConfigureAwait(false);
@@ -141,12 +144,12 @@ namespace MediaBrowser.Providers.Subtitles
if ((resultDownLoad.Status ?? string.Empty).IndexOf("407", StringComparison.OrdinalIgnoreCase) != -1)
{
_lastRateLimitException = DateTime.UtcNow;
- throw new ApplicationException("OpenSubtitles rate limit reached");
+ throw new Exception("OpenSubtitles rate limit reached");
}
if (!(resultDownLoad is MethodResponseSubtitleDownload))
{
- throw new ApplicationException("Invalid response type");
+ throw new Exception("Invalid response type");
}
var results = ((MethodResponseSubtitleDownload)resultDownLoad).Results;
@@ -218,16 +221,17 @@ namespace MediaBrowser.Providers.Subtitles
});
}
- private string NormalizeLanguage(string language)
- {
- // Problem with Greek subtitle download #1349
- if (string.Equals (language, "gre", StringComparison.OrdinalIgnoreCase)) {
-
- return "ell";
- }
+ private string NormalizeLanguage(string language)
+ {
+ // Problem with Greek subtitle download #1349
+ if (string.Equals(language, "gre", StringComparison.OrdinalIgnoreCase))
+ {
+
+ return "ell";
+ }
- return language;
- }
+ return language;
+ }
public async Task<IEnumerable<RemoteSubtitleInfo>> Search(SubtitleSearchRequest request, CancellationToken cancellationToken)
{
@@ -265,14 +269,19 @@ namespace MediaBrowser.Providers.Subtitles
await Login(cancellationToken).ConfigureAwait(false);
- var subLanguageId = NormalizeLanguage(request.Language);
- var hash = Utilities.ComputeHash(request.MediaPath);
- var fileInfo = new FileInfo(request.MediaPath);
+ var subLanguageId = NormalizeLanguage(request.Language);
+ string hash;
+
+ using (var fileStream = _fileSystem.OpenRead(request.MediaPath))
+ {
+ hash = Utilities.ComputeHash(fileStream);
+ }
+ var fileInfo = _fileSystem.GetFileInfo(request.MediaPath);
var movieByteSize = fileInfo.Length;
var searchImdbId = request.ContentType == VideoContentType.Movie ? imdbId.ToString(_usCulture) : "";
var subtitleSearchParameters = request.ContentType == VideoContentType.Episode
? new List<SubtitleSearchParameters> {
- new SubtitleSearchParameters(subLanguageId,
+ new SubtitleSearchParameters(subLanguageId,
query: request.SeriesName,
season: request.ParentIndexNumber.Value.ToString(_usCulture),
episode: request.IndexNumber.Value.ToString(_usCulture))
@@ -282,9 +291,9 @@ namespace MediaBrowser.Providers.Subtitles
new SubtitleSearchParameters(subLanguageId, query: request.Name, imdbid: searchImdbId)
};
var parms = new List<SubtitleSearchParameters> {
- new SubtitleSearchParameters( subLanguageId,
- movieHash: hash,
- movieByteSize: movieByteSize,
+ new SubtitleSearchParameters( subLanguageId,
+ movieHash: hash,
+ movieByteSize: movieByteSize,
imdbid: searchImdbId ),
};
parms.AddRange(subtitleSearchParameters);
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
index 6dda49a554..0bbc2d9162 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -17,9 +17,9 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Diagnostics;
+using MediaBrowser.Model.Text;
using UniversalDetector;
namespace MediaBrowser.MediaEncoding.Subtitles
@@ -34,9 +34,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
private readonly IJsonSerializer _json;
private readonly IHttpClient _httpClient;
private readonly IMediaSourceManager _mediaSourceManager;
- private readonly IMemoryStreamProvider _memoryStreamProvider;
+ private readonly IMemoryStreamFactory _memoryStreamProvider;
+ private readonly IProcessFactory _processFactory;
+ private readonly ITextEncoding _textEncoding;
- public SubtitleEncoder(ILibraryManager libraryManager, ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IJsonSerializer json, IHttpClient httpClient, IMediaSourceManager mediaSourceManager, IMemoryStreamProvider memoryStreamProvider)
+ public SubtitleEncoder(ILibraryManager libraryManager, ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IJsonSerializer json, IHttpClient httpClient, IMediaSourceManager mediaSourceManager, IMemoryStreamFactory memoryStreamProvider, IProcessFactory processFactory, ITextEncoding textEncoding)
{
_libraryManager = libraryManager;
_logger = logger;
@@ -47,6 +49,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles
_httpClient = httpClient;
_mediaSourceManager = mediaSourceManager;
_memoryStreamProvider = memoryStreamProvider;
+ _processFactory = processFactory;
+ _textEncoding = textEncoding;
}
private string SubtitleCachePath
@@ -137,8 +141,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles
.ConfigureAwait(false);
var inputFormat = subtitle.Item2;
+ var writer = TryGetWriter(outputFormat);
- if (string.Equals(inputFormat, outputFormat, StringComparison.OrdinalIgnoreCase) && TryGetWriter(outputFormat) == null)
+ if (string.Equals(inputFormat, outputFormat, StringComparison.OrdinalIgnoreCase) && writer == null)
+ {
+ return subtitle.Item1;
+ }
+
+ if (writer == null)
{
return subtitle.Item1;
}
@@ -417,7 +427,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// or
/// outputPath
/// </exception>
- /// <exception cref="System.ApplicationException"></exception>
private async Task ConvertTextSubtitleToSrtInternal(string inputPath, string language, MediaProtocol inputProtocol, string outputPath, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(inputPath))
@@ -439,46 +448,29 @@ namespace MediaBrowser.MediaEncoding.Subtitles
encodingParam = " -sub_charenc " + encodingParam;
}
- var process = new Process
+ var process = _processFactory.Create(new ProcessOptions
{
- StartInfo = new ProcessStartInfo
- {
- RedirectStandardOutput = false,
- RedirectStandardError = true,
- RedirectStandardInput = true,
-
- CreateNoWindow = true,
- UseShellExecute = false,
- FileName = _mediaEncoder.EncoderPath,
- Arguments = string.Format("{0} -i \"{1}\" -c:s srt \"{2}\"", encodingParam, inputPath, outputPath),
+ CreateNoWindow = true,
+ UseShellExecute = false,
+ FileName = _mediaEncoder.EncoderPath,
+ Arguments = string.Format("{0} -i \"{1}\" -c:s srt \"{2}\"", encodingParam, inputPath, outputPath),
- WindowStyle = ProcessWindowStyle.Hidden,
- ErrorDialog = false
- }
- };
+ IsHidden = true,
+ ErrorDialog = false
+ });
_logger.Info("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
- var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "ffmpeg-sub-convert-" + Guid.NewGuid() + ".txt");
- _fileSystem.CreateDirectory(Path.GetDirectoryName(logFilePath));
-
- var logFileStream = _fileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read,
- true);
-
try
{
process.Start();
}
catch (Exception ex)
{
- logFileStream.Dispose();
-
_logger.ErrorException("Error starting ffmpeg", ex);
throw;
}
-
- var logTask = process.StandardError.BaseStream.CopyToAsync(logFileStream);
var ranToCompletion = process.WaitForExit(60000);
@@ -488,19 +480,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
_logger.Info("Killing ffmpeg subtitle conversion process");
- process.StandardInput.WriteLine("q");
- process.WaitForExit(1000);
-
- await logTask.ConfigureAwait(false);
+ process.Kill();
}
catch (Exception ex)
{
_logger.ErrorException("Error killing subtitle conversion process", ex);
}
- finally
- {
- logFileStream.Dispose();
- }
}
var exitCode = ranToCompletion ? process.ExitCode : -1;
@@ -533,13 +518,15 @@ namespace MediaBrowser.MediaEncoding.Subtitles
if (failed)
{
- var msg = string.Format("ffmpeg subtitle converted failed for {0}", inputPath);
+ var msg = string.Format("ffmpeg subtitle conversion failed for {0}", inputPath);
_logger.Error(msg);
- throw new ApplicationException(msg);
+ throw new Exception(msg);
}
await SetAssFont(outputPath).ConfigureAwait(false);
+
+ _logger.Info("ffmpeg subtitle conversion succeeded for {0}", inputPath);
}
/// <summary>
@@ -592,48 +579,30 @@ namespace MediaBrowser.MediaEncoding.Subtitles
var processArgs = string.Format("-i {0} -map 0:{1} -an -vn -c:s {2} \"{3}\"", inputPath,
subtitleStreamIndex, outputCodec, outputPath);
- var process = new Process
+ var process = _processFactory.Create(new ProcessOptions
{
- StartInfo = new ProcessStartInfo
- {
- CreateNoWindow = true,
- UseShellExecute = false,
-
- RedirectStandardOutput = false,
- RedirectStandardError = true,
- RedirectStandardInput = true,
+ CreateNoWindow = true,
+ UseShellExecute = false,
- FileName = _mediaEncoder.EncoderPath,
- Arguments = processArgs,
- WindowStyle = ProcessWindowStyle.Hidden,
- ErrorDialog = false
- }
- };
+ FileName = _mediaEncoder.EncoderPath,
+ Arguments = processArgs,
+ IsHidden = true,
+ ErrorDialog = false
+ });
_logger.Info("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
- var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "ffmpeg-sub-extract-" + Guid.NewGuid() + ".txt");
- _fileSystem.CreateDirectory(Path.GetDirectoryName(logFilePath));
-
- var logFileStream = _fileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read,
- true);
-
try
{
process.Start();
}
catch (Exception ex)
{
- logFileStream.Dispose();
-
_logger.ErrorException("Error starting ffmpeg", ex);
throw;
}
- // Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
- Task.Run(() => StartStreamingLog(process.StandardError.BaseStream, logFileStream));
-
var ranToCompletion = process.WaitForExit(300000);
if (!ranToCompletion)
@@ -642,17 +611,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
_logger.Info("Killing ffmpeg subtitle extraction process");
- process.StandardInput.WriteLine("q");
- process.WaitForExit(1000);
+ process.Kill();
}
catch (Exception ex)
{
_logger.ErrorException("Error killing subtitle extraction process", ex);
}
- finally
- {
- logFileStream.Dispose();
- }
}
var exitCode = ranToCompletion ? process.ExitCode : -1;
@@ -674,10 +638,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
}
- catch (DirectoryNotFoundException)
- {
-
- }
catch (IOException ex)
{
_logger.ErrorException("Error deleting extracted subtitle {0}", ex, outputPath);
@@ -694,7 +654,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
_logger.Error(msg);
- throw new ApplicationException(msg);
+ throw new Exception(msg);
}
else
{
@@ -709,33 +669,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
}
- private async Task StartStreamingLog(Stream source, Stream target)
- {
- try
- {
- using (var reader = new StreamReader(source))
- {
- while (!reader.EndOfStream)
- {
- var line = await reader.ReadLineAsync().ConfigureAwait(false);
-
- var bytes = Encoding.UTF8.GetBytes(Environment.NewLine + line);
-
- await target.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
- await target.FlushAsync().ConfigureAwait(false);
- }
- }
- }
- catch (ObjectDisposedException)
- {
- // Don't spam the log. This doesn't seem to throw in windows, but sometimes under linux
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error reading ffmpeg log", ex);
- }
- }
-
/// <summary>
/// Sets the ass font.
/// </summary>
@@ -748,20 +681,26 @@ namespace MediaBrowser.MediaEncoding.Subtitles
string text;
Encoding encoding;
- using (var reader = new StreamReader(file, true))
+ using (var fileStream = _fileSystem.OpenRead(file))
{
- encoding = reader.CurrentEncoding;
+ using (var reader = new StreamReader(fileStream, true))
+ {
+ encoding = reader.CurrentEncoding;
- text = await reader.ReadToEndAsync().ConfigureAwait(false);
+ text = await reader.ReadToEndAsync().ConfigureAwait(false);
+ }
}
var newText = text.Replace(",Arial,", ",Arial Unicode MS,");
if (!string.Equals(text, newText))
{
- using (var writer = new StreamWriter(file, false, encoding))
+ using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
{
- writer.Write(newText);
+ using (var writer = new StreamWriter(fileStream, encoding))
+ {
+ writer.Write(newText);
+ }
}
}
}
@@ -794,7 +733,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
if (protocol == MediaProtocol.File)
{
- if (GetFileEncoding(path).Equals(Encoding.UTF8))
+ var fileEncoding = _textEncoding.GetFileEncoding(path);
+
+ if (fileEncoding != null && fileEncoding.Equals(Encoding.UTF8))
{
return string.Empty;
}
@@ -901,29 +842,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles
return null;
}
- private Encoding GetFileEncoding(string srcFile)
- {
- // *** Detect byte order mark if any - otherwise assume default
- var buffer = new byte[5];
-
- using (var file = _fileSystem.GetFileStream(srcFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
- {
- file.Read(buffer, 0, 5);
- }
-
- if (buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf)
- return Encoding.UTF8;
- if (buffer[0] == 0xfe && buffer[1] == 0xff)
- return Encoding.Unicode;
- if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0xfe && buffer[3] == 0xff)
- return Encoding.UTF32;
- if (buffer[0] == 0x2b && buffer[1] == 0x2f && buffer[2] == 0x76)
- return Encoding.UTF7;
-
- // It's ok - anything aside from utf is ok since that's what we're looking for
- return Encoding.Default;
- }
-
private async Task<Stream> GetStream(string path, MediaProtocol protocol, CancellationToken cancellationToken)
{
if (protocol == MediaProtocol.Http)
@@ -932,7 +850,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
if (protocol == MediaProtocol.File)
{
- return _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
+ return _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite);
}
throw new ArgumentOutOfRangeException("protocol");
diff --git a/MediaBrowser.MediaEncoding/packages.config b/MediaBrowser.MediaEncoding/packages.config
index 32a5c213db..bac7840a55 100644
--- a/MediaBrowser.MediaEncoding/packages.config
+++ b/MediaBrowser.MediaEncoding/packages.config
@@ -1,6 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="CommonIO" version="1.0.0.9" targetFramework="net45" />
- <package id="MediaBrowser.BdInfo" version="1.0.0.10" targetFramework="net45" />
- <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
+ <package id="UniversalDetector" version="1.0.1" targetFramework="net46" />
</packages> \ No newline at end of file
diff --git a/MediaBrowser.MediaEncoding/project.json b/MediaBrowser.MediaEncoding/project.json
new file mode 100644
index 0000000000..fbbe9eaf32
--- /dev/null
+++ b/MediaBrowser.MediaEncoding/project.json
@@ -0,0 +1,17 @@
+{
+ "frameworks":{
+ "netstandard1.6":{
+ "dependencies":{
+ "NETStandard.Library":"1.6.0",
+ }
+ },
+ ".NETPortable,Version=v4.5,Profile=Profile7":{
+ "buildOptions": {
+ "define": [ ]
+ },
+ "frameworkAssemblies":{
+
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
deleted file mode 100644
index fa1acdca96..0000000000
--- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
+++ /dev/null
@@ -1,1227 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.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>10.0</MinimumVisualStudioVersion>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <ProjectGuid>{D729ADB1-1C01-428D-B680-8EFACD687B2A}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>MediaBrowser.Model</RootNamespace>
- <AssemblyName>MediaBrowser.Model</AssemblyName>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
- <TargetFrameworkProfile>Profile259</TargetFrameworkProfile>
- <FileAlignment>512</FileAlignment>
- <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
- <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
- <FileUpgradeFlags>
- </FileUpgradeFlags>
- <UpgradeBackupLocation>
- </UpgradeBackupLocation>
- <OldToolsVersion>4.0</OldToolsVersion>
- <PublishUrl>publish\</PublishUrl>
- <Install>true</Install>
- <InstallFrom>Disk</InstallFrom>
- <UpdateEnabled>false</UpdateEnabled>
- <UpdateMode>Foreground</UpdateMode>
- <UpdateInterval>7</UpdateInterval>
- <UpdateIntervalUnits>Days</UpdateIntervalUnits>
- <UpdatePeriodically>false</UpdatePeriodically>
- <UpdateRequired>false</UpdateRequired>
- <MapFileExtensions>true</MapFileExtensions>
- <ApplicationRevision>0</ApplicationRevision>
- <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
- <IsWebBootstrapper>false</IsWebBootstrapper>
- <UseApplicationTrust>false</UseApplicationTrust>
- <BootstrapperEnabled>true</BootstrapperEnabled>
- <RestorePackages>true</RestorePackages>
- <NuGetPackageImportStamp>
- </NuGetPackageImportStamp>
- </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>
- <!-- A reference to the entire .NET Framework is automatically included -->
- <None Include="app.config" />
- </ItemGroup>
- <ItemGroup>
- <Compile Include="..\MediaBrowser.Model\Activity\ActivityLogEntry.cs">
- <Link>Activity\ActivityLogEntry.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\ApiClientExtensions.cs">
- <Link>ApiClient\ApiClientExtensions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\ApiHelpers.cs">
- <Link>ApiClient\ApiHelpers.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\ConnectionMode.cs">
- <Link>ApiClient\ConnectionMode.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\ConnectionOptions.cs">
- <Link>ApiClient\ConnectionOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\ConnectionResult.cs">
- <Link>ApiClient\ConnectionResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\ConnectionState.cs">
- <Link>ApiClient\ConnectionState.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\ConnectSignupResponse.cs">
- <Link>ApiClient\ConnectSignupResponse.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\GeneralCommandEventArgs.cs">
- <Link>ApiClient\GeneralCommandEventArgs.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\HttpResponseEventArgs.cs">
- <Link>ApiClient\HttpResponseEventArgs.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\IApiClient.cs">
- <Link>ApiClient\IApiClient.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\IClientWebSocket.cs">
- <Link>ApiClient\IClientWebSocket.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\IConnectionManager.cs">
- <Link>ApiClient\IConnectionManager.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\IDevice.cs">
- <Link>ApiClient\IDevice.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\IServerEvents.cs">
- <Link>ApiClient\IServerEvents.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\NetworkStatus.cs">
- <Link>ApiClient\NetworkStatus.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\RemoteLogoutReason.cs">
- <Link>ApiClient\RemoteLogoutReason.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\ServerCredentials.cs">
- <Link>ApiClient\ServerCredentials.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\ServerDiscoveryInfo.cs">
- <Link>ApiClient\ServerDiscoveryInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\ServerInfo.cs">
- <Link>ApiClient\ServerInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\ServerUserInfo.cs">
- <Link>ApiClient\ServerUserInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\SessionUpdatesEventArgs.cs">
- <Link>ApiClient\SessionUpdatesEventArgs.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\WakeOnLanInfo.cs">
- <Link>ApiClient\WakeOnLanInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Branding\BrandingOptions.cs">
- <Link>Branding\BrandingOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Channels\AllChannelMediaQuery.cs">
- <Link>Channels\AllChannelMediaQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Channels\ChannelFeatures.cs">
- <Link>Channels\ChannelFeatures.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Channels\ChannelFolderType.cs">
- <Link>Channels\ChannelFolderType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Channels\ChannelInfo.cs">
- <Link>Channels\ChannelInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Channels\ChannelItemQuery.cs">
- <Link>Channels\ChannelItemQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Channels\ChannelItemSortField.cs">
- <Link>Channels\ChannelItemSortField.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Channels\ChannelMediaContentType.cs">
- <Link>Channels\ChannelMediaContentType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Channels\ChannelMediaType.cs">
- <Link>Channels\ChannelMediaType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Channels\ChannelQuery.cs">
- <Link>Channels\ChannelQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Collections\CollectionCreationResult.cs">
- <Link>Collections\CollectionCreationResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\AccessSchedule.cs">
- <Link>Configuration\AccessSchedule.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\BaseApplicationConfiguration.cs">
- <Link>Configuration\BaseApplicationConfiguration.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\ChannelOptions.cs">
- <Link>Configuration\ChannelOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\ChapterOptions.cs">
- <Link>Configuration\ChapterOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\DlnaOptions.cs">
- <Link>Configuration\DlnaOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\DynamicDayOfWeek.cs">
- <Link>Configuration\DynamicDayOfWeek.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\EncodingOptions.cs">
- <Link>Configuration\EncodingOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\FanartOptions.cs">
- <Link>Configuration\FanartOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\ImageOption.cs">
- <Link>Configuration\ImageOption.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\ImageSavingConvention.cs">
- <Link>Configuration\ImageSavingConvention.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\LibraryOptions.cs">
- <Link>Configuration\LibraryOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\MetadataConfiguration.cs">
- <Link>Configuration\MetadataConfiguration.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\MetadataOptions.cs">
- <Link>Configuration\MetadataOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\MetadataPlugin.cs">
- <Link>Configuration\MetadataPlugin.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\MetadataPluginSummary.cs">
- <Link>Configuration\MetadataPluginSummary.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\MetadataPluginType.cs">
- <Link>Configuration\MetadataPluginType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\PathSubstitution.cs">
- <Link>Configuration\PathSubstitution.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\PeopleMetadataOptions.cs">
- <Link>Configuration\PeopleMetadataOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\ServerConfiguration.cs">
- <Link>Configuration\ServerConfiguration.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\SubtitlePlaybackMode.cs">
- <Link>Configuration\SubtitlePlaybackMode.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\UnratedItem.cs">
- <Link>Configuration\UnratedItem.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\UserConfiguration.cs">
- <Link>Configuration\UserConfiguration.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\XbmcMetadataOptions.cs">
- <Link>Configuration\XbmcMetadataOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\ConnectAuthenticationExchangeResult.cs">
- <Link>ApiClient\ConnectAuthenticationExchangeResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\ConnectAuthenticationResult.cs">
- <Link>Connect\ConnectAuthenticationResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\ConnectAuthorization.cs">
- <Link>Connect\ConnectAuthorization.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\ConnectAuthorizationRequest.cs">
- <Link>Connect\ConnectAuthorizationRequest.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\ConnectPassword.cs">
- <Link>Connect\ConnectPassword.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\ConnectUser.cs">
- <Link>Connect\ConnectUser.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\ConnectUserQuery.cs">
- <Link>Connect\ConnectUserQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\ConnectUserServer.cs">
- <Link>Connect\ConnectUserServer.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\PinCreationResult.cs">
- <Link>Connect\PinCreationResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\PinExchangeResult.cs">
- <Link>Connect\PinExchangeResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\PinStatusResult.cs">
- <Link>Connect\PinStatusResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\UserLinkType.cs">
- <Link>Connect\UserLinkType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Devices\ContentUploadHistory.cs">
- <Link>Devices\ContentUploadHistory.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Devices\DeviceInfo.cs">
- <Link>Devices\DeviceInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Devices\DeviceOptions.cs">
- <Link>Devices\DeviceOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Devices\DeviceQuery.cs">
- <Link>Devices\DeviceQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Devices\DevicesOptions.cs">
- <Link>Devices\DevicesOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Devices\LocalFileInfo.cs">
- <Link>Devices\LocalFileInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\AudioOptions.cs">
- <Link>Dlna\AudioOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\CodecProfile.cs">
- <Link>Dlna\CodecProfile.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\CodecType.cs">
- <Link>Dlna\CodecType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\ConditionProcessor.cs">
- <Link>Dlna\ConditionProcessor.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\ContainerProfile.cs">
- <Link>Dlna\ContainerProfile.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\ContentFeatureBuilder.cs">
- <Link>Dlna\ContentFeatureBuilder.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\DeviceIdentification.cs">
- <Link>Dlna\DeviceIdentification.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\DeviceProfile.cs">
- <Link>Dlna\DeviceProfile.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\DeviceProfileInfo.cs">
- <Link>Dlna\DeviceProfileInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\DeviceProfileType.cs">
- <Link>Dlna\DeviceProfileType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\DirectPlayProfile.cs">
- <Link>Dlna\DirectPlayProfile.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\DlnaFlags.cs">
- <Link>Dlna\DlnaFlags.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\DlnaMaps.cs">
- <Link>Dlna\DlnaMaps.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\DlnaProfileType.cs">
- <Link>Dlna\DlnaProfileType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\EncodingContext.cs">
- <Link>Dlna\EncodingContext.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\HeaderMatchType.cs">
- <Link>Dlna\HeaderMatchType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\HttpHeaderInfo.cs">
- <Link>Dlna\HttpHeaderInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\ITranscoderSupport.cs">
- <Link>Dlna\ITranscoderSupport.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\MediaFormatProfile.cs">
- <Link>Dlna\MediaFormatProfile.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\MediaFormatProfileResolver.cs">
- <Link>Dlna\MediaFormatProfileResolver.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\PlaybackErrorCode.cs">
- <Link>Dlna\PlaybackErrorCode.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\PlaybackException.cs">
- <Link>Dlna\PlaybackException.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\ProfileCondition.cs">
- <Link>Dlna\ProfileCondition.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\ProfileConditionType.cs">
- <Link>Dlna\ProfileConditionType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\ProfileConditionValue.cs">
- <Link>Dlna\ProfileConditionValue.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\ResolutionConfiguration.cs">
- <Link>Dlna\ResolutionConfiguration.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\ResolutionNormalizer.cs">
- <Link>Dlna\ResolutionNormalizer.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\ResolutionOptions.cs">
- <Link>Dlna\ResolutionOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\ResponseProfile.cs">
- <Link>Dlna\ResponseProfile.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\SearchCriteria.cs">
- <Link>Dlna\SearchCriteria.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\SearchType.cs">
- <Link>Dlna\SearchType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\SortCriteria.cs">
- <Link>Dlna\SortCriteria.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\StreamBuilder.cs">
- <Link>Dlna\StreamBuilder.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\StreamInfo.cs">
- <Link>Dlna\StreamInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\StreamInfoSorter.cs">
- <Link>Dlna\StreamInfoSorter.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\SubtitleDeliveryMethod.cs">
- <Link>Dlna\SubtitleDeliveryMethod.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\SubtitleProfile.cs">
- <Link>Dlna\SubtitleProfile.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\SubtitleStreamInfo.cs">
- <Link>Dlna\SubtitleStreamInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\TranscodeSeekInfo.cs">
- <Link>Dlna\TranscodeSeekInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\TranscodingProfile.cs">
- <Link>Dlna\TranscodingProfile.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\VideoOptions.cs">
- <Link>Dlna\VideoOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\XmlAttribute.cs">
- <Link>Dlna\XmlAttribute.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Drawing\DrawingUtils.cs">
- <Link>Drawing\DrawingUtils.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Drawing\ImageFormat.cs">
- <Link>Drawing\ImageFormat.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Drawing\ImageOrientation.cs">
- <Link>Drawing\ImageOrientation.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Drawing\ImageSize.cs">
- <Link>Drawing\ImageSize.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\BaseItemDto.cs">
- <Link>Dto\BaseItemDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\BaseItemPerson.cs">
- <Link>Dto\BaseItemPerson.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\ChapterInfoDto.cs">
- <Link>Dto\ChapterInfoDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\GameSystemSummary.cs">
- <Link>Dto\GameSystemSummary.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\IHasServerId.cs">
- <Link>Dto\IHasServerId.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\IHasSyncInfo.cs">
- <Link>Dto\IHasSyncInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\IItemDto.cs">
- <Link>Dto\IItemDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\ImageByNameInfo.cs">
- <Link>Dto\ImageByNameInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\ImageInfo.cs">
- <Link>Dto\ImageInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\ImageOptions.cs">
- <Link>Dto\ImageOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\ItemCounts.cs">
- <Link>Dto\ItemCounts.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\ItemIndex.cs">
- <Link>Dto\ItemIndex.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\ItemLayout.cs">
- <Link>Dto\ItemLayout.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\MediaSourceInfo.cs">
- <Link>Dto\MediaSourceInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\MediaSourceType.cs">
- <Link>Dto\MediaSourceType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\MetadataEditorInfo.cs">
- <Link>Dto\MetadataEditorInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\NameIdPair.cs">
- <Link>Dto\NameIdPair.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\NameValuePair.cs">
- <Link>Dto\NameValuePair.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\RatingType.cs">
- <Link>Dto\RatingType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\RecommendationDto.cs">
- <Link>Dto\RecommendationDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\RecommendationType.cs">
- <Link>Dto\RecommendationType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\StudioDto.cs">
- <Link>Dto\StudioDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\SubtitleDownloadOptions.cs">
- <Link>Dto\SubtitleDownloadOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\UserDto.cs">
- <Link>Dto\UserDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\UserItemDataDto.cs">
- <Link>Dto\UserItemDataDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\BaseItemInfo.cs">
- <Link>Entities\BaseItemInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\ChapterInfo.cs">
- <Link>Entities\ChapterInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\CollectionType.cs">
- <Link>Entities\CollectionType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\DisplayPreferences.cs">
- <Link>Entities\DisplayPreferences.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\EmptyRequestResult.cs">
- <Link>Entities\EmptyRequestResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\ExtraType.cs">
- <Link>Entities\ExtraType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\IHasProviderIds.cs">
- <Link>Entities\IHasProviderIds.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\ImageType.cs">
- <Link>Entities\ImageType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\IsoType.cs">
- <Link>Entities\IsoType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\ItemReview.cs">
- <Link>Entities\ItemReview.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\LibraryUpdateInfo.cs">
- <Link>Entities\LibraryUpdateInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\LocationType.cs">
- <Link>Entities\LocationType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\MBRegistrationRecord.cs">
- <Link>Entities\MBRegistrationRecord.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\MediaStream.cs">
- <Link>Entities\MediaStream.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\MediaStreamType.cs">
- <Link>Entities\MediaStreamType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\MediaType.cs">
- <Link>Entities\MediaType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\MediaUrl.cs">
- <Link>Entities\MediaUrl.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\MetadataFields.cs">
- <Link>Entities\MetadataFields.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\MetadataProviders.cs">
- <Link>Entities\MetadataProviders.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\PackageReviewInfo.cs">
- <Link>Entities\PackageReviewInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\ParentalRating.cs">
- <Link>Entities\ParentalRating.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\PersonType.cs">
- <Link>Entities\PersonType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\PluginSecurityInfo.cs">
- <Link>Entities\PluginSecurityInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\ProviderIdsExtensions.cs">
- <Link>Entities\ProviderIdsExtensions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\ScrollDirection.cs">
- <Link>Entities\ScrollDirection.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\SeriesStatus.cs">
- <Link>Entities\SeriesStatus.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\SortOrder.cs">
- <Link>Entities\SortOrder.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\TrailerType.cs">
- <Link>Entities\TrailerType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\UserDataSaveReason.cs">
- <Link>Entities\UserDataSaveReason.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\Video3DFormat.cs">
- <Link>Entities\Video3DFormat.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\VideoType.cs">
- <Link>Entities\VideoType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\VirtualFolderInfo.cs">
- <Link>Entities\VirtualFolderInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Events\GenericEventArgs.cs">
- <Link>Events\GenericEventArgs.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Extensions\BoolHelper.cs">
- <Link>Extensions\BoolHelper.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Extensions\DoubleHelper.cs">
- <Link>Extensions\DoubleHelper.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Extensions\FloatHelper.cs">
- <Link>Extensions\FloatHelper.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Extensions\IntHelper.cs">
- <Link>Extensions\IntHelper.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Extensions\ListHelper.cs">
- <Link>Extensions\ListHelper.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Extensions\StringHelper.cs">
- <Link>Extensions\StringHelper.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\FileOrganization\AutoOrganizeOptions.cs">
- <Link>FileOrganization\AutoOrganizeOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\FileOrganization\EpisodeFileOrganizationRequest.cs">
- <Link>FileOrganization\EpisodeFileOrganizationRequest.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\FileOrganization\FileOrganizationResult.cs">
- <Link>FileOrganization\FileOrganizationResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\FileOrganization\FileOrganizationResultQuery.cs">
- <Link>FileOrganization\FileOrganizationResultQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\FileOrganization\FileOrganizerType.cs">
- <Link>FileOrganization\FileOrganizerType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\FileOrganization\FileSortingStatus.cs">
- <Link>FileOrganization\FileSortingStatus.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\FileOrganization\SmartMatchInfo.cs">
- <Link>FileOrganization\SmartMatchInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\FileOrganization\TvFileOrganizationOptions.cs">
- <Link>FileOrganization\TvFileOrganizationOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Globalization\CountryInfo.cs">
- <Link>Globalization\CountryInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Globalization\CultureDto.cs">
- <Link>Globalization\CultureDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Globalization\LocalizatonOption.cs">
- <Link>Globalization\LocalizatonOption.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\IO\FileSystemEntryInfo.cs">
- <Link>IO\FileSystemEntryInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\IO\FileSystemEntryType.cs">
- <Link>IO\FileSystemEntryType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\IO\IIsoManager.cs">
- <Link>IO\IIsoManager.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\IO\IIsoMount.cs">
- <Link>IO\IIsoMount.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\IO\IIsoMounter.cs">
- <Link>IO\IIsoMounter.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\IO\IZipClient.cs">
- <Link>IO\IZipClient.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Library\PlayAccess.cs">
- <Link>Library\PlayAccess.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\BaseTimerInfoDto.cs">
- <Link>LiveTv\BaseTimerInfoDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\ChannelInfoDto.cs">
- <Link>LiveTv\ChannelInfoDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\ChannelType.cs">
- <Link>LiveTv\ChannelType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\DayPattern.cs">
- <Link>LiveTv\DayPattern.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\GuideInfo.cs">
- <Link>LiveTv\GuideInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\LiveTvChannelQuery.cs">
- <Link>LiveTv\LiveTvChannelQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\LiveTvInfo.cs">
- <Link>LiveTv\LiveTvInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\LiveTvOptions.cs">
- <Link>LiveTv\LiveTvOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\LiveTvServiceInfo.cs">
- <Link>LiveTv\LiveTvServiceInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\LiveTvServiceStatus.cs">
- <Link>LiveTv\LiveTvServiceStatus.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\LiveTvTunerInfoDto.cs">
- <Link>LiveTv\LiveTvTunerInfoDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\LiveTvTunerStatus.cs">
- <Link>LiveTv\LiveTvTunerStatus.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\ProgramAudio.cs">
- <Link>LiveTv\ProgramAudio.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\ProgramQuery.cs">
- <Link>LiveTv\ProgramQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\RecommendedProgramQuery.cs">
- <Link>LiveTv\RecommendedProgramQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\RecordingGroupQuery.cs">
- <Link>LiveTv\RecordingGroupQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\RecordingQuery.cs">
- <Link>LiveTv\RecordingQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\RecordingStatus.cs">
- <Link>LiveTv\RecordingStatus.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\SeriesTimerInfoDto.cs">
- <Link>LiveTv\SeriesTimerInfoDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\SeriesTimerQuery.cs">
- <Link>LiveTv\SeriesTimerQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\TimerInfoDto.cs">
- <Link>LiveTv\TimerInfoDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\TimerQuery.cs">
- <Link>LiveTv\TimerQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Logging\ILogger.cs">
- <Link>Logging\ILogger.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Logging\ILogManager.cs">
- <Link>Logging\ILogManager.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Logging\LogSeverity.cs">
- <Link>Logging\LogSeverity.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Logging\NullLogger.cs">
- <Link>Logging\NullLogger.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\AudioCodec.cs">
- <Link>MediaInfo\AudioCodec.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\BlurayDiscInfo.cs">
- <Link>MediaInfo\BlurayDiscInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\Container.cs">
- <Link>MediaInfo\Container.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\IBlurayExaminer.cs">
- <Link>MediaInfo\IBlurayExaminer.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\LiveStreamRequest.cs">
- <Link>MediaInfo\LiveStreamRequest.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\LiveStreamResponse.cs">
- <Link>MediaInfo\LiveStreamResponse.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\MediaInfo.cs">
- <Link>MediaInfo\MediaInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\MediaProtocol.cs">
- <Link>MediaInfo\MediaProtocol.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\PlaybackInfoRequest.cs">
- <Link>MediaInfo\PlaybackInfoRequest.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\PlaybackInfoResponse.cs">
- <Link>MediaInfo\PlaybackInfoResponse.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\SubtitleFormat.cs">
- <Link>MediaInfo\SubtitleFormat.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\SubtitleTrackEvent.cs">
- <Link>MediaInfo\SubtitleTrackEvent.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\SubtitleTrackInfo.cs">
- <Link>MediaInfo\SubtitleTrackInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\TransportStreamTimestamp.cs">
- <Link>MediaInfo\TransportStreamTimestamp.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\VideoCodec.cs">
- <Link>MediaInfo\VideoCodec.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Net\EndPointInfo.cs">
- <Link>Net\EndPointInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Net\HttpException.cs">
- <Link>Net\HttpException.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Net\HttpResponse.cs">
- <Link>Net\HttpResponse.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Net\MimeTypes.cs">
- <Link>Net\MimeTypes.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Net\NetworkShare.cs">
- <Link>Net\NetworkShare.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Net\NetworkShareType.cs">
- <Link>Net\NetworkShareType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Net\WebSocketMessage.cs">
- <Link>Net\WebSocketMessage.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Net\WebSocketMessageType.cs">
- <Link>Net\WebSocketMessageType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Net\WebSocketState.cs">
- <Link>Net\WebSocketState.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\News\NewsItem.cs">
- <Link>News\NewsItem.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\News\NewsQuery.cs">
- <Link>News\NewsQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\Notification.cs">
- <Link>Notifications\Notification.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\NotificationLevel.cs">
- <Link>Notifications\NotificationLevel.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\NotificationOption.cs">
- <Link>Notifications\NotificationOption.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\NotificationOptions.cs">
- <Link>Notifications\NotificationOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\NotificationQuery.cs">
- <Link>Notifications\NotificationQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\NotificationRequest.cs">
- <Link>Notifications\NotificationRequest.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\NotificationResult.cs">
- <Link>Notifications\NotificationResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\NotificationServiceInfo.cs">
- <Link>Notifications\NotificationServiceInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\NotificationsSummary.cs">
- <Link>Notifications\NotificationsSummary.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\NotificationType.cs">
- <Link>Notifications\NotificationType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\NotificationTypeInfo.cs">
- <Link>Notifications\NotificationTypeInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\SendToUserType.cs">
- <Link>Notifications\SendToUserType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Playlists\PlaylistCreationRequest.cs">
- <Link>Playlists\PlaylistCreationRequest.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Playlists\PlaylistCreationResult.cs">
- <Link>Playlists\PlaylistCreationResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Playlists\PlaylistItemQuery.cs">
- <Link>Playlists\PlaylistItemQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Plugins\BasePluginConfiguration.cs">
- <Link>Plugins\BasePluginConfiguration.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Plugins\PluginInfo.cs">
- <Link>Plugins\PluginInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Providers\ExternalIdInfo.cs">
- <Link>Providers\ExternalIdInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Providers\ExternalUrl.cs">
- <Link>Providers\ExternalUrl.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Providers\ImageProviderInfo.cs">
- <Link>Providers\ImageProviderInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Providers\RemoteImageInfo.cs">
- <Link>Providers\RemoteImageInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Providers\RemoteImageQuery.cs">
- <Link>Providers\RemoteImageQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Providers\RemoteImageResult.cs">
- <Link>Providers\RemoteImageResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Providers\RemoteSearchResult.cs">
- <Link>Providers\RemoteSearchResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Providers\RemoteSubtitleInfo.cs">
- <Link>Providers\RemoteSubtitleInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Providers\SubtitleOptions.cs">
- <Link>Providers\SubtitleOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Providers\SubtitleProviderInfo.cs">
- <Link>Providers\SubtitleProviderInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\AllThemeMediaResult.cs">
- <Link>Querying\AllThemeMediaResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\ArtistsQuery.cs">
- <Link>Querying\ArtistsQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\EpisodeQuery.cs">
- <Link>Querying\EpisodeQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\ItemCountsQuery.cs">
- <Link>Querying\ItemCountsQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\ItemFields.cs">
- <Link>Querying\ItemFields.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\ItemFilter.cs">
- <Link>Querying\ItemFilter.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\ItemQuery.cs">
- <Link>Querying\ItemQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\ItemsByNameQuery.cs">
- <Link>Querying\ItemsByNameQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\ItemSortBy.cs">
- <Link>Querying\ItemSortBy.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\ItemsResult.cs">
- <Link>Querying\ItemsResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\LatestItemsQuery.cs">
- <Link>Querying\LatestItemsQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\MovieRecommendationQuery.cs">
- <Link>Querying\MovieRecommendationQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\NextUpQuery.cs">
- <Link>Querying\NextUpQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\PersonsQuery.cs">
- <Link>Querying\PersonsQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\QueryFilters.cs">
- <Link>Querying\QueryFilters.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\QueryResult.cs">
- <Link>Querying\QueryResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\SeasonQuery.cs">
- <Link>Querying\SeasonQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\SessionQuery.cs">
- <Link>Querying\SessionQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\SimilarItemsQuery.cs">
- <Link>Querying\SimilarItemsQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\ThemeMediaResult.cs">
- <Link>Querying\ThemeMediaResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\UpcomingEpisodesQuery.cs">
- <Link>Querying\UpcomingEpisodesQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\UserQuery.cs">
- <Link>Querying\UserQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Registration\RegistrationInfo.cs">
- <Link>Registration\RegistrationInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Search\SearchHint.cs">
- <Link>Search\SearchHint.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Search\SearchHintResult.cs">
- <Link>Search\SearchHintResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Search\SearchQuery.cs">
- <Link>Search\SearchQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Serialization\IJsonSerializer.cs">
- <Link>Serialization\IJsonSerializer.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Serialization\IXmlSerializer.cs">
- <Link>Serialization\IXmlSerializer.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\BrowseRequest.cs">
- <Link>Session\BrowseRequest.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\ClientCapabilities.cs">
- <Link>Session\ClientCapabilities.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\GeneralCommand.cs">
- <Link>Session\GeneralCommand.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\GeneralCommandType.cs">
- <Link>Session\GeneralCommandType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\MessageCommand.cs">
- <Link>Session\MessageCommand.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\PlaybackProgressInfo.cs">
- <Link>Session\PlaybackProgressInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\PlaybackStartInfo.cs">
- <Link>Session\PlaybackStartInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\PlaybackStopInfo.cs">
- <Link>Session\PlaybackStopInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\PlayCommand.cs">
- <Link>Session\PlayCommand.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\PlayerStateInfo.cs">
- <Link>Session\PlayerStateInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\PlayMethod.cs">
- <Link>Session\PlayMethod.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\PlayRequest.cs">
- <Link>Session\PlayRequest.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\PlaystateCommand.cs">
- <Link>Session\PlaystateCommand.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\PlaystateRequest.cs">
- <Link>Session\PlaystateRequest.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\SessionInfoDto.cs">
- <Link>Session\SessionInfoDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\SessionUserInfo.cs">
- <Link>Session\SessionUserInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\TranscodingInfo.cs">
- <Link>Session\TranscodingInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\UserDataChangeInfo.cs">
- <Link>Session\UserDataChangeInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Social\SocialShareInfo.cs">
- <Link>Social\SocialShareInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\CompleteSyncJobInfo.cs">
- <Link>Sync\CompleteSyncJobInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\DeviceFileInfo.cs">
- <Link>Sync\DeviceFileInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\ItemFIleInfo.cs">
- <Link>Sync\ItemFIleInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\ItemFileType.cs">
- <Link>Sync\ItemFileType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\LocalItem.cs">
- <Link>Sync\LocalItem.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\LocalItemInfo.cs">
- <Link>Sync\LocalItemInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\LocalItemQuery.cs">
- <Link>Sync\LocalItemQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncCategory.cs">
- <Link>Sync\SyncCategory.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncDataRequest.cs">
- <Link>Sync\SyncDataRequest.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncDataResponse.cs">
- <Link>Sync\SyncDataResponse.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncDialogOptions.cs">
- <Link>Sync\SyncDialogOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncedItem.cs">
- <Link>Sync\SyncedItem.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncJob.cs">
- <Link>Sync\SyncJob.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncJobCreationResult.cs">
- <Link>Sync\SyncJobCreationResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncJobItem.cs">
- <Link>Sync\SyncJobItem.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncJobItemQuery.cs">
- <Link>SyncJobItemQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncJobItemStatus.cs">
- <Link>Sync\SyncJobItemStatus.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncJobQuery.cs">
- <Link>Sync\SyncJobQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncJobRequest.cs">
- <Link>Sync\SyncJobRequest.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncJobStatus.cs">
- <Link>Sync\SyncJobStatus.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncOptions.cs">
- <Link>Sync\SyncOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncParameter.cs">
- <Link>Sync\SyncParameter.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncProfileOption.cs">
- <Link>Sync\SyncProfileOption.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncQualityOption.cs">
- <Link>Sync\SyncQualityOption.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncTarget.cs">
- <Link>Sync\SyncTarget.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\System\Architecture.cs">
- <Link>System\Architecture.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\System\LogFile.cs">
- <Link>System\LogFile.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\System\PublicSystemInfo.cs">
- <Link>System\PublicSystemInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\System\SystemInfo.cs">
- <Link>System\SystemInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Tasks\SystemEvent.cs">
- <Link>Tasks\SystemEvent.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Tasks\TaskCompletionStatus.cs">
- <Link>Tasks\TaskCompletionStatus.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Tasks\TaskInfo.cs">
- <Link>Tasks\TaskInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Tasks\TaskResult.cs">
- <Link>Tasks\TaskResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Tasks\TaskState.cs">
- <Link>Tasks\TaskState.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Tasks\TaskTriggerInfo.cs">
- <Link>Tasks\TaskTriggerInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Updates\CheckForUpdateResult.cs">
- <Link>Updates\CheckForUpdateResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Updates\InstallationInfo.cs">
- <Link>Updates\InstallationInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Updates\PackageInfo.cs">
- <Link>Updates\PackageInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Updates\PackageTargetSystem.cs">
- <Link>Updates\PackageTargetSystem.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Updates\PackageVersionClass.cs">
- <Link>Updates\PackageVersionClass.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Updates\PackageVersionInfo.cs">
- <Link>Updates\PackageVersionInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Users\AuthenticationResult.cs">
- <Link>Users\AuthenticationResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Users\ForgotPasswordAction.cs">
- <Link>Users\ForgotPasswordAction.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Users\ForgotPasswordResult.cs">
- <Link>Users\ForgotPasswordResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Users\PinRedeemResult.cs">
- <Link>Users\PinRedeemResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Users\UserAction.cs">
- <Link>Users\UserAction.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Users\UserActionType.cs">
- <Link>Users\UserActionType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Users\UserPolicy.cs">
- <Link>Users\UserPolicy.cs</Link>
- </Compile>
- <Compile Include="..\SharedVersion.cs">
- <Link>Properties\SharedVersion.cs</Link>
- </Compile>
- <Compile Include="Properties\AssemblyInfo.cs" />
- </ItemGroup>
- <ItemGroup>
- <Content Include="FodyWeavers.xml" />
- </ItemGroup>
- <ItemGroup>
- <BootstrapperPackage Include="Microsoft.Net.Client.3.5">
- <Visible>False</Visible>
- <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
- <Install>false</Install>
- </BootstrapperPackage>
- <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
- <Visible>False</Visible>
- <ProductName>.NET Framework 3.5 SP1</ProductName>
- <Install>false</Install>
- </BootstrapperPackage>
- </ItemGroup>
- <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.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.Model.Portable/app.config b/MediaBrowser.Model.Portable/app.config
deleted file mode 100644
index 3c73782929..0000000000
--- a/MediaBrowser.Model.Portable/app.config
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<configuration>
- <runtime>
- <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
- <dependentAssembly>
- <assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
- <bindingRedirect oldVersion="0.0.0.0-2.6.8.0" newVersion="2.6.8.0" />
- </dependentAssembly>
- <dependentAssembly>
- <assemblyIdentity name="System.Threading.Tasks" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
- <bindingRedirect oldVersion="0.0.0.0-2.6.8.0" newVersion="2.6.8.0" />
- </dependentAssembly>
- </assemblyBinding>
- </runtime>
-</configuration> \ No newline at end of file
diff --git a/MediaBrowser.Model.net35/FodyWeavers.xml b/MediaBrowser.Model.net35/FodyWeavers.xml
deleted file mode 100644
index 6e2fa02e64..0000000000
--- a/MediaBrowser.Model.net35/FodyWeavers.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Weavers>
-</Weavers> \ No newline at end of file
diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
deleted file mode 100644
index 3ad40e2f5d..0000000000
--- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
+++ /dev/null
@@ -1,1181 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.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>{657B5410-7C3B-4806-9753-D254102CE537}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>MediaBrowser.Model</RootNamespace>
- <AssemblyName>MediaBrowser.Model</AssemblyName>
- <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
- <FileAlignment>512</FileAlignment>
- <TargetFrameworkProfile />
- <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
- <FodyPath>..\packages\Fody.1.17.0.0</FodyPath>
- </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>
- <SignAssembly>true</SignAssembly>
- </PropertyGroup>
- <PropertyGroup>
- <AssemblyOriginatorKeyFile>MediaBrowser.Model.snk</AssemblyOriginatorKeyFile>
- </PropertyGroup>
- <PropertyGroup>
- <RunPostBuildEvent>Always</RunPostBuildEvent>
- </PropertyGroup>
- <ItemGroup>
- <Reference Include="System" />
- <Reference Include="System.Core" />
- <Reference Include="System.Runtime.Serialization" />
- <Reference Include="System.Xml.Linq" />
- <Reference Include="System.Data.DataSetExtensions" />
- <Reference Include="System.Data" />
- <Reference Include="System.Xml" />
- </ItemGroup>
- <ItemGroup>
- <Compile Include="..\MediaBrowser.Model\Activity\ActivityLogEntry.cs">
- <Link>Activity\ActivityLogEntry.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\ApiHelpers.cs">
- <Link>ApiClient\ApiHelpers.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\ConnectionMode.cs">
- <Link>ApiClient\ConnectionMode.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\ConnectionOptions.cs">
- <Link>ApiClient\ConnectionOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\ConnectionState.cs">
- <Link>ApiClient\ConnectionState.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\ConnectSignupResponse.cs">
- <Link>ApiClient\ConnectSignupResponse.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\GeneralCommandEventArgs.cs">
- <Link>ApiClient\GeneralCommandEventArgs.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\HttpResponseEventArgs.cs">
- <Link>ApiClient\HttpResponseEventArgs.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\IServerEvents.cs">
- <Link>ApiClient\IServerEvents.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\NetworkStatus.cs">
- <Link>ApiClient\NetworkStatus.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\RemoteLogoutReason.cs">
- <Link>ApiClient\RemoteLogoutReason.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\ServerCredentials.cs">
- <Link>ApiClient\ServerCredentials.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\ServerDiscoveryInfo.cs">
- <Link>ApiClient\ServerDiscoveryInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\ServerInfo.cs">
- <Link>ApiClient\ServerInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\ServerUserInfo.cs">
- <Link>ApiClient\ServerUserInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\SessionUpdatesEventArgs.cs">
- <Link>ApiClient\SessionUpdatesEventArgs.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\ApiClient\WakeOnLanInfo.cs">
- <Link>ApiClient\WakeOnLanInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Branding\BrandingOptions.cs">
- <Link>Branding\BrandingOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Channels\AllChannelMediaQuery.cs">
- <Link>Channels\AllChannelMediaQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Channels\ChannelFeatures.cs">
- <Link>Channels\ChannelFeatures.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Channels\ChannelFolderType.cs">
- <Link>Channels\ChannelFolderType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Channels\ChannelInfo.cs">
- <Link>Channels\ChannelInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Channels\ChannelItemQuery.cs">
- <Link>Channels\ChannelItemQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Channels\ChannelItemSortField.cs">
- <Link>Channels\ChannelItemSortField.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Channels\ChannelMediaContentType.cs">
- <Link>Channels\ChannelMediaContentType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Channels\ChannelMediaType.cs">
- <Link>Channels\ChannelMediaType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Channels\ChannelQuery.cs">
- <Link>Channels\ChannelQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Collections\CollectionCreationResult.cs">
- <Link>Collections\CollectionCreationResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\AccessSchedule.cs">
- <Link>Configuration\AccessSchedule.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\BaseApplicationConfiguration.cs">
- <Link>Configuration\BaseApplicationConfiguration.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\ChannelOptions.cs">
- <Link>Configuration\ChannelOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\ChapterOptions.cs">
- <Link>Configuration\ChapterOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\DlnaOptions.cs">
- <Link>Configuration\DlnaOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\DynamicDayOfWeek.cs">
- <Link>Configuration\DynamicDayOfWeek.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\EncodingOptions.cs">
- <Link>Configuration\EncodingOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\FanartOptions.cs">
- <Link>Configuration\FanartOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\ImageOption.cs">
- <Link>Configuration\ImageOption.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\ImageSavingConvention.cs">
- <Link>Configuration\ImageSavingConvention.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\LibraryOptions.cs">
- <Link>Configuration\LibraryOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\MetadataConfiguration.cs">
- <Link>Configuration\MetadataConfiguration.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\MetadataOptions.cs">
- <Link>Configuration\MetadataOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\MetadataPlugin.cs">
- <Link>Configuration\MetadataPlugin.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\MetadataPluginSummary.cs">
- <Link>Configuration\MetadataPluginSummary.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\MetadataPluginType.cs">
- <Link>Configuration\MetadataPluginType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\PathSubstitution.cs">
- <Link>Configuration\PathSubstitution.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\PeopleMetadataOptions.cs">
- <Link>Configuration\PeopleMetadataOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\ServerConfiguration.cs">
- <Link>Configuration\ServerConfiguration.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\SubtitlePlaybackMode.cs">
- <Link>Configuration\SubtitlePlaybackMode.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\UnratedItem.cs">
- <Link>Configuration\UnratedItem.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\UserConfiguration.cs">
- <Link>Configuration\UserConfiguration.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Configuration\XbmcMetadataOptions.cs">
- <Link>Configuration\XbmcMetadataOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\ConnectAuthenticationExchangeResult.cs">
- <Link>Connect\ConnectAuthenticationExchangeResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\ConnectAuthenticationResult.cs">
- <Link>Connect\ConnectAuthenticationResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\ConnectAuthorization.cs">
- <Link>Connect\ConnectAuthorization.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\ConnectAuthorizationRequest.cs">
- <Link>Connect\ConnectAuthorizationRequest.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\ConnectPassword.cs">
- <Link>Connect\ConnectPassword.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\ConnectUser.cs">
- <Link>Connect\ConnectUser.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\ConnectUserQuery.cs">
- <Link>Connect\ConnectUserQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\ConnectUserServer.cs">
- <Link>Connect\ConnectUserServer.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\PinCreationResult.cs">
- <Link>Connect\PinCreationResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\PinExchangeResult.cs">
- <Link>Connect\PinExchangeResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\PinStatusResult.cs">
- <Link>Connect\PinStatusResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Connect\UserLinkType.cs">
- <Link>Connect\UserLinkType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Devices\ContentUploadHistory.cs">
- <Link>Devices\ContentUploadHistory.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Devices\DeviceInfo.cs">
- <Link>Devices\DeviceInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Devices\DeviceOptions.cs">
- <Link>Devices\DeviceOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Devices\DeviceQuery.cs">
- <Link>Devices\DeviceQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Devices\DevicesOptions.cs">
- <Link>Devices\DevicesOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Devices\LocalFileInfo.cs">
- <Link>Devices\LocalFileInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\AudioOptions.cs">
- <Link>Dlna\AudioOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\CodecProfile.cs">
- <Link>Dlna\CodecProfile.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\CodecType.cs">
- <Link>Dlna\CodecType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\ConditionProcessor.cs">
- <Link>Dlna\ConditionProcessor.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\ContainerProfile.cs">
- <Link>Dlna\ContainerProfile.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\ContentFeatureBuilder.cs">
- <Link>Dlna\ContentFeatureBuilder.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\DeviceIdentification.cs">
- <Link>Dlna\DeviceIdentification.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\DeviceProfile.cs">
- <Link>Dlna\DeviceProfile.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\DeviceProfileInfo.cs">
- <Link>Dlna\DeviceProfileInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\DeviceProfileType.cs">
- <Link>Dlna\DeviceProfileType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\DirectPlayProfile.cs">
- <Link>Dlna\DirectPlayProfile.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\DlnaFlags.cs">
- <Link>Dlna\DlnaFlags.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\DlnaMaps.cs">
- <Link>Dlna\DlnaMaps.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\DlnaProfileType.cs">
- <Link>Dlna\DlnaProfileType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\EncodingContext.cs">
- <Link>Dlna\EncodingContext.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\HeaderMatchType.cs">
- <Link>Dlna\HeaderMatchType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\HttpHeaderInfo.cs">
- <Link>Dlna\HttpHeaderInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\ITranscoderSupport.cs">
- <Link>Dlna\ITranscoderSupport.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\MediaFormatProfile.cs">
- <Link>Dlna\MediaFormatProfile.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\MediaFormatProfileResolver.cs">
- <Link>Dlna\MediaFormatProfileResolver.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\PlaybackErrorCode.cs">
- <Link>Dlna\PlaybackErrorCode.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\PlaybackException.cs">
- <Link>Dlna\PlaybackException.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\ProfileCondition.cs">
- <Link>Dlna\ProfileCondition.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\ProfileConditionType.cs">
- <Link>Dlna\ProfileConditionType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\ProfileConditionValue.cs">
- <Link>Dlna\ProfileConditionValue.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\ResolutionConfiguration.cs">
- <Link>Dlna\ResolutionConfiguration.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\ResolutionNormalizer.cs">
- <Link>Dlna\ResolutionNormalizer.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\ResolutionOptions.cs">
- <Link>Dlna\ResolutionOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\ResponseProfile.cs">
- <Link>Dlna\ResponseProfile.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\SearchCriteria.cs">
- <Link>Dlna\SearchCriteria.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\SearchType.cs">
- <Link>Dlna\SearchType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\SortCriteria.cs">
- <Link>Dlna\SortCriteria.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\StreamBuilder.cs">
- <Link>Dlna\StreamBuilder.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\StreamInfo.cs">
- <Link>Dlna\StreamInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\StreamInfoSorter.cs">
- <Link>Dlna\StreamInfoSorter.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\SubtitleDeliveryMethod.cs">
- <Link>Dlna\SubtitleDeliveryMethod.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\SubtitleProfile.cs">
- <Link>Dlna\SubtitleProfile.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\SubtitleStreamInfo.cs">
- <Link>Dlna\SubtitleStreamInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\TranscodeSeekInfo.cs">
- <Link>Dlna\TranscodeSeekInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\TranscodingProfile.cs">
- <Link>Dlna\TranscodingProfile.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\VideoOptions.cs">
- <Link>Dlna\VideoOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dlna\XmlAttribute.cs">
- <Link>Dlna\XmlAttribute.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Drawing\DrawingUtils.cs">
- <Link>Drawing\DrawingUtils.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Drawing\ImageFormat.cs">
- <Link>Drawing\ImageFormat.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Drawing\ImageOrientation.cs">
- <Link>Drawing\ImageOrientation.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Drawing\ImageSize.cs">
- <Link>Drawing\ImageSize.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\BaseItemDto.cs">
- <Link>Dto\BaseItemDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\BaseItemPerson.cs">
- <Link>Dto\BaseItemPerson.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\ChapterInfoDto.cs">
- <Link>Dto\ChapterInfoDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\GameSystemSummary.cs">
- <Link>Dto\GameSystemSummary.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\IHasServerId.cs">
- <Link>Dto\IHasServerId.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\IHasSyncInfo.cs">
- <Link>Dto\IHasSyncInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\IItemDto.cs">
- <Link>Dto\IItemDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\ImageByNameInfo.cs">
- <Link>Dto\ImageByNameInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\ImageInfo.cs">
- <Link>Dto\ImageInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\ImageOptions.cs">
- <Link>Dto\ImageOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\ItemCounts.cs">
- <Link>Dto\ItemCounts.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\ItemIndex.cs">
- <Link>Dto\ItemIndex.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\ItemLayout.cs">
- <Link>Dto\ItemLayout.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\MediaSourceInfo.cs">
- <Link>Dto\MediaSourceInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\MediaSourceType.cs">
- <Link>Dto\MediaSourceType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\MetadataEditorInfo.cs">
- <Link>Dto\MetadataEditorInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\NameIdPair.cs">
- <Link>Dto\NameIdPair.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\NameValuePair.cs">
- <Link>Dto\NameValuePair.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\RatingType.cs">
- <Link>Dto\RatingType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\RecommendationDto.cs">
- <Link>Dto\RecommendationDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\RecommendationType.cs">
- <Link>Dto\RecommendationType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\StudioDto.cs">
- <Link>Dto\StudioDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\SubtitleDownloadOptions.cs">
- <Link>Dto\SubtitleDownloadOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\UserDto.cs">
- <Link>Dto\UserDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Dto\UserItemDataDto.cs">
- <Link>Dto\UserItemDataDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\BaseItemInfo.cs">
- <Link>Entities\BaseItemInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\ChapterInfo.cs">
- <Link>Entities\ChapterInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\CollectionType.cs">
- <Link>Entities\CollectionType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\DisplayPreferences.cs">
- <Link>Entities\DisplayPreferences.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\EmptyRequestResult.cs">
- <Link>Entities\EmptyRequestResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\ExtraType.cs">
- <Link>Entities\ExtraType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\IHasProviderIds.cs">
- <Link>Entities\IHasProviderIds.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\ImageType.cs">
- <Link>Entities\ImageType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\IsoType.cs">
- <Link>Entities\IsoType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\ItemReview.cs">
- <Link>Entities\ItemReview.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\LibraryUpdateInfo.cs">
- <Link>Entities\LibraryUpdateInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\LocationType.cs">
- <Link>Entities\LocationType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\MBRegistrationRecord.cs">
- <Link>Entities\MBRegistrationRecord.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\MediaStream.cs">
- <Link>Entities\MediaStream.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\MediaStreamType.cs">
- <Link>Entities\MediaStreamType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\MediaType.cs">
- <Link>Entities\MediaType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\MediaUrl.cs">
- <Link>Entities\MediaUrl.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\MetadataFields.cs">
- <Link>Entities\MetadataFields.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\MetadataProviders.cs">
- <Link>Entities\MetadataProviders.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\PackageReviewInfo.cs">
- <Link>Entities\PackageReviewInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\ParentalRating.cs">
- <Link>Entities\ParentalRating.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\PersonType.cs">
- <Link>Entities\PersonType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\PluginSecurityInfo.cs">
- <Link>Entities\PluginSecurityInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\ProviderIdsExtensions.cs">
- <Link>Entities\ProviderIdsExtensions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\ScrollDirection.cs">
- <Link>Entities\ScrollDirection.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\SeriesStatus.cs">
- <Link>Entities\SeriesStatus.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\SortOrder.cs">
- <Link>Entities\SortOrder.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\TrailerType.cs">
- <Link>Entities\TrailerType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\UserDataSaveReason.cs">
- <Link>Entities\UserDataSaveReason.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\Video3DFormat.cs">
- <Link>Entities\Video3DFormat.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\VideoType.cs">
- <Link>Entities\VideoType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Entities\VirtualFolderInfo.cs">
- <Link>Entities\VirtualFolderInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Events\GenericEventArgs.cs">
- <Link>Events\GenericEventArgs.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Extensions\BoolHelper.cs">
- <Link>Extensions\BoolHelper.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Extensions\DoubleHelper.cs">
- <Link>Extensions\DoubleHelper.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Extensions\FloatHelper.cs">
- <Link>Extensions\FloatHelper.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Extensions\IntHelper.cs">
- <Link>Extensions\IntHelper.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Extensions\ListHelper.cs">
- <Link>Extensions\ListHelper.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Extensions\StringHelper.cs">
- <Link>Extensions\StringHelper.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\FileOrganization\AutoOrganizeOptions.cs">
- <Link>FileOrganization\AutoOrganizeOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\FileOrganization\EpisodeFileOrganizationRequest.cs">
- <Link>FileOrganization\EpisodeFileOrganizationRequest.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\FileOrganization\FileOrganizationResult.cs">
- <Link>FileOrganization\FileOrganizationResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\FileOrganization\FileOrganizationResultQuery.cs">
- <Link>FileOrganization\FileOrganizationResultQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\FileOrganization\FileOrganizerType.cs">
- <Link>FileOrganization\FileOrganizerType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\FileOrganization\FileSortingStatus.cs">
- <Link>FileOrganization\FileSortingStatus.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\FileOrganization\SmartMatchInfo.cs">
- <Link>FileOrganization\SmartMatchInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\FileOrganization\TvFileOrganizationOptions.cs">
- <Link>FileOrganization\TvFileOrganizationOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Globalization\CountryInfo.cs">
- <Link>Globalization\CountryInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Globalization\CultureDto.cs">
- <Link>Globalization\CultureDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Globalization\LocalizatonOption.cs">
- <Link>Globalization\LocalizatonOption.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\IO\FileSystemEntryInfo.cs">
- <Link>IO\FileSystemEntryInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\IO\FileSystemEntryType.cs">
- <Link>IO\FileSystemEntryType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\IO\IIsoMount.cs">
- <Link>IO\IIsoMount.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Library\PlayAccess.cs">
- <Link>Library\PlayAccess.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\BaseTimerInfoDto.cs">
- <Link>LiveTv\BaseTimerInfoDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\ChannelInfoDto.cs">
- <Link>LiveTv\ChannelInfoDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\ChannelType.cs">
- <Link>LiveTv\ChannelType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\DayPattern.cs">
- <Link>LiveTv\DayPattern.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\GuideInfo.cs">
- <Link>LiveTv\GuideInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\LiveTvChannelQuery.cs">
- <Link>LiveTv\LiveTvChannelQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\LiveTvInfo.cs">
- <Link>LiveTv\LiveTvInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\LiveTvOptions.cs">
- <Link>LiveTv\LiveTvOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\LiveTvServiceInfo.cs">
- <Link>LiveTv\LiveTvServiceInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\LiveTvServiceStatus.cs">
- <Link>LiveTv\LiveTvServiceStatus.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\LiveTvTunerInfoDto.cs">
- <Link>LiveTv\LiveTvTunerInfoDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\LiveTvTunerStatus.cs">
- <Link>LiveTv\LiveTvTunerStatus.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\ProgramAudio.cs">
- <Link>LiveTv\ProgramAudio.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\ProgramQuery.cs">
- <Link>LiveTv\ProgramQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\RecommendedProgramQuery.cs">
- <Link>LiveTv\RecommendedProgramQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\RecordingGroupQuery.cs">
- <Link>LiveTv\RecordingGroupQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\RecordingQuery.cs">
- <Link>LiveTv\RecordingQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\RecordingStatus.cs">
- <Link>LiveTv\RecordingStatus.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\SeriesTimerInfoDto.cs">
- <Link>LiveTv\SeriesTimerInfoDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\SeriesTimerQuery.cs">
- <Link>LiveTv\SeriesTimerQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\TimerInfoDto.cs">
- <Link>LiveTv\TimerInfoDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\LiveTv\TimerQuery.cs">
- <Link>LiveTv\TimerQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Logging\ILogger.cs">
- <Link>Logging\ILogger.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Logging\ILogManager.cs">
- <Link>Logging\ILogManager.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Logging\LogSeverity.cs">
- <Link>Logging\LogSeverity.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Logging\NullLogger.cs">
- <Link>Logging\NullLogger.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\AudioCodec.cs">
- <Link>MediaInfo\AudioCodec.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\BlurayDiscInfo.cs">
- <Link>MediaInfo\BlurayDiscInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\Container.cs">
- <Link>MediaInfo\Container.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\IBlurayExaminer.cs">
- <Link>MediaInfo\IBlurayExaminer.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\LiveStreamRequest.cs">
- <Link>MediaInfo\LiveStreamRequest.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\LiveStreamResponse.cs">
- <Link>MediaInfo\LiveStreamResponse.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\MediaInfo.cs">
- <Link>MediaInfo\MediaInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\MediaProtocol.cs">
- <Link>MediaInfo\MediaProtocol.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\PlaybackInfoRequest.cs">
- <Link>MediaInfo\PlaybackInfoRequest.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\PlaybackInfoResponse.cs">
- <Link>MediaInfo\PlaybackInfoResponse.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\SubtitleFormat.cs">
- <Link>MediaInfo\SubtitleFormat.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\SubtitleTrackEvent.cs">
- <Link>MediaInfo\SubtitleTrackEvent.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\SubtitleTrackInfo.cs">
- <Link>MediaInfo\SubtitleTrackInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\TransportStreamTimestamp.cs">
- <Link>MediaInfo\TransportStreamTimestamp.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\MediaInfo\VideoCodec.cs">
- <Link>MediaInfo\VideoCodec.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Net\EndPointInfo.cs">
- <Link>Net\EndPointInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Net\HttpException.cs">
- <Link>Net\HttpException.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Net\HttpResponse.cs">
- <Link>Net\HttpResponse.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Net\MimeTypes.cs">
- <Link>Net\MimeTypes.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Net\NetworkShare.cs">
- <Link>Net\NetworkShare.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Net\NetworkShareType.cs">
- <Link>Net\NetworkShareType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Net\WebSocketMessage.cs">
- <Link>Net\WebSocketMessage.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Net\WebSocketMessageType.cs">
- <Link>Net\WebSocketMessageType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Net\WebSocketState.cs">
- <Link>Net\WebSocketState.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\News\NewsItem.cs">
- <Link>News\NewsItem.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\News\NewsQuery.cs">
- <Link>News\NewsQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\Notification.cs">
- <Link>Notifications\Notification.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\NotificationLevel.cs">
- <Link>Notifications\NotificationLevel.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\NotificationOption.cs">
- <Link>Notifications\NotificationOption.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\NotificationOptions.cs">
- <Link>Notifications\NotificationOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\NotificationQuery.cs">
- <Link>Notifications\NotificationQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\NotificationRequest.cs">
- <Link>Notifications\NotificationRequest.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\NotificationResult.cs">
- <Link>Notifications\NotificationResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\NotificationServiceInfo.cs">
- <Link>Notifications\NotificationServiceInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\NotificationsSummary.cs">
- <Link>Notifications\NotificationsSummary.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\NotificationType.cs">
- <Link>Notifications\NotificationType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\NotificationTypeInfo.cs">
- <Link>Notifications\NotificationTypeInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Notifications\SendToUserType.cs">
- <Link>Notifications\SendToUserType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Playlists\PlaylistCreationRequest.cs">
- <Link>Playlists\PlaylistCreationRequest.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Playlists\PlaylistCreationResult.cs">
- <Link>Playlists\PlaylistCreationResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Playlists\PlaylistItemQuery.cs">
- <Link>Playlists\PlaylistItemQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Plugins\BasePluginConfiguration.cs">
- <Link>Plugins\BasePluginConfiguration.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Plugins\PluginInfo.cs">
- <Link>Plugins\PluginInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Providers\ExternalIdInfo.cs">
- <Link>Providers\ExternalIdInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Providers\ExternalUrl.cs">
- <Link>Providers\ExternalUrl.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Providers\ImageProviderInfo.cs">
- <Link>Providers\ImageProviderInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Providers\RemoteImageInfo.cs">
- <Link>Providers\RemoteImageInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Providers\RemoteImageQuery.cs">
- <Link>Providers\RemoteImageQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Providers\RemoteImageResult.cs">
- <Link>Providers\RemoteImageResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Providers\RemoteSearchResult.cs">
- <Link>Providers\RemoteSearchResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Providers\RemoteSubtitleInfo.cs">
- <Link>Providers\RemoteSubtitleInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Providers\SubtitleOptions.cs">
- <Link>Providers\SubtitleOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Providers\SubtitleProviderInfo.cs">
- <Link>Providers\SubtitleProviderInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\AllThemeMediaResult.cs">
- <Link>Querying\AllThemeMediaResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\ArtistsQuery.cs">
- <Link>Querying\ArtistsQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\EpisodeQuery.cs">
- <Link>Querying\EpisodeQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\ItemCountsQuery.cs">
- <Link>Querying\ItemCountsQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\ItemFields.cs">
- <Link>Querying\ItemFields.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\ItemFilter.cs">
- <Link>Querying\ItemFilter.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\ItemQuery.cs">
- <Link>Querying\ItemQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\ItemsByNameQuery.cs">
- <Link>Querying\ItemsByNameQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\ItemSortBy.cs">
- <Link>Querying\ItemSortBy.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\ItemsResult.cs">
- <Link>Querying\ItemsResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\LatestItemsQuery.cs">
- <Link>Querying\LatestItemsQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\MovieRecommendationQuery.cs">
- <Link>Querying\MovieRecommendationQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\NextUpQuery.cs">
- <Link>Querying\NextUpQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\PersonsQuery.cs">
- <Link>Querying\PersonsQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\QueryFilters.cs">
- <Link>Querying\QueryFilters.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\QueryResult.cs">
- <Link>Querying\QueryResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\SeasonQuery.cs">
- <Link>Querying\SeasonQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\SessionQuery.cs">
- <Link>Querying\SessionQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\SimilarItemsQuery.cs">
- <Link>Querying\SimilarItemsQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\ThemeMediaResult.cs">
- <Link>Querying\ThemeMediaResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\UpcomingEpisodesQuery.cs">
- <Link>Querying\UpcomingEpisodesQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Querying\UserQuery.cs">
- <Link>Querying\UserQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Registration\RegistrationInfo.cs">
- <Link>Registration\RegistrationInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Search\SearchHint.cs">
- <Link>Search\SearchHint.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Search\SearchHintResult.cs">
- <Link>Search\SearchHintResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Search\SearchQuery.cs">
- <Link>Search\SearchQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Serialization\IJsonSerializer.cs">
- <Link>Serialization\IJsonSerializer.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Serialization\IXmlSerializer.cs">
- <Link>Serialization\IXmlSerializer.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\BrowseRequest.cs">
- <Link>Session\BrowseRequest.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\ClientCapabilities.cs">
- <Link>Session\ClientCapabilities.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\GeneralCommand.cs">
- <Link>Session\GeneralCommand.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\GeneralCommandType.cs">
- <Link>Session\GeneralCommandType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\MessageCommand.cs">
- <Link>Session\MessageCommand.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\PlaybackProgressInfo.cs">
- <Link>Session\PlaybackProgressInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\PlaybackStartInfo.cs">
- <Link>Session\PlaybackStartInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\PlaybackStopInfo.cs">
- <Link>Session\PlaybackStopInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\PlayCommand.cs">
- <Link>Session\PlayCommand.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\PlayerStateInfo.cs">
- <Link>Session\PlayerStateInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\PlayMethod.cs">
- <Link>Session\PlayMethod.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\PlayRequest.cs">
- <Link>Session\PlayRequest.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\PlaystateCommand.cs">
- <Link>Session\PlaystateCommand.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\PlaystateRequest.cs">
- <Link>Session\PlaystateRequest.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\SessionInfoDto.cs">
- <Link>Session\SessionInfoDto.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\SessionUserInfo.cs">
- <Link>Session\SessionUserInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\TranscodingInfo.cs">
- <Link>Session\TranscodingInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Session\UserDataChangeInfo.cs">
- <Link>Session\UserDataChangeInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Social\SocialShareInfo.cs">
- <Link>Social\SocialShareInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\CompleteSyncJobInfo.cs">
- <Link>Sync\CompleteSyncJobInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\DeviceFileInfo.cs">
- <Link>Sync\DeviceFileInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\ItemFIleInfo.cs">
- <Link>Sync\ItemFIleInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\ItemFileType.cs">
- <Link>Sync\ItemFileType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\LocalItem.cs">
- <Link>Sync\LocalItem.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\LocalItemInfo.cs">
- <Link>Sync\LocalItemInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\LocalItemQuery.cs">
- <Link>Sync\LocalItemQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncCategory.cs">
- <Link>Sync\SyncCategory.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncDataRequest.cs">
- <Link>Sync\SyncDataRequest.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncDataResponse.cs">
- <Link>Sync\SyncDataResponse.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncDialogOptions.cs">
- <Link>Sync\SyncDialogOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncedItem.cs">
- <Link>Sync\SyncedItem.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncJob.cs">
- <Link>Sync\SyncJob.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncJobCreationResult.cs">
- <Link>Sync\SyncJobCreationResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncJobItem.cs">
- <Link>Sync\SyncJobItem.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncJobItemQuery.cs">
- <Link>Sync\SyncJobItemQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncJobItemStatus.cs">
- <Link>Sync\SyncJobItemStatus.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncJobQuery.cs">
- <Link>Sync\SyncJobQuery.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncJobRequest.cs">
- <Link>Sync\SyncJobRequest.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncJobStatus.cs">
- <Link>Sync\SyncJobStatus.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncOptions.cs">
- <Link>Sync\SyncOptions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncParameter.cs">
- <Link>Sync\SyncParameter.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncProfileOption.cs">
- <Link>Sync\SyncProfileOption.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncQualityOption.cs">
- <Link>Sync\SyncQualityOption.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Sync\SyncTarget.cs">
- <Link>Sync\SyncTarget.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\System\Architecture.cs">
- <Link>System\Architecture.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\System\LogFile.cs">
- <Link>System\LogFile.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\System\PublicSystemInfo.cs">
- <Link>System\PublicSystemInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\System\SystemInfo.cs">
- <Link>System\SystemInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Tasks\SystemEvent.cs">
- <Link>Tasks\SystemEvent.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Tasks\TaskCompletionStatus.cs">
- <Link>Tasks\TaskCompletionStatus.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Tasks\TaskInfo.cs">
- <Link>Tasks\TaskInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Tasks\TaskResult.cs">
- <Link>Tasks\TaskResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Tasks\TaskState.cs">
- <Link>Tasks\TaskState.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Tasks\TaskTriggerInfo.cs">
- <Link>Tasks\TaskTriggerInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Updates\CheckForUpdateResult.cs">
- <Link>Updates\CheckForUpdateResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Updates\InstallationInfo.cs">
- <Link>Updates\InstallationInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Updates\PackageInfo.cs">
- <Link>Updates\PackageInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Updates\PackageTargetSystem.cs">
- <Link>Updates\PackageTargetSystem.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Updates\PackageVersionClass.cs">
- <Link>Updates\PackageVersionClass.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Updates\PackageVersionInfo.cs">
- <Link>Updates\PackageVersionInfo.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Users\AuthenticationResult.cs">
- <Link>Users\AuthenticationResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Users\ForgotPasswordAction.cs">
- <Link>Users\ForgotPasswordAction.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Users\ForgotPasswordResult.cs">
- <Link>Users\ForgotPasswordResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Users\PinRedeemResult.cs">
- <Link>Users\PinRedeemResult.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Users\UserAction.cs">
- <Link>Users\UserAction.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Users\UserActionType.cs">
- <Link>Users\UserActionType.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Model\Users\UserPolicy.cs">
- <Link>Users\UserPolicy.cs</Link>
- </Compile>
- <Compile Include="..\SharedVersion.cs">
- <Link>Properties\SharedVersion.cs</Link>
- </Compile>
- <Compile Include="Properties\AssemblyInfo.cs" />
- </ItemGroup>
- <ItemGroup>
- <None Include="MediaBrowser.Model.snk" />
- </ItemGroup>
- <ItemGroup>
- <Content Include="FodyWeavers.xml" />
- </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.Model.net35/MediaBrowser.Model.snk b/MediaBrowser.Model.net35/MediaBrowser.Model.snk
deleted file mode 100644
index f8188c78e3..0000000000
--- a/MediaBrowser.Model.net35/MediaBrowser.Model.snk
+++ /dev/null
Binary files differ
diff --git a/MediaBrowser.Controller/Activity/IActivityManager.cs b/MediaBrowser.Model/Activity/IActivityManager.cs
index 7285489112..7032dff143 100644
--- a/MediaBrowser.Controller/Activity/IActivityManager.cs
+++ b/MediaBrowser.Model/Activity/IActivityManager.cs
@@ -1,10 +1,9 @@
-using MediaBrowser.Model.Activity;
+using System;
+using System.Threading.Tasks;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Querying;
-using System;
-using System.Threading.Tasks;
-namespace MediaBrowser.Controller.Activity
+namespace MediaBrowser.Model.Activity
{
public interface IActivityManager
{
diff --git a/MediaBrowser.Controller/Activity/IActivityRepository.cs b/MediaBrowser.Model/Activity/IActivityRepository.cs
index 7ccbc2e99b..c1952d436b 100644
--- a/MediaBrowser.Controller/Activity/IActivityRepository.cs
+++ b/MediaBrowser.Model/Activity/IActivityRepository.cs
@@ -1,9 +1,8 @@
-using MediaBrowser.Model.Activity;
-using MediaBrowser.Model.Querying;
-using System;
+using System;
using System.Threading.Tasks;
+using MediaBrowser.Model.Querying;
-namespace MediaBrowser.Controller.Activity
+namespace MediaBrowser.Model.Activity
{
public interface IActivityRepository
{
diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs
index 5f8c77ccd0..e79253d190 100644
--- a/MediaBrowser.Model/Configuration/LibraryOptions.cs
+++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs
@@ -13,6 +13,8 @@
public bool SaveLocalMetadata { get; set; }
public bool EnableInternetProviders { get; set; }
+ public bool ImportMissingEpisodes { get; set; }
+ public bool EnableAutomaticSeriesGrouping { get; set; }
public LibraryOptions()
{
@@ -20,6 +22,7 @@
EnableRealtimeMonitor = true;
PathInfos = new MediaPathInfo[] { };
EnableInternetProviders = true;
+ EnableAutomaticSeriesGrouping = true;
}
}
diff --git a/MediaBrowser.Model/Configuration/PeopleMetadataOptions.cs b/MediaBrowser.Model/Configuration/PeopleMetadataOptions.cs
deleted file mode 100644
index 8564d5b5b4..0000000000
--- a/MediaBrowser.Model/Configuration/PeopleMetadataOptions.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace MediaBrowser.Model.Configuration
-{
- public class PeopleMetadataOptions
- {
- public bool DownloadActorMetadata { get; set; }
- public bool DownloadDirectorMetadata { get; set; }
- public bool DownloadProducerMetadata { get; set; }
- public bool DownloadWriterMetadata { get; set; }
- public bool DownloadComposerMetadata { get; set; }
- public bool DownloadOtherPeopleMetadata { get; set; }
- public bool DownloadGuestStarMetadata { get; set; }
-
- public PeopleMetadataOptions()
- {
- DownloadActorMetadata = true;
- DownloadDirectorMetadata = true;
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index a1e5637a49..493fe1bd24 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -8,6 +8,9 @@ namespace MediaBrowser.Model.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>
@@ -43,6 +46,7 @@ namespace MediaBrowser.Model.Configuration
/// </summary>
/// <value><c>true</c> if [use HTTPS]; otherwise, <c>false</c>.</value>
public bool EnableHttps { get; set; }
+ public bool EnableSeriesPresentationUniqueKey { get; set; }
/// <summary>
/// Gets or sets the value pointing to the file system where the ssl certiifcate is located..
@@ -151,7 +155,6 @@ namespace MediaBrowser.Model.Configuration
/// </summary>
/// <value><c>true</c> if [enable dashboard response caching]; otherwise, <c>false</c>.</value>
public bool EnableDashboardResponseCaching { get; set; }
- public bool EnableDashboardResourceMinification { get; set; }
/// <summary>
/// Allows the dashboard to be served from a custom path.
@@ -170,6 +173,7 @@ namespace MediaBrowser.Model.Configuration
public bool EnableAutomaticRestart { get; set; }
public bool SkipDeserializationForBasicTypes { get; set; }
public bool SkipDeserializationForPrograms { get; set; }
+ public bool SkipDeserializationForAudio { get; set; }
public PathSubstitution[] PathSubstitutions { get; set; }
@@ -178,8 +182,6 @@ namespace MediaBrowser.Model.Configuration
public string UICulture { get; set; }
- public PeopleMetadataOptions PeopleMetadataOptions { get; set; }
-
public bool SaveMetadataHidden { get; set; }
public NameValuePair[] ContentTypes { get; set; }
@@ -188,11 +190,7 @@ namespace MediaBrowser.Model.Configuration
public int SharingExpirationDays { get; set; }
- public string[] Migrations { get; set; }
-
- public int MigrationVersion { get; set; }
public int SchemaVersion { get; set; }
- public int SqliteCacheSize { get; set; }
public bool EnableAnonymousUsageReporting { get; set; }
public bool EnableStandaloneMusicKeys { get; set; }
@@ -203,6 +201,7 @@ namespace MediaBrowser.Model.Configuration
public bool DisplayCollectionsView { get; set; }
public string[] LocalNetworkAddresses { get; set; }
public string[] CodecsUsed { get; set; }
+ public string[] Migrations { get; set; }
public bool EnableChannelView { get; set; }
public bool EnableExternalContentInSuggestions { get; set; }
public bool EnableSimpleArtistDetection { get; set; }
@@ -214,9 +213,8 @@ namespace MediaBrowser.Model.Configuration
public ServerConfiguration()
{
LocalNetworkAddresses = new string[] { };
- Migrations = new string[] { };
CodecsUsed = new string[] { };
- SqliteCacheSize = 0;
+ Migrations = new string[] { };
ImageExtractionTimeoutMs = 0;
EnableLocalizedGuids = true;
@@ -224,13 +222,12 @@ namespace MediaBrowser.Model.Configuration
EnableExternalContentInSuggestions = true;
ImageSavingConvention = ImageSavingConvention.Compatible;
- PublicPort = 8096;
- PublicHttpsPort = 8920;
- HttpServerPortNumber = 8096;
- HttpsPortNumber = 8920;
+ PublicPort = DefaultHttpPort;
+ PublicHttpsPort = DefaultHttpsPort;
+ HttpServerPortNumber = DefaultHttpPort;
+ HttpsPortNumber = DefaultHttpsPort;
EnableHttps = false;
EnableDashboardResponseCaching = true;
- EnableDashboardResourceMinification = true;
EnableAnonymousUsageReporting = true;
EnableAutomaticRestart = true;
@@ -262,8 +259,6 @@ namespace MediaBrowser.Model.Configuration
UICulture = "en-us";
- PeopleMetadataOptions = new PeopleMetadataOptions();
-
MetadataOptions = new[]
{
new MetadataOptions(1, 1280) {ItemType = "Book"},
diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs
index a6785a06a7..a4a88f169b 100644
--- a/MediaBrowser.Model/Configuration/UserConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs
@@ -57,6 +57,7 @@ namespace MediaBrowser.Model.Configuration
HidePlayedInLatest = true;
PlayDefaultAudioTrack = true;
+ DisplayMissingEpisodes = true;
LatestItemsExcludes = new string[] { };
OrderedViews = new string[] { };
diff --git a/MediaBrowser.Model/Cryptography/ICryptoProvider.cs b/MediaBrowser.Model/Cryptography/ICryptoProvider.cs
new file mode 100644
index 0000000000..7a82dee52b
--- /dev/null
+++ b/MediaBrowser.Model/Cryptography/ICryptoProvider.cs
@@ -0,0 +1,13 @@
+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/DeviceQuery.cs b/MediaBrowser.Model/Devices/DeviceQuery.cs
index 9ae4986062..9ceea1ea8f 100644
--- a/MediaBrowser.Model/Devices/DeviceQuery.cs
+++ b/MediaBrowser.Model/Devices/DeviceQuery.cs
@@ -4,11 +4,6 @@ namespace MediaBrowser.Model.Devices
public class DeviceQuery
{
/// <summary>
- /// Gets or sets a value indicating whether [supports content uploading].
- /// </summary>
- /// <value><c>null</c> if [supports content uploading] contains no value, <c>true</c> if [supports content uploading]; otherwise, <c>false</c>.</value>
- public bool? SupportsContentUploading { get; set; }
- /// <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>
diff --git a/MediaBrowser.Model/Diagnostics/IProcess.cs b/MediaBrowser.Model/Diagnostics/IProcess.cs
new file mode 100644
index 0000000000..ab0b0cfcff
--- /dev/null
+++ b/MediaBrowser.Model/Diagnostics/IProcess.cs
@@ -0,0 +1,19 @@
+using System;
+using System.IO;
+
+namespace MediaBrowser.Model.Diagnostics
+{
+ public interface IProcess : IDisposable
+ {
+ event EventHandler Exited;
+
+ void Kill();
+ bool WaitForExit(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
new file mode 100644
index 0000000000..a60c4b4fb6
--- /dev/null
+++ b/MediaBrowser.Model/Diagnostics/IProcessFactory.cs
@@ -0,0 +1,24 @@
+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/CodecProfile.cs b/MediaBrowser.Model/Dlna/CodecProfile.cs
index 385e98f619..29d4d21ecf 100644
--- a/MediaBrowser.Model/Dlna/CodecProfile.cs
+++ b/MediaBrowser.Model/Dlna/CodecProfile.cs
@@ -1,6 +1,7 @@
using MediaBrowser.Model.Extensions;
using System.Collections.Generic;
using System.Xml.Serialization;
+using MediaBrowser.Model.Dlna;
namespace MediaBrowser.Model.Dlna
{
diff --git a/MediaBrowser.Model/Dlna/ConditionProcessor.cs b/MediaBrowser.Model/Dlna/ConditionProcessor.cs
index ec13cacfae..e9e76a993e 100644
--- a/MediaBrowser.Model/Dlna/ConditionProcessor.cs
+++ b/MediaBrowser.Model/Dlna/ConditionProcessor.cs
@@ -1,6 +1,9 @@
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.MediaInfo;
using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
namespace MediaBrowser.Model.Dlna
{
@@ -21,7 +24,7 @@ namespace MediaBrowser.Model.Dlna
int? numVideoStreams,
int? numAudioStreams,
string videoCodecTag,
- bool? isAvc)
+ bool? isAvc )
{
switch (condition.Property)
{
@@ -86,8 +89,8 @@ namespace MediaBrowser.Model.Dlna
}
}
- public bool IsVideoAudioConditionSatisfied(ProfileCondition condition,
- int? audioChannels,
+ public bool IsVideoAudioConditionSatisfied(ProfileCondition condition,
+ int? audioChannels,
int? audioBitrate,
string audioProfile,
bool? isSecondaryTrack)
@@ -116,7 +119,7 @@ namespace MediaBrowser.Model.Dlna
}
int expected;
- if (IntHelper.TryParseCultureInvariant(condition.Value, out expected))
+ if (int.TryParse(condition.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out expected))
{
switch (condition.Condition)
{
@@ -149,9 +152,9 @@ namespace MediaBrowser.Model.Dlna
switch (condition.Condition)
{
case ProfileConditionType.EqualsAny:
- {
- return ListHelper.ContainsIgnoreCase(expected.Split('|'), currentValue);
- }
+ {
+ return ListHelper.ContainsIgnoreCase(expected.Split('|'), currentValue);
+ }
case ProfileConditionType.Equals:
return StringHelper.EqualsIgnoreCase(currentValue, expected);
case ProfileConditionType.NotEquals:
@@ -170,7 +173,7 @@ namespace MediaBrowser.Model.Dlna
}
bool expected;
- if (BoolHelper.TryParseCultureInvariant(condition.Value, out expected))
+ if (bool.TryParse(condition.Value, out expected))
{
switch (condition.Condition)
{
@@ -195,7 +198,7 @@ namespace MediaBrowser.Model.Dlna
}
float expected;
- if (FloatHelper.TryParseCultureInvariant(condition.Value, out expected))
+ if (float.TryParse(condition.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out expected))
{
switch (condition.Condition)
{
@@ -214,7 +217,7 @@ namespace MediaBrowser.Model.Dlna
return false;
}
-
+
private bool IsConditionSatisfied(ProfileCondition condition, double? currentValue)
{
if (!currentValue.HasValue)
@@ -224,7 +227,7 @@ namespace MediaBrowser.Model.Dlna
}
double expected;
- if (DoubleHelper.TryParseCultureInvariant(condition.Value, out expected))
+ if (double.TryParse(condition.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out expected))
{
switch (condition.Condition)
{
@@ -243,7 +246,7 @@ namespace MediaBrowser.Model.Dlna
return false;
}
-
+
private bool IsConditionSatisfied(ProfileCondition condition, TransportStreamTimestamp? timestamp)
{
if (!timestamp.HasValue)
@@ -251,9 +254,9 @@ namespace MediaBrowser.Model.Dlna
// 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:
diff --git a/MediaBrowser.Model/Dlna/ContainerProfile.cs b/MediaBrowser.Model/Dlna/ContainerProfile.cs
index 931194dd3d..35d7ada6b9 100644
--- a/MediaBrowser.Model/Dlna/ContainerProfile.cs
+++ b/MediaBrowser.Model/Dlna/ContainerProfile.cs
@@ -1,5 +1,7 @@
using System.Collections.Generic;
using System.Xml.Serialization;
+using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Model.Dlna
{
@@ -26,5 +28,12 @@ namespace MediaBrowser.Model.Dlna
}
return list;
}
+
+ public bool ContainsContainer(string container)
+ {
+ List<string> containers = GetContainers();
+
+ return containers.Count == 0 || ListHelper.ContainsIgnoreCase(containers, container ?? string.Empty);
+ }
}
}
diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs
index 884a9f29d1..821531ed06 100644
--- a/MediaBrowser.Model/Dlna/DeviceProfile.cs
+++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs
@@ -18,13 +18,13 @@ namespace MediaBrowser.Model.Dlna
public string Id { get; set; }
[XmlIgnore]
- public DeviceProfileType ProfileType { get; set; }
+ public MediaBrowser.Model.Dlna.DeviceProfileType ProfileType { get; set; }
/// <summary>
/// Gets or sets the identification.
/// </summary>
/// <value>The identification.</value>
- public DeviceIdentification Identification { get; set; }
+ public MediaBrowser.Model.Dlna.DeviceIdentification Identification { get; set; }
public string FriendlyName { get; set; }
public string Manufacturer { get; set; }
@@ -36,8 +36,8 @@ namespace MediaBrowser.Model.Dlna
public string SerialNumber { get; set; }
public bool EnableAlbumArtInDidl { get; set; }
- public bool EnableSingleAlbumArtLimit { get; set; }
- public bool EnableSingleSubtitleLimit { get; set; }
+ public bool EnableSingleAlbumArtLimit { get; set; }
+ public bool EnableSingleSubtitleLimit { get; set; }
public string SupportedMediaTypes { get; set; }
@@ -99,7 +99,7 @@ namespace MediaBrowser.Model.Dlna
public ResponseProfile[] ResponseProfiles { get; set; }
public SubtitleProfile[] SubtitleProfiles { get; set; }
-
+
public DeviceProfile()
{
DirectPlayProfiles = new DirectPlayProfile[] { };
@@ -108,9 +108,9 @@ namespace MediaBrowser.Model.Dlna
CodecProfiles = new CodecProfile[] { };
ContainerProfiles = new ContainerProfile[] { };
SubtitleProfiles = new SubtitleProfile[] { };
-
+
XmlRootAttributes = new XmlAttribute[] { };
-
+
SupportedMediaTypes = "Audio,Photo,Video";
MaxStreamingBitrate = 8000000;
MaxStaticBitrate = 8000000;
@@ -122,7 +122,7 @@ namespace MediaBrowser.Model.Dlna
List<string> list = new List<string>();
foreach (string i in (SupportedMediaTypes ?? string.Empty).Split(','))
{
- if (!string.IsNullOrEmpty(i))
+ if (!string.IsNullOrEmpty(i))
list.Add(i);
}
return list;
@@ -134,7 +134,7 @@ namespace MediaBrowser.Model.Dlna
foreach (var i in TranscodingProfiles)
{
- if (i.Type != DlnaProfileType.Audio)
+ if (i.Type != MediaBrowser.Model.Dlna.DlnaProfileType.Audio)
{
continue;
}
@@ -160,7 +160,7 @@ namespace MediaBrowser.Model.Dlna
foreach (var i in TranscodingProfiles)
{
- if (i.Type != DlnaProfileType.Video)
+ if (i.Type != MediaBrowser.Model.Dlna.DlnaProfileType.Video)
{
continue;
}
@@ -191,7 +191,7 @@ namespace MediaBrowser.Model.Dlna
foreach (var i in ResponseProfiles)
{
- if (i.Type != DlnaProfileType.Audio)
+ if (i.Type != MediaBrowser.Model.Dlna.DlnaProfileType.Audio)
{
continue;
}
@@ -208,12 +208,12 @@ namespace MediaBrowser.Model.Dlna
continue;
}
- ConditionProcessor conditionProcessor = new ConditionProcessor();
+ var conditionProcessor = new MediaBrowser.Model.Dlna.ConditionProcessor();
var anyOff = false;
foreach (ProfileCondition c in i.Conditions)
{
- if (!conditionProcessor.IsAudioConditionSatisfied(c, audioChannels, audioBitrate))
+ if (!conditionProcessor.IsAudioConditionSatisfied(GetModelProfileCondition(c), audioChannels, audioBitrate))
{
anyOff = true;
break;
@@ -230,13 +230,24 @@ namespace MediaBrowser.Model.Dlna
return null;
}
+ private MediaBrowser.Model.Dlna.ProfileCondition GetModelProfileCondition(ProfileCondition c)
+ {
+ return new MediaBrowser.Model.Dlna.ProfileCondition
+ {
+ Condition = c.Condition,
+ IsRequired = c.IsRequired,
+ Property = c.Property,
+ Value = c.Value
+ };
+ }
+
public ResponseProfile GetImageMediaProfile(string container, int? width, int? height)
{
container = StringHelper.TrimStart(container ?? string.Empty, '.');
foreach (var i in ResponseProfiles)
{
- if (i.Type != DlnaProfileType.Photo)
+ if (i.Type != MediaBrowser.Model.Dlna.DlnaProfileType.Photo)
{
continue;
}
@@ -247,12 +258,12 @@ namespace MediaBrowser.Model.Dlna
continue;
}
- ConditionProcessor conditionProcessor = new ConditionProcessor();
+ var conditionProcessor = new MediaBrowser.Model.Dlna.ConditionProcessor();
var anyOff = false;
foreach (ProfileCondition c in i.Conditions)
{
- if (!conditionProcessor.IsImageConditionSatisfied(c, width, height))
+ if (!conditionProcessor.IsImageConditionSatisfied(GetModelProfileCondition(c), width, height))
{
anyOff = true;
break;
@@ -269,7 +280,7 @@ namespace MediaBrowser.Model.Dlna
return null;
}
- public ResponseProfile GetVideoMediaProfile(string container,
+ public ResponseProfile GetVideoMediaProfile(string container,
string audioCodec,
string videoCodec,
int? width,
@@ -292,7 +303,7 @@ namespace MediaBrowser.Model.Dlna
foreach (var i in ResponseProfiles)
{
- if (i.Type != DlnaProfileType.Video)
+ if (i.Type != MediaBrowser.Model.Dlna.DlnaProfileType.Video)
{
continue;
}
@@ -315,12 +326,12 @@ namespace MediaBrowser.Model.Dlna
continue;
}
- ConditionProcessor conditionProcessor = new ConditionProcessor();
+ var conditionProcessor = new MediaBrowser.Model.Dlna.ConditionProcessor();
var anyOff = false;
foreach (ProfileCondition c in i.Conditions)
{
- if (!conditionProcessor.IsVideoConditionSatisfied(c, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc))
+ if (!conditionProcessor.IsVideoConditionSatisfied(GetModelProfileCondition(c), width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc))
{
anyOff = true;
break;
diff --git a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
index 183299425e..0b6ecf3857 100644
--- a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
+++ b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Xml.Serialization;
+using MediaBrowser.Model.Dlna;
namespace MediaBrowser.Model.Dlna
{
diff --git a/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs b/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs
index 926963ef67..b4fa4e0afd 100644
--- a/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs
+++ b/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs
@@ -1,4 +1,5 @@
using System.Xml.Serialization;
+using MediaBrowser.Model.Dlna;
namespace MediaBrowser.Model.Dlna
{
diff --git a/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs b/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs
new file mode 100644
index 0000000000..70191ff23c
--- /dev/null
+++ b/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs
@@ -0,0 +1,11 @@
+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/ProfileCondition.cs b/MediaBrowser.Model/Dlna/ProfileCondition.cs
index 9234a27136..3d104f9c41 100644
--- a/MediaBrowser.Model/Dlna/ProfileCondition.cs
+++ b/MediaBrowser.Model/Dlna/ProfileCondition.cs
@@ -1,4 +1,5 @@
using System.Xml.Serialization;
+using MediaBrowser.Model.Dlna;
namespace MediaBrowser.Model.Dlna
{
diff --git a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs
index fb353b016b..b0760d91f2 100644
--- a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs
+++ b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs
@@ -61,7 +61,8 @@ namespace MediaBrowser.Model.Dlna
private static double GetVideoBitrateScaleFactor(string codec)
{
if (StringHelper.EqualsIgnoreCase(codec, "h265") ||
- StringHelper.EqualsIgnoreCase(codec, "hevc"))
+ StringHelper.EqualsIgnoreCase(codec, "hevc") ||
+ StringHelper.EqualsIgnoreCase(codec, "vp9"))
{
return .5;
}
diff --git a/MediaBrowser.Model/Dlna/ResponseProfile.cs b/MediaBrowser.Model/Dlna/ResponseProfile.cs
index c1735f3b7c..1d4791b5c9 100644
--- a/MediaBrowser.Model/Dlna/ResponseProfile.cs
+++ b/MediaBrowser.Model/Dlna/ResponseProfile.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Xml.Serialization;
+using MediaBrowser.Model.Dlna;
namespace MediaBrowser.Model.Dlna
{
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index 0c93df52b8..6d68831ff7 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -6,6 +6,8 @@ using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Session;
using System;
using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
namespace MediaBrowser.Model.Dlna
{
@@ -408,6 +410,8 @@ namespace MediaBrowser.Model.Dlna
audioStreamIndex = audioStream.Index;
}
+ var allMediaStreams = item.MediaStreams;
+
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
@@ -423,7 +427,7 @@ namespace MediaBrowser.Model.Dlna
if (isEligibleForDirectPlay || isEligibleForDirectStream)
{
// See if it can be direct played
- PlayMethod? directPlay = GetVideoDirectPlayProfile(options, item, videoStream, audioStream, isEligibleForDirectPlay, isEligibleForDirectStream);
+ PlayMethod? directPlay = GetVideoDirectPlayProfile(options, item, videoStream, audioStream, isEligibleForDirectPlay, isEligibleForDirectStream, allMediaStreams);
if (directPlay != null)
{
@@ -483,7 +487,7 @@ namespace MediaBrowser.Model.Dlna
if (!string.IsNullOrEmpty(transcodingProfile.MaxAudioChannels))
{
int transcodingMaxAudioChannels;
- if (IntHelper.TryParseCultureInvariant(transcodingProfile.MaxAudioChannels, out transcodingMaxAudioChannels))
+ if (int.TryParse(transcodingProfile.MaxAudioChannels, NumberStyles.Any, CultureInfo.InvariantCulture, out transcodingMaxAudioChannels))
{
playlistItem.TranscodingMaxAudioChannels = transcodingMaxAudioChannels;
}
@@ -652,7 +656,8 @@ namespace MediaBrowser.Model.Dlna
MediaStream videoStream,
MediaStream audioStream,
bool isEligibleForDirectPlay,
- bool isEligibleForDirectStream)
+ bool isEligibleForDirectStream,
+ List<MediaStream> allMediaStreams)
{
DeviceProfile profile = options.Profile;
@@ -700,7 +705,7 @@ namespace MediaBrowser.Model.Dlna
foreach (ContainerProfile i in profile.ContainerProfiles)
{
if (i.Type == DlnaProfileType.Video &&
- ListHelper.ContainsIgnoreCase(i.GetContainers(), container))
+ i.ContainsContainer(container))
{
foreach (ProfileCondition c in i.Conditions)
{
@@ -975,7 +980,7 @@ namespace MediaBrowser.Model.Dlna
if (item.Bitrate.Value > maxBitrate.Value)
{
- _logger.Info("Bitrate exceeds DirectPlay limit");
+ _logger.Info("Bitrate exceeds DirectPlay limit: media bitrate: {0}, max bitrate: {1}", item.Bitrate.Value.ToString(CultureInfo.InvariantCulture), maxBitrate.Value.ToString(CultureInfo.InvariantCulture));
return false;
}
@@ -1039,7 +1044,7 @@ namespace MediaBrowser.Model.Dlna
case ProfileConditionValue.AudioBitrate:
{
int num;
- if (IntHelper.TryParseCultureInvariant(value, out num))
+ if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
{
item.AudioBitrate = num;
}
@@ -1048,12 +1053,28 @@ namespace MediaBrowser.Model.Dlna
case ProfileConditionValue.AudioChannels:
{
int num;
- if (IntHelper.TryParseCultureInvariant(value, out num))
+ if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
{
item.MaxAudioChannels = num;
}
break;
}
+ case ProfileConditionValue.IsAvc:
+ {
+ 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:
case ProfileConditionValue.AudioProfile:
case ProfileConditionValue.Has64BitOffsets:
@@ -1069,7 +1090,7 @@ namespace MediaBrowser.Model.Dlna
case ProfileConditionValue.RefFrames:
{
int num;
- if (IntHelper.TryParseCultureInvariant(value, out num))
+ if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
{
item.MaxRefFrames = num;
}
@@ -1078,7 +1099,7 @@ namespace MediaBrowser.Model.Dlna
case ProfileConditionValue.VideoBitDepth:
{
int num;
- if (IntHelper.TryParseCultureInvariant(value, out num))
+ if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
{
item.MaxVideoBitDepth = num;
}
@@ -1092,7 +1113,7 @@ namespace MediaBrowser.Model.Dlna
case ProfileConditionValue.Height:
{
int num;
- if (IntHelper.TryParseCultureInvariant(value, out num))
+ if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
{
item.MaxHeight = num;
}
@@ -1101,7 +1122,7 @@ namespace MediaBrowser.Model.Dlna
case ProfileConditionValue.VideoBitrate:
{
int num;
- if (IntHelper.TryParseCultureInvariant(value, out num))
+ if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
{
item.VideoBitrate = num;
}
@@ -1110,7 +1131,7 @@ namespace MediaBrowser.Model.Dlna
case ProfileConditionValue.VideoFramerate:
{
float num;
- if (FloatHelper.TryParseCultureInvariant(value, out num))
+ if (float.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
{
item.MaxFramerate = num;
}
@@ -1119,7 +1140,7 @@ namespace MediaBrowser.Model.Dlna
case ProfileConditionValue.VideoLevel:
{
int num;
- if (IntHelper.TryParseCultureInvariant(value, out num))
+ if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
{
item.VideoLevel = num;
}
@@ -1128,12 +1149,14 @@ namespace MediaBrowser.Model.Dlna
case ProfileConditionValue.Width:
{
int num;
- if (IntHelper.TryParseCultureInvariant(value, out num))
+ if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
{
item.MaxWidth = num;
}
break;
}
+ default:
+ break;
}
}
}
diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs
index b8db8a10c5..eb3b7d8fbf 100644
--- a/MediaBrowser.Model/Dlna/StreamInfo.cs
+++ b/MediaBrowser.Model/Dlna/StreamInfo.cs
@@ -6,6 +6,7 @@ using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Session;
using System;
using System.Collections.Generic;
+using System.Linq;
namespace MediaBrowser.Model.Dlna
{
@@ -35,6 +36,7 @@ namespace MediaBrowser.Model.Dlna
public string VideoCodec { get; set; }
public string VideoProfile { get; set; }
+ public bool RequireAvc { get; set; }
public bool CopyTimestamps { get; set; }
public bool EnableSubtitlesInManifest { get; set; }
public bool EnableSplittingOnNonKeyFrames { get; set; }
@@ -266,6 +268,7 @@ namespace MediaBrowser.Model.Dlna
list.Add(new NameValuePair("Tag", item.MediaSource.ETag ?? string.Empty));
list.Add(new NameValuePair("EnableSplittingOnNonKeyFrames", item.EnableSplittingOnNonKeyFrames.ToString().ToLower()));
+ list.Add(new NameValuePair("RequireAvc", item.RequireAvc.ToString().ToLower()));
return list;
}
diff --git a/MediaBrowser.Model/Dlna/SubtitleProfile.cs b/MediaBrowser.Model/Dlna/SubtitleProfile.cs
index 0723de222b..f182541d8a 100644
--- a/MediaBrowser.Model/Dlna/SubtitleProfile.cs
+++ b/MediaBrowser.Model/Dlna/SubtitleProfile.cs
@@ -1,6 +1,7 @@
using MediaBrowser.Model.Extensions;
using System.Collections.Generic;
using System.Xml.Serialization;
+using MediaBrowser.Model.Dlna;
namespace MediaBrowser.Model.Dlna
{
diff --git a/MediaBrowser.Model/Dlna/TranscodingProfile.cs b/MediaBrowser.Model/Dlna/TranscodingProfile.cs
index eeab996780..0f9fbae308 100644
--- a/MediaBrowser.Model/Dlna/TranscodingProfile.cs
+++ b/MediaBrowser.Model/Dlna/TranscodingProfile.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Xml.Serialization;
+using MediaBrowser.Model.Dlna;
namespace MediaBrowser.Model.Dlna
{
diff --git a/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs b/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs
new file mode 100644
index 0000000000..f4b9d1e9bc
--- /dev/null
+++ b/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs
@@ -0,0 +1,14 @@
+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/Drawing/ImageSize.cs b/MediaBrowser.Model/Drawing/ImageSize.cs
index 5ed6e72933..8cf09da185 100644
--- a/MediaBrowser.Model/Drawing/ImageSize.cs
+++ b/MediaBrowser.Model/Drawing/ImageSize.cs
@@ -1,4 +1,4 @@
-using MediaBrowser.Model.Extensions;
+using System.Globalization;
namespace MediaBrowser.Model.Drawing
{
@@ -71,12 +71,12 @@ namespace MediaBrowser.Model.Drawing
{
double val;
- if (DoubleHelper.TryParseCultureInvariant(parts[0], out val))
+ if (double.TryParse(parts[0], NumberStyles.Any, CultureInfo.InvariantCulture, out val))
{
_width = val;
}
- if (DoubleHelper.TryParseCultureInvariant(parts[1], out val))
+ if (double.TryParse(parts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out val))
{
_height = val;
}
diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs
index 9267222adf..864123bb91 100644
--- a/MediaBrowser.Model/Dto/BaseItemDto.cs
+++ b/MediaBrowser.Model/Dto/BaseItemDto.cs
@@ -8,7 +8,7 @@ using MediaBrowser.Model.Sync;
using System;
using System.Collections.Generic;
using System.Diagnostics;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Model.Dto
{
@@ -729,6 +729,8 @@ namespace MediaBrowser.Model.Dto
/// <value>The series studio.</value>
public string SeriesStudio { get; set; }
+ public StudioDto SeriesStudioInfo { get; set; }
+
/// <summary>
/// Gets or sets the parent thumb item id.
/// </summary>
diff --git a/MediaBrowser.Model/Dto/BaseItemPerson.cs b/MediaBrowser.Model/Dto/BaseItemPerson.cs
index 7052f1b82c..e73872cb76 100644
--- a/MediaBrowser.Model/Dto/BaseItemPerson.cs
+++ b/MediaBrowser.Model/Dto/BaseItemPerson.cs
@@ -1,5 +1,5 @@
using System.Diagnostics;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Model.Dto
{
diff --git a/MediaBrowser.Model/Dto/ChapterInfoDto.cs b/MediaBrowser.Model/Dto/ChapterInfoDto.cs
index a71d979900..51e0a545af 100644
--- a/MediaBrowser.Model/Dto/ChapterInfoDto.cs
+++ b/MediaBrowser.Model/Dto/ChapterInfoDto.cs
@@ -1,5 +1,5 @@
using System.Diagnostics;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Model.Dto
{
diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs
index 0b047f9e8f..814368d32f 100644
--- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs
+++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.MediaInfo;
using System.Collections.Generic;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Model.Dto
{
diff --git a/MediaBrowser.Model/Dto/StudioDto.cs b/MediaBrowser.Model/Dto/StudioDto.cs
index a0027cc4e2..13623fb1a9 100644
--- a/MediaBrowser.Model/Dto/StudioDto.cs
+++ b/MediaBrowser.Model/Dto/StudioDto.cs
@@ -1,6 +1,6 @@
using System.ComponentModel;
using System.Diagnostics;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Model.Dto
{
diff --git a/MediaBrowser.Model/Dto/UserDto.cs b/MediaBrowser.Model/Dto/UserDto.cs
index 94e4f95a3f..f9e3f7718c 100644
--- a/MediaBrowser.Model/Dto/UserDto.cs
+++ b/MediaBrowser.Model/Dto/UserDto.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Model.Connect;
using MediaBrowser.Model.Users;
using System;
using System.Diagnostics;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Model.Dto
{
diff --git a/MediaBrowser.Model/Entities/BaseItemInfo.cs b/MediaBrowser.Model/Entities/BaseItemInfo.cs
index 88af18289c..db6c4b3fa7 100644
--- a/MediaBrowser.Model/Entities/BaseItemInfo.cs
+++ b/MediaBrowser.Model/Entities/BaseItemInfo.cs
@@ -2,7 +2,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Model.Entities
{
@@ -132,6 +132,8 @@ namespace MediaBrowser.Model.Entities
/// <value>The album.</value>
public string Album { get; set; }
+ public bool IsThemeMedia { get; set; }
+
/// <summary>
/// Gets or sets the artists.
/// </summary>
diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs
index c215a80713..85f475ca25 100644
--- a/MediaBrowser.Model/Entities/MediaStream.cs
+++ b/MediaBrowser.Model/Entities/MediaStream.cs
@@ -118,7 +118,9 @@ namespace MediaBrowser.Model.Entities
private string AddLanguageIfNeeded(string title)
{
- if (!string.IsNullOrEmpty(Language) && title.IndexOf(Language, StringComparison.OrdinalIgnoreCase) == -1)
+ if (!string.IsNullOrEmpty(Language) &&
+ !string.Equals(Language, "und", StringComparison.OrdinalIgnoreCase) &&
+ !IsLanguageInTitle(title, Language))
{
title = StringHelper.FirstToUpper(Language) + " " + title;
}
@@ -126,6 +128,16 @@ namespace MediaBrowser.Model.Entities
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>
diff --git a/MediaBrowser.Model/Extensions/BoolHelper.cs b/MediaBrowser.Model/Extensions/BoolHelper.cs
deleted file mode 100644
index 5b61f864b0..0000000000
--- a/MediaBrowser.Model/Extensions/BoolHelper.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-namespace MediaBrowser.Model.Extensions
-{
- public static class BoolHelper
- {
- /// <summary>
- /// Tries the parse culture invariant.
- /// </summary>
- /// <param name="s">The s.</param>
- /// <param name="result">The result.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- public static bool TryParseCultureInvariant(string s, out bool result)
- {
- return bool.TryParse(s, out result);
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Extensions/DoubleHelper.cs b/MediaBrowser.Model/Extensions/DoubleHelper.cs
deleted file mode 100644
index bcaf2d7800..0000000000
--- a/MediaBrowser.Model/Extensions/DoubleHelper.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System.Globalization;
-
-namespace MediaBrowser.Model.Extensions
-{
- /// <summary>
- /// Isolating these helpers allow this entire project to be easily converted to Java
- /// </summary>
- public static class DoubleHelper
- {
- /// <summary>
- /// Tries the parse culture invariant.
- /// </summary>
- /// <param name="s">The s.</param>
- /// <param name="result">The result.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- public static bool TryParseCultureInvariant(string s, out double result)
- {
- return double.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out result);
- }
- }
-}
diff --git a/MediaBrowser.Model/Extensions/FloatHelper.cs b/MediaBrowser.Model/Extensions/FloatHelper.cs
deleted file mode 100644
index 171eccf931..0000000000
--- a/MediaBrowser.Model/Extensions/FloatHelper.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System.Globalization;
-
-namespace MediaBrowser.Model.Extensions
-{
- public static class FloatHelper
- {
- /// <summary>
- /// Tries the parse culture invariant.
- /// </summary>
- /// <param name="s">The s.</param>
- /// <param name="result">The result.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- public static bool TryParseCultureInvariant(string s, out float result)
- {
- return float.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out result);
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Model/Extensions/IntHelper.cs b/MediaBrowser.Model/Extensions/IntHelper.cs
deleted file mode 100644
index 6c5f26080a..0000000000
--- a/MediaBrowser.Model/Extensions/IntHelper.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System.Globalization;
-
-namespace MediaBrowser.Model.Extensions
-{
- /// <summary>
- /// Isolating these helpers allow this entire project to be easily converted to Java
- /// </summary>
- public static class IntHelper
- {
- /// <summary>
- /// Tries the parse culture invariant.
- /// </summary>
- /// <param name="s">The s.</param>
- /// <param name="result">The result.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
- public static bool TryParseCultureInvariant(string s, out int result)
- {
- return int.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out result);
- }
- }
-}
diff --git a/MediaBrowser.Model/Extensions/LinqExtensions.cs b/MediaBrowser.Model/Extensions/LinqExtensions.cs
new file mode 100644
index 0000000000..6b2bdb4c77
--- /dev/null
+++ b/MediaBrowser.Model/Extensions/LinqExtensions.cs
@@ -0,0 +1,84 @@
+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);
+ }
+
+ /// <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.Controller/Localization/ILocalizationManager.cs b/MediaBrowser.Model/Globalization/ILocalizationManager.cs
index 5742d235cd..4477d8de35 100644
--- a/MediaBrowser.Controller/Localization/ILocalizationManager.cs
+++ b/MediaBrowser.Model/Globalization/ILocalizationManager.cs
@@ -1,8 +1,7 @@
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Globalization;
-using System.Collections.Generic;
+using System.Collections.Generic;
+using MediaBrowser.Model.Entities;
-namespace MediaBrowser.Controller.Localization
+namespace MediaBrowser.Model.Globalization
{
/// <summary>
/// Interface ILocalizationManager
@@ -51,5 +50,9 @@ namespace MediaBrowser.Controller.Localization
/// </summary>
/// <returns>IEnumerable{LocalizatonOption}.</returns>
IEnumerable<LocalizatonOption> GetLocalizationOptions();
+
+ string RemoveDiacritics(string text);
+
+ string NormalizeFormKD(string text);
}
}
diff --git a/MediaBrowser.Controller/Health/IHealthMonitor.cs b/MediaBrowser.Model/Health/IHealthMonitor.cs
index b8ad98fc14..a4f95c1bcc 100644
--- a/MediaBrowser.Controller/Health/IHealthMonitor.cs
+++ b/MediaBrowser.Model/Health/IHealthMonitor.cs
@@ -3,7 +3,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Notifications;
-namespace MediaBrowser.Controller.Health
+namespace MediaBrowser.Model.Health
{
public interface IHealthMonitor
{
diff --git a/MediaBrowser.Model/IO/FileSystemMetadata.cs b/MediaBrowser.Model/IO/FileSystemMetadata.cs
new file mode 100644
index 0000000000..2aae4bb54d
--- /dev/null
+++ b/MediaBrowser.Model/IO/FileSystemMetadata.cs
@@ -0,0 +1,56 @@
+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
new file mode 100644
index 0000000000..62bb66ea85
--- /dev/null
+++ b/MediaBrowser.Model/IO/IFileSystem.cs
@@ -0,0 +1,421 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace MediaBrowser.Model.IO
+{
+ /// <summary>
+ /// Interface IFileSystem
+ /// </summary>
+ public interface IFileSystem
+ {
+ /// <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);
+
+ /// <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);
+
+ /// <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);
+
+ /// <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>
+ /// <param name="path">The path.</param>
+ /// <param name="recursive">if set to <c>true</c> [recursive].</param>
+ /// <returns>IEnumerable&lt;FileInfo&gt;.</returns>
+ IEnumerable<FileSystemMetadata> GetFiles(string path, bool recursive = false);
+
+ /// <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);
+
+ /// <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 isHidden);
+
+ char DirectorySeparatorChar { get; }
+ char PathSeparator { 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,
+ //
+ // Summary:
+ // Specifies that the operating system should open an existing file. When the file
+ // is opened, it should be truncated so that its size is zero bytes. This requires
+ // System.Security.Permissions.FileIOPermissionAccess.Write permission. Attempts
+ // to read from a file opened with FileMode.Truncate cause an System.ArgumentException
+ // exception.
+ Truncate = 5,
+ //
+ // Summary:
+ // Opens the file if it exists and seeks to the end of the file, or creates a new
+ // file. This requires System.Security.Permissions.FileIOPermissionAccess.Append
+ // permission. FileMode.Append can be used only in conjunction with FileAccess.Write.
+ // Trying to seek to a position before the end of the file throws an System.IO.IOException
+ // exception, and any attempt to read fails and throws a System.NotSupportedException
+ // exception.
+ Append = 6
+ }
+
+ 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,
+ //
+ // Summary:
+ // Read and write access to the file. Data can be written to and read from the file.
+ ReadWrite = 3
+ }
+
+ 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
+ }
+
+}
diff --git a/MediaBrowser.Common/IO/IMemoryStreamProvider.cs b/MediaBrowser.Model/IO/IMemoryStreamFactory.cs
index 1c0e98018e..f4f1746430 100644
--- a/MediaBrowser.Common/IO/IMemoryStreamProvider.cs
+++ b/MediaBrowser.Model/IO/IMemoryStreamFactory.cs
@@ -1,11 +1,12 @@
using System.IO;
-namespace MediaBrowser.Common.IO
+namespace MediaBrowser.Model.IO
{
- public interface IMemoryStreamProvider
+ 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
new file mode 100644
index 0000000000..16255e51f0
--- /dev/null
+++ b/MediaBrowser.Model/IO/IShortcutHandler.cs
@@ -0,0 +1,25 @@
+
+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.Common/IO/StreamDefaults.cs b/MediaBrowser.Model/IO/StreamDefaults.cs
index 8b16d89b3b..1e99ff4b55 100644
--- a/MediaBrowser.Common/IO/StreamDefaults.cs
+++ b/MediaBrowser.Model/IO/StreamDefaults.cs
@@ -1,5 +1,5 @@

-namespace MediaBrowser.Common.IO
+namespace MediaBrowser.Model.IO
{
/// <summary>
/// Class StreamDefaults
diff --git a/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs b/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs
index 7e93a130b6..9d7fdd129e 100644
--- a/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs
+++ b/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Model.Dto;
using System;
+using System.Collections.Generic;
namespace MediaBrowser.Model.LiveTv
{
@@ -103,6 +104,18 @@ namespace MediaBrowser.Model.LiveTv
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 List<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>
diff --git a/MediaBrowser.Model/LiveTv/ChannelInfoDto.cs b/MediaBrowser.Model/LiveTv/ChannelInfoDto.cs
index 8991aad866..a8ea864944 100644
--- a/MediaBrowser.Model/LiveTv/ChannelInfoDto.cs
+++ b/MediaBrowser.Model/LiveTv/ChannelInfoDto.cs
@@ -3,7 +3,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Library;
using System.Collections.Generic;
using System.Diagnostics;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Model.LiveTv
{
diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
index e19bddeee4..c5e140032e 100644
--- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
+++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
@@ -16,7 +16,7 @@ namespace MediaBrowser.Model.LiveTv
public string RecordingEncodingFormat { get; set; }
public bool EnableRecordingSubfolders { get; set; }
public bool EnableOriginalAudioWithEncodedRecordings { get; set; }
- public bool EnableOriginalVideoWithEncodedRecordings { get; set; }
+ public string RecordedVideoCodec { get; set; }
public List<TunerHostInfo> TunerHosts { get; set; }
public List<ListingsProviderInfo> ListingProviders { get; set; }
@@ -26,14 +26,17 @@ namespace MediaBrowser.Model.LiveTv
public string[] MediaLocationsCreated { get; set; }
+ public string RecordingPostProcessor { get; set; }
+ public string RecordingPostProcessorArguments { get; set; }
+
public LiveTvOptions()
{
EnableMovieProviders = true;
- EnableRecordingSubfolders = true;
TunerHosts = new List<TunerHostInfo>();
ListingProviders = new List<ListingsProviderInfo>();
MediaLocationsCreated = new string[] { };
RecordingEncodingFormat = "mp4";
+ RecordingPostProcessorArguments = "\"{path}\"";
}
}
@@ -83,6 +86,7 @@ namespace MediaBrowser.Model.LiveTv
public string[] KidsCategories { get; set; }
public string[] MovieCategories { get; set; }
public NameValuePair[] ChannelMappings { get; set; }
+ public string MoviePrefix { get; set; }
public ListingsProviderInfo()
{
diff --git a/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs b/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs
index 997b090ff6..3880012874 100644
--- a/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs
+++ b/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs
@@ -2,7 +2,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Model.LiveTv
{
diff --git a/MediaBrowser.Model/LiveTv/TimerQuery.cs b/MediaBrowser.Model/LiveTv/TimerQuery.cs
index 310dc486fe..c6202680c5 100644
--- a/MediaBrowser.Model/LiveTv/TimerQuery.cs
+++ b/MediaBrowser.Model/LiveTv/TimerQuery.cs
@@ -8,6 +8,8 @@
/// <value>The channel identifier.</value>
public string ChannelId { get; set; }
+ public string Id { get; set; }
+
/// <summary>
/// Gets or sets the series timer identifier.
/// </summary>
diff --git a/MediaBrowser.Common.Implementations/Logging/LogHelper.cs b/MediaBrowser.Model/Logging/LogHelper.cs
index 8080c2111c..cf1c021862 100644
--- a/MediaBrowser.Common.Implementations/Logging/LogHelper.cs
+++ b/MediaBrowser.Model/Logging/LogHelper.cs
@@ -1,7 +1,7 @@
using System;
using System.Text;
-namespace MediaBrowser.Common.Implementations.Logging
+namespace MediaBrowser.Model.Logging
{
/// <summary>
/// Class LogHelper
@@ -22,7 +22,7 @@ namespace MediaBrowser.Common.Implementations.Logging
var messageText = new StringBuilder();
- messageText.AppendLine(exception.Message);
+ messageText.AppendLine(exception.ToString());
messageText.AppendLine(exception.GetType().FullName);
@@ -71,7 +71,7 @@ namespace MediaBrowser.Common.Implementations.Logging
private static void AppendInnerException(StringBuilder messageText, Exception e)
{
messageText.AppendLine("InnerException: " + e.GetType().FullName);
- messageText.AppendLine(e.Message);
+ messageText.AppendLine(e.ToString());
LogExceptionData(messageText, e);
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index b9b920588d..f36519e27b 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<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>
@@ -9,12 +10,11 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MediaBrowser.Model</RootNamespace>
<AssemblyName>MediaBrowser.Model</AssemblyName>
+ <DefaultLanguage>en-US</DefaultLanguage>
<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>
- <ReleaseVersion>
- </ReleaseVersion>
- <NuGetPackageImportStamp>60e95275</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -24,7 +24,6 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
- <PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -34,25 +33,17 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release Mono|AnyCPU' ">
- <DebugType>pdbonly</DebugType>
- <Optimize>true</Optimize>
- <OutputPath>bin\Release Mono\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- </PropertyGroup>
- <PropertyGroup>
- <RunPostBuildEvent>Always</RunPostBuildEvent>
- </PropertyGroup>
- <PropertyGroup>
- <AssemblyOriginatorKeyFile>MediaBrowser.Model.snk</AssemblyOriginatorKeyFile>
- </PropertyGroup>
+ <ItemGroup>
+ <None Include="project.json" />
+ <!-- A reference to the entire .NET Framework is automatically included -->
+ </ItemGroup>
<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\ApiHelpers.cs" />
<Compile Include="ApiClient\ConnectionMode.cs" />
<Compile Include="ApiClient\ConnectionResult.cs" />
@@ -93,7 +84,6 @@
<Compile Include="Configuration\FanartOptions.cs" />
<Compile Include="Configuration\LibraryOptions.cs" />
<Compile Include="Configuration\MetadataConfiguration.cs" />
- <Compile Include="Configuration\PeopleMetadataOptions.cs" />
<Compile Include="Configuration\XbmcMetadataOptions.cs" />
<Compile Include="Configuration\SubtitlePlaybackMode.cs" />
<Compile Include="Connect\ConnectAuthenticationExchangeResult.cs" />
@@ -109,13 +99,24 @@
<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\PlaybackException.cs" />
@@ -123,7 +124,11 @@
<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" />
@@ -131,7 +136,26 @@
<Compile Include="Dto\MetadataEditorInfo.cs" />
<Compile Include="Dto\NameIdPair.cs" />
<Compile Include="Dto\NameValuePair.cs" />
+ <Compile Include="Net\IpEndPointInfo.cs" />
+ <Compile Include="Net\ISocket.cs" />
+ <Compile Include="Net\ISocketFactory.cs" />
+ <Compile Include="Net\IUdpSocket.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="FileOrganization\SmartMatchInfo.cs" />
+ <Compile Include="Health\IHealthMonitor.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" />
@@ -140,8 +164,6 @@
<Compile Include="Configuration\DynamicDayOfWeek.cs" />
<Compile Include="Entities\ExtraType.cs" />
<Compile Include="Entities\TrailerType.cs" />
- <Compile Include="Extensions\BoolHelper.cs" />
- <Compile Include="Extensions\FloatHelper.cs" />
<Compile Include="FileOrganization\AutoOrganizeOptions.cs" />
<Compile Include="FileOrganization\TvFileOrganizationOptions.cs" />
<Compile Include="Configuration\BaseApplicationConfiguration.cs" />
@@ -153,13 +175,27 @@
<Compile Include="Configuration\MetadataOptions.cs" />
<Compile Include="Configuration\MetadataPluginSummary.cs" />
<Compile Include="Configuration\MetadataPluginType.cs" />
- <Compile Include="Dlna\SubtitleProfile.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" />
@@ -171,36 +207,27 @@
<Compile Include="Providers\SubtitleOptions.cs" />
<Compile Include="Configuration\UnratedItem.cs" />
<Compile Include="Dlna\AudioOptions.cs" />
- <Compile Include="Dlna\CodecProfile.cs" />
<Compile Include="Dlna\CodecType.cs" />
<Compile Include="Dlna\ConditionProcessor.cs" />
- <Compile Include="Dlna\ContainerProfile.cs" />
<Compile Include="Dlna\ContentFeatureBuilder.cs" />
<Compile Include="Dlna\DeviceIdentification.cs" />
- <Compile Include="Dlna\DeviceProfile.cs" />
<Compile Include="Dlna\DeviceProfileInfo.cs" />
<Compile Include="Dlna\DeviceProfileType.cs" />
- <Compile Include="Dlna\DirectPlayProfile.cs" />
<Compile Include="Dlna\DlnaFlags.cs" />
<Compile Include="Dlna\DlnaMaps.cs" />
<Compile Include="Dlna\DlnaProfileType.cs" />
<Compile Include="Dlna\HeaderMatchType.cs" />
- <Compile Include="Dlna\HttpHeaderInfo.cs" />
<Compile Include="Dlna\MediaFormatProfile.cs" />
<Compile Include="Dlna\MediaFormatProfileResolver.cs" />
- <Compile Include="Dlna\ProfileCondition.cs" />
<Compile Include="Dlna\ProfileConditionType.cs" />
<Compile Include="Dlna\ProfileConditionValue.cs" />
- <Compile Include="Dlna\ResponseProfile.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\TranscodingProfile.cs" />
<Compile Include="Dlna\VideoOptions.cs" />
- <Compile Include="Dlna\XmlAttribute.cs" />
<Compile Include="Drawing\ImageFormat.cs" />
<Compile Include="Drawing\ImageSize.cs" />
<Compile Include="Dto\BaseItemPerson.cs" />
@@ -224,8 +251,6 @@
<Compile Include="Entities\ScrollDirection.cs" />
<Compile Include="Entities\SortOrder.cs" />
<Compile Include="Events\GenericEventArgs.cs" />
- <Compile Include="Extensions\DoubleHelper.cs" />
- <Compile Include="Extensions\IntHelper.cs" />
<Compile Include="Extensions\ListHelper.cs" />
<Compile Include="Extensions\StringHelper.cs" />
<Compile Include="FileOrganization\EpisodeFileOrganizationRequest.cs" />
@@ -315,6 +340,10 @@
<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" />
@@ -364,6 +393,7 @@
<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" />
@@ -391,8 +421,19 @@
<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" />
@@ -432,22 +473,9 @@
<Compile Include="Users\UserAction.cs" />
<Compile Include="Users\UserActionType.cs" />
<Compile Include="Users\UserPolicy.cs" />
- <None Include="MediaBrowser.Model.snk" />
- </ItemGroup>
- <ItemGroup>
- <Reference Include="Microsoft.CSharp" />
- <Reference Include="System" />
- <Reference Include="System.Core" />
- <Reference Include="System.Runtime.Serialization" />
- <Reference Include="System.Xml" />
+ <Compile Include="Xml\IXmlReaderSettingsFactory.cs" />
</ItemGroup>
- <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
- <PropertyGroup>
- <PostBuildEvent />
- </PropertyGroup>
- <PropertyGroup>
- <PostBuildEvent />
- </PropertyGroup>
+ <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">
diff --git a/MediaBrowser.Model/MediaBrowser.Model.nuget.targets b/MediaBrowser.Model/MediaBrowser.Model.nuget.targets
new file mode 100644
index 0000000000..e69ce0e64f
--- /dev/null
+++ b/MediaBrowser.Model/MediaBrowser.Model.nuget.targets
@@ -0,0 +1,6 @@
+<?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/MediaBrowser.Model.snk b/MediaBrowser.Model/MediaBrowser.Model.snk
deleted file mode 100644
index f8188c78e3..0000000000
--- a/MediaBrowser.Model/MediaBrowser.Model.snk
+++ /dev/null
Binary files differ
diff --git a/MediaBrowser.Model/Net/ISocket.cs b/MediaBrowser.Model/Net/ISocket.cs
new file mode 100644
index 0000000000..aed35bce89
--- /dev/null
+++ b/MediaBrowser.Model/Net/ISocket.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace MediaBrowser.Model.Net
+{
+ public interface ISocket : 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 StartAccept(Action<ISocket> onAccept, Func<bool> isClosed);
+ }
+
+ 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/ISocketFactory.cs b/MediaBrowser.Model/Net/ISocketFactory.cs
new file mode 100644
index 0000000000..ac406e7f12
--- /dev/null
+++ b/MediaBrowser.Model/Net/ISocketFactory.cs
@@ -0,0 +1,43 @@
+
+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="IUdpSocket"/> 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="IUdpSocket"/> implementation.</returns>
+ IUdpSocket CreateUdpSocket(int localPort);
+
+ /// <summary>
+ /// Createa a new unicast socket using the specified local port number.
+ /// </summary>
+ IUdpSocket 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="IUdpSocket"/> implementation.</returns>
+ IUdpSocket CreateUdpMulticastSocket(string ipAddress, int multicastTimeToLive, int localPort);
+
+ ISocket CreateSocket(IpAddressFamily family, SocketType socketType, ProtocolType protocolType, bool dualMode);
+ }
+
+ public enum SocketType
+ {
+ Stream
+ }
+
+ public enum ProtocolType
+ {
+ Tcp
+ }
+}
diff --git a/MediaBrowser.Model/Net/IUdpSocket.cs b/MediaBrowser.Model/Net/IUdpSocket.cs
new file mode 100644
index 0000000000..c705107266
--- /dev/null
+++ b/MediaBrowser.Model/Net/IUdpSocket.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+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 IUdpSocket : IDisposable
+ {
+ IpAddressInfo LocalIPAddress { get; }
+
+ /// <summary>
+ /// Waits for and returns the next UDP message sent to this socket (uni or multicast).
+ /// </summary>
+ /// <returns></returns>
+ Task<SocketReceiveResult> ReceiveAsync();
+
+ /// <summary>
+ /// Sends a UDP message to a particular end point (uni or multicast).
+ /// </summary>
+ Task SendAsync(byte[] buffer, int bytes, IpEndPointInfo endPoint);
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Model/Net/IpAddressInfo.cs b/MediaBrowser.Model/Net/IpAddressInfo.cs
new file mode 100644
index 0000000000..00a16c03df
--- /dev/null
+++ b/MediaBrowser.Model/Net/IpAddressInfo.cs
@@ -0,0 +1,42 @@
+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()
+ {
+
+ }
+
+ public IpAddressInfo(string address, IpAddressFamily addressFamily)
+ {
+ 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
new file mode 100644
index 0000000000..b5cadc4299
--- /dev/null
+++ b/MediaBrowser.Model/Net/IpEndPointInfo.cs
@@ -0,0 +1,30 @@
+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/SocketReceiveResult.cs b/MediaBrowser.Model/Net/SocketReceiveResult.cs
new file mode 100644
index 0000000000..483e2297b9
--- /dev/null
+++ b/MediaBrowser.Model/Net/SocketReceiveResult.cs
@@ -0,0 +1,25 @@
+
+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.Controller/News/INewsService.cs b/MediaBrowser.Model/News/INewsService.cs
index 8237764df4..4c92664d9b 100644
--- a/MediaBrowser.Controller/News/INewsService.cs
+++ b/MediaBrowser.Model/News/INewsService.cs
@@ -1,7 +1,6 @@
-using MediaBrowser.Model.News;
-using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Querying;
-namespace MediaBrowser.Controller.News
+namespace MediaBrowser.Model.News
{
/// <summary>
/// Interface INewsFeed
diff --git a/MediaBrowser.Model/Plugins/IHasWebPages.cs b/MediaBrowser.Model/Plugins/IHasWebPages.cs
new file mode 100644
index 0000000000..0745c3c602
--- /dev/null
+++ b/MediaBrowser.Model/Plugins/IHasWebPages.cs
@@ -0,0 +1,9 @@
+using System.Collections.Generic;
+
+namespace MediaBrowser.Model.Plugins
+{
+ public interface IHasWebPages
+ {
+ IEnumerable<PluginPageInfo> GetPages();
+ }
+}
diff --git a/MediaBrowser.Model/Plugins/PluginPageInfo.cs b/MediaBrowser.Model/Plugins/PluginPageInfo.cs
new file mode 100644
index 0000000000..4b91e07917
--- /dev/null
+++ b/MediaBrowser.Model/Plugins/PluginPageInfo.cs
@@ -0,0 +1,9 @@
+namespace MediaBrowser.Model.Plugins
+{
+ public class PluginPageInfo
+ {
+ public string Name { get; set; }
+
+ public string EmbeddedResourcePath { get; set; }
+ }
+}
diff --git a/MediaBrowser.Model/PropertyChanged.xml b/MediaBrowser.Model/PropertyChanged.xml
deleted file mode 100644
index e69755cf46..0000000000
--- a/MediaBrowser.Model/PropertyChanged.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0"?>
-<doc>
- <assembly>
- <name>PropertyChanged</name>
- </assembly>
- <members>
- <member name="T:PropertyChanged.AlsoNotifyForAttribute">
- <summary>
- Injects this property to be notified when a dependant property is set.
- </summary>
- </member>
- <member name="M:PropertyChanged.AlsoNotifyForAttribute.#ctor(System.String)">
- <summary>
- Initializes a new instance of <see cref="T:PropertyChanged.DependsOnAttribute"/>.
- </summary>
- <param name="property">A property that will be notified for.</param>
- </member>
- <member name="M:PropertyChanged.AlsoNotifyForAttribute.#ctor(System.String,System.String[])">
- <summary>
- Initializes a new instance of <see cref="T:PropertyChanged.DependsOnAttribute"/>.
- </summary>
- <param name="property">A property that will be notified for.</param>
- <param name="otherProperties">The properties that will be notified for.</param>
- </member>
- <member name="T:PropertyChanged.DependsOnAttribute">
- <summary>
- Injects this property to be notified when a dependant property is set.
- </summary>
- </member>
- <member name="M:PropertyChanged.DependsOnAttribute.#ctor(System.String)">
- <summary>
- Initializes a new instance of <see cref="T:PropertyChanged.DependsOnAttribute"/>.
- </summary>
- <param name="dependency">A property that the assigned property depends on.</param>
- </member>
- <member name="M:PropertyChanged.DependsOnAttribute.#ctor(System.String,System.String[])">
- <summary>
- Initializes a new instance of <see cref="T:PropertyChanged.DependsOnAttribute"/>.
- </summary>
- <param name="dependency">A property that the assigned property depends on.</param>
- <param name="otherDependencies">The properties that the assigned property depends on.</param>
- </member>
- <member name="T:PropertyChanged.DoNotNotifyAttribute">
- <summary>
- Exclude a <see cref="T:System.Type"/> or property from notification.
- </summary>
- </member>
- <member name="T:PropertyChanged.DoNotSetChangedAttribute">
- <summary>
- Exclude a <see cref="T:System.Type"/> or property from IsChanged flagging.
- </summary>
- </member>
- </members>
-</doc>
diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs
index dfca9e7718..e36abf16ed 100644
--- a/MediaBrowser.Model/Querying/ItemFields.cs
+++ b/MediaBrowser.Model/Querying/ItemFields.cs
@@ -45,6 +45,8 @@
/// </summary>
Chapters,
+ ChildCount,
+
/// <summary>
/// The critic rating summary
/// </summary>
@@ -169,6 +171,8 @@
/// </summary>
PrimaryImageAspectRatio,
+ RecursiveItemCount,
+
/// <summary>
/// The revenue
/// </summary>
diff --git a/MediaBrowser.Model/Reflection/IAssemblyInfo.cs b/MediaBrowser.Model/Reflection/IAssemblyInfo.cs
new file mode 100644
index 0000000000..e8e9c414cf
--- /dev/null
+++ b/MediaBrowser.Model/Reflection/IAssemblyInfo.cs
@@ -0,0 +1,14 @@
+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/Serialization/IXmlSerializer.cs b/MediaBrowser.Model/Serialization/IXmlSerializer.cs
index eb23d784fe..b26b673f3e 100644
--- a/MediaBrowser.Model/Serialization/IXmlSerializer.cs
+++ b/MediaBrowser.Model/Serialization/IXmlSerializer.cs
@@ -34,7 +34,7 @@ namespace MediaBrowser.Model.Serialization
/// <param name="file">The file.</param>
/// <returns>System.Object.</returns>
object DeserializeFromFile(Type type, string file);
-
+
/// <summary>
/// Deserializes from bytes.
/// </summary>
diff --git a/MediaBrowser.Model/Serialization/IgnoreDataMemberAttribute.cs b/MediaBrowser.Model/Serialization/IgnoreDataMemberAttribute.cs
new file mode 100644
index 0000000000..8e23edc24c
--- /dev/null
+++ b/MediaBrowser.Model/Serialization/IgnoreDataMemberAttribute.cs
@@ -0,0 +1,12 @@
+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
new file mode 100644
index 0000000000..4a28317757
--- /dev/null
+++ b/MediaBrowser.Model/Services/ApiMemberAttribute.cs
@@ -0,0 +1,61 @@
+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
new file mode 100644
index 0000000000..b10e12813a
--- /dev/null
+++ b/MediaBrowser.Model/Services/IAsyncStreamWriter.cs
@@ -0,0 +1,11 @@
+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
new file mode 100644
index 0000000000..35e652b0ff
--- /dev/null
+++ b/MediaBrowser.Model/Services/IHasHeaders.cs
@@ -0,0 +1,9 @@
+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
new file mode 100644
index 0000000000..2164179d50
--- /dev/null
+++ b/MediaBrowser.Model/Services/IHasRequestFilter.cs
@@ -0,0 +1,21 @@
+
+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
new file mode 100644
index 0000000000..46c0240cde
--- /dev/null
+++ b/MediaBrowser.Model/Services/IHttpRequest.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+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
new file mode 100644
index 0000000000..377f303a76
--- /dev/null
+++ b/MediaBrowser.Model/Services/IHttpResponse.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+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
new file mode 100644
index 0000000000..fcb137c6b0
--- /dev/null
+++ b/MediaBrowser.Model/Services/IHttpResult.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+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
new file mode 100644
index 0000000000..e9a9f1c5b5
--- /dev/null
+++ b/MediaBrowser.Model/Services/IRequest.cs
@@ -0,0 +1,156 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+
+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>
+ /// Whether the ResponseContentType has been explicitly overrided or whether it was just the default
+ /// </summary>
+ bool HasExplicitResponseContentType { get; }
+
+ /// <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; }
+ }
+
+}
diff --git a/MediaBrowser.Model/Services/IRequestFilter.cs b/MediaBrowser.Model/Services/IRequestFilter.cs
new file mode 100644
index 0000000000..7f6db2e4d6
--- /dev/null
+++ b/MediaBrowser.Model/Services/IRequestFilter.cs
@@ -0,0 +1,8 @@
+
+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
new file mode 100644
index 0000000000..0b8ac3ed34
--- /dev/null
+++ b/MediaBrowser.Model/Services/IRequiresRequestStream.cs
@@ -0,0 +1,12 @@
+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
new file mode 100644
index 0000000000..3e0ff280b3
--- /dev/null
+++ b/MediaBrowser.Model/Services/IService.cs
@@ -0,0 +1,12 @@
+
+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
new file mode 100644
index 0000000000..1fc11049e0
--- /dev/null
+++ b/MediaBrowser.Model/Services/IStreamWriter.cs
@@ -0,0 +1,9 @@
+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
new file mode 100644
index 0000000000..dfea628213
--- /dev/null
+++ b/MediaBrowser.Model/Services/QueryParamCollection.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using MediaBrowser.Model.Dto;
+
+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 stringComparison = GetStringComparison();
+
+ var parameters = this.Where(p => string.Equals(key, p.Name, stringComparison)).ToArray();
+
+ 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>
+ /// True if the collection contains a query parameter with the given name.
+ /// </summary>
+ public bool ContainsKey(string name)
+ {
+ return this.Any(p => p.Name == name);
+ }
+
+ /// <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();
+
+ return this.Where(p => string.Equals(p.Name, name, stringComparison))
+ .Select(p => p.Value)
+ .FirstOrDefault();
+ }
+
+ public virtual string[] GetValues(string name)
+ {
+ var stringComparison = GetStringComparison();
+
+ return this.Where(p => string.Equals(p.Name, name, stringComparison)).Select(p => p.Value).ToArray();
+ }
+
+ 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 { return this.Select(i => i.Name); }
+ }
+
+ /// <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();
+
+ return string.Join("&", vals);
+ }
+ }
+}
diff --git a/MediaBrowser.Model/Services/RouteAttribute.cs b/MediaBrowser.Model/Services/RouteAttribute.cs
new file mode 100644
index 0000000000..5a39688da9
--- /dev/null
+++ b/MediaBrowser.Model/Services/RouteAttribute.cs
@@ -0,0 +1,144 @@
+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; }
+
+ /// <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/ClientCapabilities.cs b/MediaBrowser.Model/Session/ClientCapabilities.cs
index f00b145450..d5e54ae11d 100644
--- a/MediaBrowser.Model/Session/ClientCapabilities.cs
+++ b/MediaBrowser.Model/Session/ClientCapabilities.cs
@@ -13,10 +13,8 @@ namespace MediaBrowser.Model.Session
public string MessageCallbackUrl { get; set; }
- public bool SupportsContentUploading { get; set; }
public bool SupportsPersistentIdentifier { get; set; }
public bool SupportsSync { get; set; }
- public bool SupportsOfflineAccess { get; set; }
public DeviceProfile DeviceProfile { get; set; }
public List<string> SupportedLiveMediaTypes { get; set; }
diff --git a/MediaBrowser.Controller/Social/ISharingManager.cs b/MediaBrowser.Model/Social/ISharingManager.cs
index ded37771af..94c22baba5 100644
--- a/MediaBrowser.Controller/Social/ISharingManager.cs
+++ b/MediaBrowser.Model/Social/ISharingManager.cs
@@ -1,7 +1,6 @@
-using MediaBrowser.Model.Social;
-using System.Threading.Tasks;
+using System.Threading.Tasks;
-namespace MediaBrowser.Controller.Social
+namespace MediaBrowser.Model.Social
{
public interface ISharingManager
{
diff --git a/MediaBrowser.Model/Social/ISharingRepository.cs b/MediaBrowser.Model/Social/ISharingRepository.cs
new file mode 100644
index 0000000000..069b6e1fe2
--- /dev/null
+++ b/MediaBrowser.Model/Social/ISharingRepository.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Model.Social
+{
+ public interface ISharingRepository
+ {
+ Task CreateShare(SocialShareInfo info);
+ Task DeleteShare(string id);
+ SocialShareInfo GetShareInfo(string id);
+ }
+}
diff --git a/MediaBrowser.Model/System/Architecture.cs b/MediaBrowser.Model/System/Architecture.cs
index 09eedddc13..73f78cd582 100644
--- a/MediaBrowser.Model/System/Architecture.cs
+++ b/MediaBrowser.Model/System/Architecture.cs
@@ -4,6 +4,7 @@
{
X86 = 0,
X64 = 1,
- Arm = 2
+ Arm = 2,
+ Arm64 = 3
}
}
diff --git a/MediaBrowser.Model/System/IEnvironmentInfo.cs b/MediaBrowser.Model/System/IEnvironmentInfo.cs
new file mode 100644
index 0000000000..abe39fa03d
--- /dev/null
+++ b/MediaBrowser.Model/System/IEnvironmentInfo.cs
@@ -0,0 +1,22 @@
+
+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; }
+ }
+
+ public enum OperatingSystem
+ {
+ Windows,
+ Linux,
+ OSX
+ }
+}
diff --git a/MediaBrowser.Model/System/IPowerManagement.cs b/MediaBrowser.Model/System/IPowerManagement.cs
new file mode 100644
index 0000000000..91cae0d3e4
--- /dev/null
+++ b/MediaBrowser.Model/System/IPowerManagement.cs
@@ -0,0 +1,9 @@
+
+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
new file mode 100644
index 0000000000..dec8ed8c02
--- /dev/null
+++ b/MediaBrowser.Model/System/ISystemEvents.cs
@@ -0,0 +1,12 @@
+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/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs
index 022e03baf6..6145c7b61f 100644
--- a/MediaBrowser.Model/System/SystemInfo.cs
+++ b/MediaBrowser.Model/System/SystemInfo.cs
@@ -49,12 +49,6 @@ namespace MediaBrowser.Model.System
public bool SupportsLibraryMonitor { get; set; }
/// <summary>
- /// Gets or sets a value indicating whether this instance is network deployed.
- /// </summary>
- /// <value><c>true</c> if this instance is network deployed; otherwise, <c>false</c>.</value>
- public bool IsNetworkDeployed { get; set; }
-
- /// <summary>
/// Gets or sets the in progress installations.
/// </summary>
/// <value>The in progress installations.</value>
diff --git a/MediaBrowser.Common/ScheduledTasks/IConfigurableScheduledTask.cs b/MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs
index 6989dea06b..ed981a905b 100644
--- a/MediaBrowser.Common/ScheduledTasks/IConfigurableScheduledTask.cs
+++ b/MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs
@@ -1,4 +1,4 @@
-namespace MediaBrowser.Common.ScheduledTasks
+namespace MediaBrowser.Model.Tasks
{
public interface IConfigurableScheduledTask
{
@@ -12,10 +12,7 @@
/// </summary>
/// <value><c>true</c> if this instance is enabled; otherwise, <c>false</c>.</value>
bool IsEnabled { get; }
- }
- public interface IScheduledTaskActivityLog
- {
- bool IsActivityLogged { get; }
+ bool IsLogged { get; }
}
} \ No newline at end of file
diff --git a/MediaBrowser.Common/ScheduledTasks/IScheduledTask.cs b/MediaBrowser.Model/Tasks/IScheduledTask.cs
index 351e96c7d5..81ba239ad8 100644
--- a/MediaBrowser.Common/ScheduledTasks/IScheduledTask.cs
+++ b/MediaBrowser.Model/Tasks/IScheduledTask.cs
@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
-namespace MediaBrowser.Common.ScheduledTasks
+namespace MediaBrowser.Model.Tasks
{
/// <summary>
/// Interface IScheduledTaskWorker
@@ -16,6 +16,8 @@ namespace MediaBrowser.Common.ScheduledTasks
/// <value>The name.</value>
string Name { get; }
+ string Key { get; }
+
/// <summary>
/// Gets the description.
/// </summary>
@@ -40,6 +42,6 @@ namespace MediaBrowser.Common.ScheduledTasks
/// Gets the default triggers.
/// </summary>
/// <returns>IEnumerable{BaseTaskTrigger}.</returns>
- IEnumerable<ITaskTrigger> GetDefaultTriggers();
+ IEnumerable<TaskTriggerInfo> GetDefaultTriggers();
}
}
diff --git a/MediaBrowser.Common/ScheduledTasks/IScheduledTaskWorker.cs b/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs
index 0b9a5e276a..415207f8f0 100644
--- a/MediaBrowser.Common/ScheduledTasks/IScheduledTaskWorker.cs
+++ b/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs
@@ -1,9 +1,7 @@
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Tasks;
-using System;
-using System.Collections.Generic;
+using System;
+using MediaBrowser.Model.Events;
-namespace MediaBrowser.Common.ScheduledTasks
+namespace MediaBrowser.Model.Tasks
{
/// <summary>
/// Interface IScheduledTaskWorker
@@ -61,8 +59,8 @@ namespace MediaBrowser.Common.ScheduledTasks
/// Gets the triggers that define when the task will run
/// </summary>
/// <value>The triggers.</value>
- /// <exception cref="System.ArgumentNullException">value</exception>
- IEnumerable<ITaskTrigger> Triggers { get; set; }
+ /// <exception cref="ArgumentNullException">value</exception>
+ TaskTriggerInfo[] Triggers { get; set; }
/// <summary>
/// Gets the unique id.
diff --git a/MediaBrowser.Common/ScheduledTasks/ITaskManager.cs b/MediaBrowser.Model/Tasks/ITaskManager.cs
index f1809c4516..fa3da97b3e 100644
--- a/MediaBrowser.Common/ScheduledTasks/ITaskManager.cs
+++ b/MediaBrowser.Model/Tasks/ITaskManager.cs
@@ -1,9 +1,9 @@
-using MediaBrowser.Model.Events;
-using System;
+using System;
using System.Collections.Generic;
using System.Threading.Tasks;
+using MediaBrowser.Model.Events;
-namespace MediaBrowser.Common.ScheduledTasks
+namespace MediaBrowser.Model.Tasks
{
public interface ITaskManager : IDisposable
{
@@ -74,7 +74,5 @@ namespace MediaBrowser.Common.ScheduledTasks
event EventHandler<GenericEventArgs<IScheduledTaskWorker>> TaskExecuting;
event EventHandler<TaskCompletionEventArgs> TaskCompleted;
-
- bool SuspendTriggers { get; set; }
}
} \ No newline at end of file
diff --git a/MediaBrowser.Common/ScheduledTasks/ITaskTrigger.cs b/MediaBrowser.Model/Tasks/ITaskTrigger.cs
index ef1ea9d387..3beca569cc 100644
--- a/MediaBrowser.Common/ScheduledTasks/ITaskTrigger.cs
+++ b/MediaBrowser.Model/Tasks/ITaskTrigger.cs
@@ -1,9 +1,8 @@
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Tasks;
-using System;
+using System;
+using MediaBrowser.Model.Events;
using MediaBrowser.Model.Logging;
-namespace MediaBrowser.Common.ScheduledTasks
+namespace MediaBrowser.Model.Tasks
{
/// <summary>
/// Interface ITaskTrigger
@@ -18,8 +17,6 @@ namespace MediaBrowser.Common.ScheduledTasks
/// <summary>
/// Stars waiting for the trigger action
/// </summary>
- /// <param name="lastResult">The last result.</param>
- /// <param name="isApplicationStartup">if set to <c>true</c> [is application startup].</param>
void Start(TaskResult lastResult, ILogger logger, string taskName, bool isApplicationStartup);
/// <summary>
diff --git a/MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs b/MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs
new file mode 100644
index 0000000000..66f5294e7c
--- /dev/null
+++ b/MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Linq;
+
+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;
+
+ var triggers = task.Triggers
+ .OrderBy(i => i.Type)
+ .ThenBy(i => i.DayOfWeek ?? DayOfWeek.Sunday)
+ .ThenBy(i => i.TimeOfDayTicks ?? 0)
+ .ToList();
+
+ return new TaskInfo
+ {
+ Name = task.Name,
+ CurrentProgressPercentage = task.CurrentProgress,
+ State = task.State,
+ Id = task.Id,
+ LastExecutionResult = task.LastExecutionResult,
+
+ Triggers = triggers,
+
+ Description = task.Description,
+ Category = task.Category,
+ IsHidden = isHidden,
+ Key = key
+ };
+ }
+ }
+}
diff --git a/MediaBrowser.Common/ScheduledTasks/TaskCompletionEventArgs.cs b/MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs
index 2974806d02..be9eaa613d 100644
--- a/MediaBrowser.Common/ScheduledTasks/TaskCompletionEventArgs.cs
+++ b/MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs
@@ -1,7 +1,6 @@
-using MediaBrowser.Model.Tasks;
-using System;
+using System;
-namespace MediaBrowser.Common.ScheduledTasks
+namespace MediaBrowser.Model.Tasks
{
public class TaskCompletionEventArgs : EventArgs
{
diff --git a/MediaBrowser.Common/ScheduledTasks/TaskExecutionOptions.cs b/MediaBrowser.Model/Tasks/TaskExecutionOptions.cs
index 41b33b1c26..faba35b222 100644
--- a/MediaBrowser.Common/ScheduledTasks/TaskExecutionOptions.cs
+++ b/MediaBrowser.Model/Tasks/TaskExecutionOptions.cs
@@ -1,5 +1,5 @@

-namespace MediaBrowser.Common.ScheduledTasks
+namespace MediaBrowser.Model.Tasks
{
/// <summary>
/// A class that encomposases all common task run properties.
diff --git a/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs b/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs
index aacd56e655..69578c41d0 100644
--- a/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs
+++ b/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs
@@ -42,5 +42,11 @@ namespace MediaBrowser.Model.Tasks
/// </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
new file mode 100644
index 0000000000..6901f1f944
--- /dev/null
+++ b/MediaBrowser.Model/Text/ITextEncoding.cs
@@ -0,0 +1,10 @@
+using System.Text;
+
+namespace MediaBrowser.Model.Text
+{
+ public interface ITextEncoding
+ {
+ Encoding GetASCIIEncoding();
+ Encoding GetFileEncoding(string path);
+ }
+}
diff --git a/MediaBrowser.Model/Threading/ITimer.cs b/MediaBrowser.Model/Threading/ITimer.cs
new file mode 100644
index 0000000000..42090250bf
--- /dev/null
+++ b/MediaBrowser.Model/Threading/ITimer.cs
@@ -0,0 +1,10 @@
+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
new file mode 100644
index 0000000000..5f3df1738d
--- /dev/null
+++ b/MediaBrowser.Model/Threading/ITimerFactory.cs
@@ -0,0 +1,10 @@
+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/PackageVersionInfo.cs b/MediaBrowser.Model/Updates/PackageVersionInfo.cs
index 22404b6f66..5e0631b3b7 100644
--- a/MediaBrowser.Model/Updates/PackageVersionInfo.cs
+++ b/MediaBrowser.Model/Updates/PackageVersionInfo.cs
@@ -1,5 +1,5 @@
using System;
-using System.Runtime.Serialization;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Model.Updates
{
diff --git a/MediaBrowser.Model/Xml/IXmlReaderSettingsFactory.cs b/MediaBrowser.Model/Xml/IXmlReaderSettingsFactory.cs
new file mode 100644
index 0000000000..b9628ec3eb
--- /dev/null
+++ b/MediaBrowser.Model/Xml/IXmlReaderSettingsFactory.cs
@@ -0,0 +1,9 @@
+using System.Xml;
+
+namespace MediaBrowser.Model.Xml
+{
+ public interface IXmlReaderSettingsFactory
+ {
+ XmlReaderSettings Create(bool enableValidation);
+ }
+}
diff --git a/MediaBrowser.Model/project.json b/MediaBrowser.Model/project.json
new file mode 100644
index 0000000000..fbbe9eaf32
--- /dev/null
+++ b/MediaBrowser.Model/project.json
@@ -0,0 +1,17 @@
+{
+ "frameworks":{
+ "netstandard1.6":{
+ "dependencies":{
+ "NETStandard.Library":"1.6.0",
+ }
+ },
+ ".NETPortable,Version=v4.5,Profile=Profile7":{
+ "buildOptions": {
+ "define": [ ]
+ },
+ "frameworkAssemblies":{
+
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Mono.sln b/MediaBrowser.Mono.sln
index 6300a95597..35c3962002 100644
--- a/MediaBrowser.Mono.sln
+++ b/MediaBrowser.Mono.sln
@@ -7,8 +7,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Model", "Media
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Common", "MediaBrowser.Common\MediaBrowser.Common.csproj", "{9142EEFA-7570-41E1-BFCC-468BB571AF2F}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Common.Implementations", "MediaBrowser.Common.Implementations\MediaBrowser.Common.Implementations.csproj", "{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Controller", "MediaBrowser.Controller\MediaBrowser.Controller.csproj", "{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Providers", "MediaBrowser.Providers\MediaBrowser.Providers.csproj", "{442B5058-DCAF-4263-BB6A-F21E31120A1B}"
@@ -21,8 +19,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Api", "MediaBr
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Server.Mono", "MediaBrowser.Server.Mono\MediaBrowser.Server.Mono.csproj", "{175A9388-F352-4586-A6B4-070DED62B644}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Dlna", "MediaBrowser.Dlna\MediaBrowser.Dlna.csproj", "{734098EB-6DC1-4DD0-A1CA-3140DCD2737C}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.MediaEncoding", "MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj", "{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenSubtitlesHandler", "OpenSubtitlesHandler\OpenSubtitlesHandler.csproj", "{4A4402D4-E910-443B-B8FC-2C18286A2CA0}"
@@ -35,7 +31,25 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Server.Startup
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing", "Emby.Drawing\Emby.Drawing.csproj", "{08FFF49B-F175-4807-A2B5-73B0EBD9F716}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Nat", "Mono.Nat\Mono.Nat.csproj", "{D7453B88-2266-4805-B39B-2B5A2A33E1BA}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "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}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Photos", "Emby.Photos\Emby.Photos.csproj", "{89AB4548-770D-41FD-A891-8DAFF44F452C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Server.Implementations", "Emby.Server.Implementations\Emby.Server.Implementations.csproj", "{E383961B-9356-4D5D-8233-9A1079D03055}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RSSDP", "RSSDP\RSSDP.csproj", "{21002819-C39A-4D3E-BE83-2A276A77FB1F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Dlna", "Emby.Dlna\Emby.Dlna.csproj", "{805844AB-E92F-45E6-9D99-4F6D48D129A5}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing.ImageMagick", "Emby.Drawing.ImageMagick\Emby.Drawing.ImageMagick.csproj", "{6CFEE013-6E7C-432B-AC37-CABF0880C69A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing.Net", "Emby.Drawing.Net\Emby.Drawing.Net.csproj", "{C97A239E-A96C-4D64-A844-CCF8CC30AECB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceStack", "ServiceStack\ServiceStack.csproj", "{680A1709-25EB-4D52-A87F-EE03FFD94BAA}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SocketHttpListener.Portable", "SocketHttpListener.Portable\SocketHttpListener.Portable.csproj", "{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -45,18 +59,25 @@ Global
Release Mono|x86 = Release Mono|x86
Release|Any CPU = Release|Any CPU
Release|x86 = Release|x86
+ Signed|Any CPU = Signed|Any CPU
+ Signed|x86 = Signed|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Debug|Any CPU.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 Mono|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release Mono|x86.ActiveCfg = Release Mono|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|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|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|x86.ActiveCfg = Release|Any CPU
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.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|x86.ActiveCfg = Debug|Any CPU
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Debug|x86.Build.0 = Debug|Any CPU
@@ -67,16 +88,10 @@ Global
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release|Any CPU.Build.0 = 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
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|x86.ActiveCfg = Debug|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|x86.Build.0 = Debug|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release Mono|x86.ActiveCfg = Release Mono|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|Any CPU.Build.0 = Release|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|x86.ActiveCfg = Release|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.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|x86.ActiveCfg = Release|Any CPU
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|x86.Build.0 = Release|Any CPU
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Debug|Any CPU.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
@@ -87,6 +102,10 @@ Global
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release|Any CPU.Build.0 = 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|x86.ActiveCfg = Release|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.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|x86.ActiveCfg = Debug|Any CPU
{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Debug|x86.Build.0 = Debug|Any CPU
@@ -97,6 +116,10 @@ Global
{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release|Any CPU.Build.0 = Release|Any CPU
{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release|x86.ActiveCfg = Release|Any CPU
{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release|x86.Build.0 = 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|x86.ActiveCfg = Release|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|x86.Build.0 = Release|Any CPU
{2E781478-814D-4A48-9D80-BFF206441A65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2E781478-814D-4A48-9D80-BFF206441A65}.Debug|x86.ActiveCfg = Debug|Any CPU
{2E781478-814D-4A48-9D80-BFF206441A65}.Debug|x86.Build.0 = Debug|Any CPU
@@ -107,6 +130,10 @@ Global
{2E781478-814D-4A48-9D80-BFF206441A65}.Release|Any CPU.Build.0 = Release|Any CPU
{2E781478-814D-4A48-9D80-BFF206441A65}.Release|x86.ActiveCfg = Release|Any CPU
{2E781478-814D-4A48-9D80-BFF206441A65}.Release|x86.Build.0 = Release|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Signed|Any CPU.Build.0 = Release|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Signed|x86.ActiveCfg = Release|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.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|x86.ActiveCfg = Debug|Any CPU
{5624B7B5-B5A7-41D8-9F10-CC5611109619}.Debug|x86.Build.0 = Debug|Any CPU
@@ -117,6 +144,10 @@ Global
{5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release|Any CPU.Build.0 = 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|x86.ActiveCfg = Release|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.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|x86.ActiveCfg = Debug|Any CPU
{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Debug|x86.Build.0 = Debug|Any CPU
@@ -127,6 +158,10 @@ Global
{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release|Any CPU.Build.0 = 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|x86.ActiveCfg = Release|Any CPU
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|x86.Build.0 = Release|Any CPU
{175A9388-F352-4586-A6B4-070DED62B644}.Debug|Any CPU.ActiveCfg = Debug|x86
{175A9388-F352-4586-A6B4-070DED62B644}.Debug|x86.ActiveCfg = Debug|x86
{175A9388-F352-4586-A6B4-070DED62B644}.Debug|x86.Build.0 = Debug|x86
@@ -137,16 +172,10 @@ Global
{175A9388-F352-4586-A6B4-070DED62B644}.Release|Any CPU.Build.0 = Release|Any CPU
{175A9388-F352-4586-A6B4-070DED62B644}.Release|x86.ActiveCfg = Release|x86
{175A9388-F352-4586-A6B4-070DED62B644}.Release|x86.Build.0 = Release|x86
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Debug|x86.ActiveCfg = Debug|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Debug|x86.Build.0 = Debug|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release Mono|x86.ActiveCfg = Release Mono|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release|Any CPU.Build.0 = Release|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release|x86.ActiveCfg = Release|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release|x86.Build.0 = Release|Any CPU
+ {175A9388-F352-4586-A6B4-070DED62B644}.Signed|Any CPU.ActiveCfg = Debug|Any CPU
+ {175A9388-F352-4586-A6B4-070DED62B644}.Signed|Any CPU.Build.0 = Debug|Any CPU
+ {175A9388-F352-4586-A6B4-070DED62B644}.Signed|x86.ActiveCfg = Release Mono|x86
+ {175A9388-F352-4586-A6B4-070DED62B644}.Signed|x86.Build.0 = Release Mono|x86
{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|x86.ActiveCfg = Debug|Any CPU
{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|x86.Build.0 = Debug|Any CPU
@@ -157,6 +186,10 @@ Global
{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Any CPU.Build.0 = Release|Any CPU
{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|x86.ActiveCfg = Release|Any CPU
{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|x86.Build.0 = Release|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|Any CPU.Build.0 = Release|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|x86.ActiveCfg = Release|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|x86.Build.0 = Release|Any CPU
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Debug|x86.ActiveCfg = Debug|Any CPU
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Debug|x86.Build.0 = Debug|Any CPU
@@ -167,6 +200,10 @@ Global
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release|Any CPU.Build.0 = Release|Any CPU
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release|x86.ActiveCfg = Release|Any CPU
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release|x86.Build.0 = 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|x86.ActiveCfg = Release|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.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|x86.ActiveCfg = Debug|Any CPU
{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Debug|x86.Build.0 = Debug|Any CPU
@@ -177,6 +214,10 @@ Global
{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release|Any CPU.Build.0 = Release|Any CPU
{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release|x86.ActiveCfg = Release|Any CPU
{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release|x86.Build.0 = 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|x86.ActiveCfg = Release|Any CPU
+ {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.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|x86.ActiveCfg = Debug|Any CPU
{23499896-B135-4527-8574-C26E926EA99E}.Debug|x86.Build.0 = Debug|Any CPU
@@ -187,6 +228,10 @@ Global
{23499896-B135-4527-8574-C26E926EA99E}.Release|Any CPU.Build.0 = Release|Any CPU
{23499896-B135-4527-8574-C26E926EA99E}.Release|x86.ActiveCfg = Release|Any CPU
{23499896-B135-4527-8574-C26E926EA99E}.Release|x86.Build.0 = 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|x86.ActiveCfg = Release|Any CPU
+ {23499896-B135-4527-8574-C26E926EA99E}.Signed|x86.Build.0 = Release|Any CPU
{B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Debug|x86.ActiveCfg = Debug|Any CPU
@@ -196,6 +241,10 @@ Global
{B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Release|Any CPU.Build.0 = Release|Any CPU
{B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Release|x86.ActiveCfg = Release|Any CPU
+ {B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Signed|Any CPU.Build.0 = Release|Any CPU
+ {B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Signed|x86.ActiveCfg = Release|Any CPU
+ {B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.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|x86.ActiveCfg = Debug|Any CPU
@@ -205,18 +254,170 @@ Global
{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|x86.ActiveCfg = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|x86.ActiveCfg = Debug|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|x86.Build.0 = Debug|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|x86.Build.0 = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|Any CPU.Build.0 = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|x86.ActiveCfg = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|x86.Build.0 = 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|x86.ActiveCfg = Release|Any CPU
+ {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.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|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|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|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|x86.ActiveCfg = Release|Any CPU
+ {88AE38DF-19D7-406F-A6A9-09527719A21E}.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|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|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|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|x86.ActiveCfg = Release|Any CPU
+ {713F42B5-878E-499D-A878-E4C652B1D5E8}.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|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|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|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|x86.ActiveCfg = Release|Any CPU
+ {89AB4548-770D-41FD-A891-8DAFF44F452C}.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|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|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|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|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|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|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|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|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|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|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|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|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|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|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|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|x86.ActiveCfg = Release|Any CPU
+ {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Signed|x86.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Debug|x86.Build.0 = Debug|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release Mono|Any CPU.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release Mono|x86.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release Mono|x86.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release|x86.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release|x86.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|Any CPU.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|x86.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|x86.Build.0 = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|x86.Build.0 = Debug|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Any CPU.Build.0 = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|x86.ActiveCfg = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|x86.Build.0 = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|x86.ActiveCfg = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|x86.Build.0 = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Any CPU.ActiveCfg = Signed|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Any CPU.Build.0 = Signed|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|x86.ActiveCfg = Signed|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|x86.Build.0 = Signed|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|x86.Build.0 = Debug|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release Mono|Any CPU.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release Mono|x86.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release Mono|x86.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release|x86.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release|x86.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|Any CPU.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|x86.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/MediaBrowser.Providers/Books/AudioBookMetadataService.cs b/MediaBrowser.Providers/Books/AudioBookMetadataService.cs
new file mode 100644
index 0000000000..696619a8c1
--- /dev/null
+++ b/MediaBrowser.Providers/Books/AudioBookMetadataService.cs
@@ -0,0 +1,41 @@
+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.Common.IO;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+
+namespace MediaBrowser.Providers.Books
+{
+ public class AudioBookMetadataService : MetadataService<AudioBook, SongInfo>
+ {
+ protected override void MergeData(MetadataResult<AudioBook> source, MetadataResult<AudioBook> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ {
+ ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+
+ var sourceItem = source.Item;
+ var targetItem = target.Item;
+
+ if (replaceData || targetItem.Artists.Count == 0)
+ {
+ targetItem.Artists = sourceItem.Artists.ToList();
+ }
+
+ if (replaceData || string.IsNullOrEmpty(targetItem.Album))
+ {
+ targetItem.Album = sourceItem.Album;
+ }
+ }
+
+ public AudioBookMetadataService(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/AudioPodcastMetadataService.cs b/MediaBrowser.Providers/Books/AudioPodcastMetadataService.cs
new file mode 100644
index 0000000000..86b2cf1b13
--- /dev/null
+++ b/MediaBrowser.Providers/Books/AudioPodcastMetadataService.cs
@@ -0,0 +1,41 @@
+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.Common.IO;
+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, List<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.Count == 0)
+ {
+ targetItem.Artists = sourceItem.Artists.ToList();
+ }
+
+ 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/BookMetadataService.cs b/MediaBrowser.Providers/Books/BookMetadataService.cs
index 6f4a744c29..ccc69eb626 100644
--- a/MediaBrowser.Providers/Books/BookMetadataService.cs
+++ b/MediaBrowser.Providers/Books/BookMetadataService.cs
@@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Books
{
diff --git a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
index 2dacb16ca1..a625b93b80 100644
--- a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
+++ b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
@@ -9,7 +9,9 @@ using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.BoxSets
{
diff --git a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs
index 2dce13ebc3..852feab37c 100644
--- a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs
+++ b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs
@@ -162,8 +162,7 @@ namespace MediaBrowser.Providers.BoxSets
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
- Url = url,
- ResourcePool = MovieDbProvider.Current.MovieDbResourcePool
+ Url = url
});
}
}
diff --git a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs
index ab05c959e8..547420092d 100644
--- a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs
+++ b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs
@@ -3,7 +3,6 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
@@ -17,7 +16,10 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Globalization;
namespace MediaBrowser.Providers.BoxSets
{
@@ -317,8 +319,7 @@ namespace MediaBrowser.Providers.BoxSets
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
- Url = url,
- ResourcePool = MovieDbProvider.Current.MovieDbResourcePool
+ Url = url
});
}
}
diff --git a/MediaBrowser.Providers/Channels/ChannelMetadataService.cs b/MediaBrowser.Providers/Channels/ChannelMetadataService.cs
index 22e196d72c..c2a6c07f99 100644
--- a/MediaBrowser.Providers/Channels/ChannelMetadataService.cs
+++ b/MediaBrowser.Providers/Channels/ChannelMetadataService.cs
@@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Channels
{
diff --git a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs
index 2f534c12e5..c6013a94df 100644
--- a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs
+++ b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs
@@ -1,8 +1,10 @@
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Collections;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
diff --git a/MediaBrowser.Providers/Folders/FolderMetadataService.cs b/MediaBrowser.Providers/Folders/FolderMetadataService.cs
index 8c4737fc4a..94125b29db 100644
--- a/MediaBrowser.Providers/Folders/FolderMetadataService.cs
+++ b/MediaBrowser.Providers/Folders/FolderMetadataService.cs
@@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Folders
{
diff --git a/MediaBrowser.Providers/Folders/UserViewMetadataService.cs b/MediaBrowser.Providers/Folders/UserViewMetadataService.cs
index b8f58307a4..b1410bfd9a 100644
--- a/MediaBrowser.Providers/Folders/UserViewMetadataService.cs
+++ b/MediaBrowser.Providers/Folders/UserViewMetadataService.cs
@@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Folders
{
diff --git a/MediaBrowser.Providers/GameGenres/GameGenreImageProvider.cs b/MediaBrowser.Providers/GameGenres/GameGenreImageProvider.cs
deleted file mode 100644
index b26f237151..0000000000
--- a/MediaBrowser.Providers/GameGenres/GameGenreImageProvider.cs
+++ /dev/null
@@ -1,144 +0,0 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Providers;
-using MediaBrowser.Providers.Genres;
-using MediaBrowser.Providers.ImagesByName;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using CommonIO;
-
-namespace MediaBrowser.Providers.GameGenres
-{
- public class GameGenreImageProvider : IRemoteImageProvider
- {
- private readonly IServerConfigurationManager _config;
- private readonly IHttpClient _httpClient;
- private readonly IFileSystem _fileSystem;
-
- private readonly SemaphoreSlim _listResourcePool = new SemaphoreSlim(1, 1);
-
- public GameGenreImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem)
- {
- _config = config;
- _httpClient = httpClient;
- _fileSystem = fileSystem;
- }
-
- public string Name
- {
- get { return ProviderName; }
- }
-
- public static string ProviderName
- {
- get { return "Emby Designs"; }
- }
-
- public bool Supports(IHasImages item)
- {
- return item is GameGenre;
- }
-
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
- {
- return new List<ImageType>
- {
- ImageType.Primary,
- ImageType.Thumb
- };
- }
-
- public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
- {
- return GetImages(item, true, true, cancellationToken);
- }
-
- private async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, bool posters, bool thumbs, CancellationToken cancellationToken)
- {
- var list = new List<RemoteImageInfo>();
-
- if (posters)
- {
- var posterPath = Path.Combine(_config.ApplicationPaths.CachePath, "imagesbyname", "remotegamegenreposters.txt");
-
- await EnsurePosterList(posterPath, cancellationToken).ConfigureAwait(false);
-
- list.Add(GetImage(item, posterPath, ImageType.Primary, "folder"));
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- if (thumbs)
- {
- var thumbsPath = Path.Combine(_config.ApplicationPaths.CachePath, "imagesbyname", "remotegamegenrethumbs.txt");
-
- await EnsureThumbsList(thumbsPath, cancellationToken).ConfigureAwait(false);
-
- list.Add(GetImage(item, thumbsPath, ImageType.Thumb, "thumb"));
- }
-
- return list.Where(i => i != null);
- }
-
- private RemoteImageInfo GetImage(IHasImages item, string filename, ImageType type, string remoteFilename)
- {
- var list = ImageUtils.GetAvailableImages(filename);
-
- var match = ImageUtils.FindMatch(item, list);
-
- if (!string.IsNullOrEmpty(match))
- {
- var url = GetUrl(match, remoteFilename);
-
- return new RemoteImageInfo
- {
- ProviderName = Name,
- Type = type,
- Url = url
- };
- }
-
- return null;
- }
-
- private string GetUrl(string image, string filename)
- {
- return string.Format("https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/gamegenres/{0}/{1}.jpg", image, filename);
- }
-
- private Task EnsureThumbsList(string file, CancellationToken cancellationToken)
- {
- const string url = "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/gamegenrethumbs.txt";
-
- return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, _listResourcePool, cancellationToken);
- }
-
- private Task EnsurePosterList(string file, CancellationToken cancellationToken)
- {
- const string url = "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/gamegenreposters.txt";
-
- return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, _listResourcePool, cancellationToken);
- }
-
- public int Order
- {
- get { return 0; }
- }
-
- public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
- {
- return _httpClient.GetResponse(new HttpRequestOptions
- {
- CancellationToken = cancellationToken,
- Url = url,
- BufferContent = false
- });
- }
- }
-}
diff --git a/MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs b/MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs
index fb2244e327..180d36378e 100644
--- a/MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs
+++ b/MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs
@@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.GameGenres
{
diff --git a/MediaBrowser.Providers/Games/GameMetadataService.cs b/MediaBrowser.Providers/Games/GameMetadataService.cs
index a44f1d95f8..d6f61fa94f 100644
--- a/MediaBrowser.Providers/Games/GameMetadataService.cs
+++ b/MediaBrowser.Providers/Games/GameMetadataService.cs
@@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Games
{
diff --git a/MediaBrowser.Providers/Games/GameSystemMetadataService.cs b/MediaBrowser.Providers/Games/GameSystemMetadataService.cs
index 6cf2a45a47..5794e8c9b6 100644
--- a/MediaBrowser.Providers/Games/GameSystemMetadataService.cs
+++ b/MediaBrowser.Providers/Games/GameSystemMetadataService.cs
@@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Games
{
diff --git a/MediaBrowser.Providers/Genres/GenreImageProvider.cs b/MediaBrowser.Providers/Genres/GenreImageProvider.cs
deleted file mode 100644
index 954cd008e3..0000000000
--- a/MediaBrowser.Providers/Genres/GenreImageProvider.cs
+++ /dev/null
@@ -1,143 +0,0 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
-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 CommonIO;
-
-namespace MediaBrowser.Providers.Genres
-{
- public class GenreImageProvider : IRemoteImageProvider
- {
- private readonly IServerConfigurationManager _config;
- private readonly IHttpClient _httpClient;
- private readonly IFileSystem _fileSystem;
-
- private readonly SemaphoreSlim _listResourcePool = new SemaphoreSlim(1, 1);
-
- public GenreImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem)
- {
- _config = config;
- _httpClient = httpClient;
- _fileSystem = fileSystem;
- }
-
- public string Name
- {
- get { return ProviderName; }
- }
-
- public static string ProviderName
- {
- get { return "Emby Designs"; }
- }
-
- public bool Supports(IHasImages item)
- {
- return item is Genre;
- }
-
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
- {
- return new List<ImageType>
- {
- ImageType.Primary,
- ImageType.Thumb
- };
- }
-
- public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
- {
- return GetImages(item, true, true, cancellationToken);
- }
-
- private async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, bool posters, bool thumbs, CancellationToken cancellationToken)
- {
- var list = new List<RemoteImageInfo>();
-
- if (posters)
- {
- var posterPath = Path.Combine(_config.ApplicationPaths.CachePath, "imagesbyname", "remotegenreposters.txt");
-
- await EnsurePosterList(posterPath, cancellationToken).ConfigureAwait(false);
-
- list.Add(GetImage(item, posterPath, ImageType.Primary, "folder"));
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- if (thumbs)
- {
- var thumbsPath = Path.Combine(_config.ApplicationPaths.CachePath, "imagesbyname", "remotegenrethumbs.txt");
-
- await EnsureThumbsList(thumbsPath, cancellationToken).ConfigureAwait(false);
-
- list.Add(GetImage(item, thumbsPath, ImageType.Thumb, "thumb"));
- }
-
- return list.Where(i => i != null);
- }
-
- private RemoteImageInfo GetImage(IHasImages item, string filename, ImageType type, string remoteFilename)
- {
- var list = ImageUtils.GetAvailableImages(filename);
-
- var match = ImageUtils.FindMatch(item, list);
-
- if (!string.IsNullOrEmpty(match))
- {
- var url = GetUrl(match, remoteFilename);
-
- return new RemoteImageInfo
- {
- ProviderName = Name,
- Type = type,
- Url = url
- };
- }
-
- return null;
- }
-
- private string GetUrl(string image, string filename)
- {
- return string.Format("https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/genres/{0}/{1}.jpg", image, filename);
- }
-
- private Task EnsureThumbsList(string file, CancellationToken cancellationToken)
- {
- const string url = "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/genrethumbs.txt";
-
- return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, _listResourcePool, cancellationToken);
- }
-
- private Task EnsurePosterList(string file, CancellationToken cancellationToken)
- {
- const string url = "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/genreposters.txt";
-
- return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, _listResourcePool, cancellationToken);
- }
-
- public int Order
- {
- get { return 0; }
- }
-
- public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
- {
- return _httpClient.GetResponse(new HttpRequestOptions
- {
- CancellationToken = cancellationToken,
- Url = url,
- BufferContent = false
- });
- }
- }
-}
diff --git a/MediaBrowser.Providers/Genres/GenreMetadataService.cs b/MediaBrowser.Providers/Genres/GenreMetadataService.cs
index d4ea3e9cf8..39f9354dba 100644
--- a/MediaBrowser.Providers/Genres/GenreMetadataService.cs
+++ b/MediaBrowser.Providers/Genres/GenreMetadataService.cs
@@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Genres
{
diff --git a/MediaBrowser.Providers/ImagesByName/ImageUtils.cs b/MediaBrowser.Providers/ImagesByName/ImageUtils.cs
index b53348749f..bbcbbda905 100644
--- a/MediaBrowser.Providers/ImagesByName/ImageUtils.cs
+++ b/MediaBrowser.Providers/ImagesByName/ImageUtils.cs
@@ -6,7 +6,9 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.ImagesByName
{
@@ -68,23 +70,26 @@ namespace MediaBrowser.Providers.ImagesByName
.Replace("/", string.Empty);
}
- public static IEnumerable<string> GetAvailableImages(string file)
+ public static IEnumerable<string> GetAvailableImages(string file, IFileSystem fileSystem)
{
- using (var reader = new StreamReader(file))
+ using (var fileStream = fileSystem.GetFileStream(file, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
{
- var lines = new List<string>();
-
- while (!reader.EndOfStream)
+ using (var reader = new StreamReader(fileStream))
{
- var text = reader.ReadLine();
+ var lines = new List<string>();
- if (!string.IsNullOrWhiteSpace(text))
+ while (!reader.EndOfStream)
{
- lines.Add(text);
+ var text = reader.ReadLine();
+
+ if (!string.IsNullOrWhiteSpace(text))
+ {
+ lines.Add(text);
+ }
}
- }
- return lines;
+ return lines;
+ }
}
}
}
diff --git a/MediaBrowser.Providers/LiveTv/AudioRecordingService.cs b/MediaBrowser.Providers/LiveTv/AudioRecordingService.cs
index 1d99a678ba..df4a03cdb2 100644
--- a/MediaBrowser.Providers/LiveTv/AudioRecordingService.cs
+++ b/MediaBrowser.Providers/LiveTv/AudioRecordingService.cs
@@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.LiveTv
{
diff --git a/MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs b/MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs
index 8abb996895..7b5065986d 100644
--- a/MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs
+++ b/MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs
@@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.LiveTv
{
diff --git a/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs b/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs
index b73d82c19d..eaacc41fa2 100644
--- a/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs
+++ b/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs
@@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.LiveTv
{
diff --git a/MediaBrowser.Providers/LiveTv/VideoRecordingService.cs b/MediaBrowser.Providers/LiveTv/VideoRecordingService.cs
index 15530f8f99..a8df3c88be 100644
--- a/MediaBrowser.Providers/LiveTv/VideoRecordingService.cs
+++ b/MediaBrowser.Providers/LiveTv/VideoRecordingService.cs
@@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.LiveTv
{
diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs
index 767c034ee6..5146df6e6d 100644
--- a/MediaBrowser.Providers/Manager/ImageSaver.cs
+++ b/MediaBrowser.Providers/Manager/ImageSaver.cs
@@ -16,7 +16,8 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Manager
{
@@ -38,7 +39,7 @@ namespace MediaBrowser.Providers.Manager
private readonly ILibraryMonitor _libraryMonitor;
private readonly IFileSystem _fileSystem;
private readonly ILogger _logger;
- private readonly IMemoryStreamProvider _memoryStreamProvider;
+ private readonly IMemoryStreamFactory _memoryStreamProvider;
/// <summary>
/// Initializes a new instance of the <see cref="ImageSaver" /> class.
@@ -47,7 +48,7 @@ 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, IMemoryStreamProvider memoryStreamProvider)
+ public ImageSaver(IServerConfigurationManager config, ILibraryMonitor libraryMonitor, IFileSystem fileSystem, ILogger logger, IMemoryStreamFactory memoryStreamProvider)
{
_config = config;
_libraryMonitor = libraryMonitor;
@@ -172,14 +173,14 @@ namespace MediaBrowser.Providers.Manager
try
{
- var currentFile = new FileInfo(currentPath);
+ var currentFile = _fileSystem.GetFileInfo(currentPath);
// This will fail if the file is hidden
if (currentFile.Exists)
{
- if ((currentFile.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
+ if (currentFile.IsHidden)
{
- currentFile.Attributes &= ~FileAttributes.Hidden;
+ _fileSystem.SetHidden(currentFile.FullName, false);
}
_fileSystem.DeleteFile(currentFile.FullName);
@@ -254,18 +255,22 @@ namespace MediaBrowser.Providers.Manager
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
// If the file is currently hidden we'll have to remove that or the save will fail
- var file = new FileInfo(path);
+ var file = _fileSystem.GetFileInfo(path);
// This will fail if the file is hidden
if (file.Exists)
{
- if ((file.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
+ if (file.IsHidden)
{
- file.Attributes &= ~FileAttributes.Hidden;
+ _fileSystem.SetHidden(file.FullName, false);
+ }
+ if (file.IsReadOnly)
+ {
+ _fileSystem.SetReadOnly(path, false);
}
}
- using (var fs = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true))
+ using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
{
await source.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, cancellationToken)
.ConfigureAwait(false);
@@ -273,10 +278,7 @@ namespace MediaBrowser.Providers.Manager
if (_config.Configuration.SaveMetadataHidden)
{
- file.Refresh();
-
- // Add back the attribute
- file.Attributes |= FileAttributes.Hidden;
+ _fileSystem.SetHidden(file.FullName, true);
}
}
finally
diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
index 898fa522df..9dff243c1e 100644
--- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs
+++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
@@ -15,8 +15,12 @@ using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.MediaInfo;
@@ -154,7 +158,7 @@ namespace MediaBrowser.Providers.Manager
{
var mimeType = MimeTypes.GetMimeType(response.Path);
- var stream = _fileSystem.GetFileStream(response.Path, FileMode.Open, FileAccess.Read, FileShare.Read, true);
+ var stream = _fileSystem.GetFileStream(response.Path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true);
await _providerManager.SaveImage(item, stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false);
}
@@ -370,14 +374,14 @@ namespace MediaBrowser.Providers.Manager
}
// Delete the source file
- var currentFile = new FileInfo(image.Path);
+ var currentFile = _fileSystem.GetFileInfo(image.Path);
// Deletion will fail if the file is hidden so remove the attribute first
if (currentFile.Exists)
{
- if ((currentFile.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
+ if (currentFile.IsHidden)
{
- currentFile.Attributes &= ~FileAttributes.Hidden;
+ _fileSystem.SetHidden(currentFile.FullName, false);
}
_fileSystem.DeleteFile(currentFile.FullName);
@@ -552,9 +556,7 @@ namespace MediaBrowser.Providers.Manager
switch (type)
{
case ImageType.Primary:
- return false;
- case ImageType.Thumb:
- return false;
+ return !(item is Movie || item is Series || item is Game);
default:
return true;
}
@@ -611,7 +613,7 @@ namespace MediaBrowser.Providers.Manager
{
try
{
- if (item.GetImages(imageType).Any(i => new FileInfo(i.Path).Length == response.ContentLength.Value))
+ if (item.GetImages(imageType).Any(i => _fileSystem.GetFileInfo(i.Path).Length == response.ContentLength.Value))
{
response.Content.Dispose();
continue;
diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs
index 41cacbe0a8..9c6d6a482d 100644
--- a/MediaBrowser.Providers/Manager/MetadataService.cs
+++ b/MediaBrowser.Providers/Manager/MetadataService.cs
@@ -10,7 +10,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Providers;
@@ -78,16 +78,15 @@ namespace MediaBrowser.Providers.Manager
bool hasRefreshedMetadata = true;
bool hasRefreshedImages = true;
+ var isFirstRefresh = item.DateLastRefreshed == default(DateTime);
// Next run metadata providers
if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None)
{
- var providers = GetProviders(item, refreshOptions, requiresRefresh)
+ var providers = GetProviders(item, refreshOptions, isFirstRefresh, requiresRefresh)
.ToList();
- var dateLastRefresh = item.DateLastRefreshed;
-
- if (providers.Count > 0 || dateLastRefresh == default(DateTime))
+ if (providers.Count > 0 || isFirstRefresh)
{
if (item.BeforeMetadataRefresh())
{
@@ -110,11 +109,7 @@ namespace MediaBrowser.Providers.Manager
var result = await RefreshWithProviders(metadataResult, id, refreshOptions, providers, itemImageProvider, cancellationToken).ConfigureAwait(false);
updateType = updateType | result.UpdateType;
- if (result.Failures == 0)
- {
- hasRefreshedMetadata = true;
- }
- else
+ if (result.Failures > 0)
{
hasRefreshedMetadata = false;
}
@@ -138,19 +133,13 @@ namespace MediaBrowser.Providers.Manager
var result = await itemImageProvider.RefreshImages(itemOfType, libraryOptions, providers, refreshOptions, config, cancellationToken).ConfigureAwait(false);
updateType = updateType | result.UpdateType;
- if (result.Failures == 0)
- {
- hasRefreshedImages = true;
- }
- else
+ if (result.Failures > 0)
{
hasRefreshedImages = false;
}
}
}
- var isFirstRefresh = item.DateLastRefreshed == default(DateTime);
-
var beforeSaveResult = await BeforeSave(itemOfType, isFirstRefresh || refreshOptions.ReplaceAllMetadata || refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || requiresRefresh, updateType).ConfigureAwait(false);
updateType = updateType | beforeSaveResult;
@@ -373,15 +362,18 @@ namespace MediaBrowser.Providers.Manager
/// Gets the providers.
/// </summary>
/// <returns>IEnumerable{`0}.</returns>
- protected IEnumerable<IMetadataProvider> GetProviders(IHasMetadata item, MetadataRefreshOptions options, bool requiresRefresh)
+ protected IEnumerable<IMetadataProvider> GetProviders(IHasMetadata item, MetadataRefreshOptions options, bool isFirstRefresh, bool requiresRefresh)
{
// Get providers to refresh
var providers = ((ProviderManager)ProviderManager).GetMetadataProviders<TItemType>(item).ToList();
- var dateLastRefresh = item.DateLastRefreshed;
+ var metadataRefreshMode = options.MetadataRefreshMode;
// Run all if either of these flags are true
- var runAllProviders = options.ReplaceAllMetadata || options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || dateLastRefresh == default(DateTime) || requiresRefresh;
+ var runAllProviders = options.ReplaceAllMetadata ||
+ metadataRefreshMode == MetadataRefreshMode.FullRefresh ||
+ (isFirstRefresh && metadataRefreshMode >= MetadataRefreshMode.Default) ||
+ (requiresRefresh && metadataRefreshMode >= MetadataRefreshMode.Default);
if (!runAllProviders)
{
@@ -404,6 +396,9 @@ namespace MediaBrowser.Providers.Manager
}
else
{
+ var anyRemoteProvidersChanged = providersWithChanges.OfType<IRemoteMetadataProvider>()
+ .Any();
+
providers = providers.Where(i =>
{
// If any provider reports a change, always run local ones as well
@@ -412,12 +407,14 @@ namespace MediaBrowser.Providers.Manager
return true;
}
- var anyRemoteProvidersChanged = providersWithChanges.OfType<IRemoteMetadataProvider>()
- .Any();
-
// If any remote providers changed, run them all so that priorities can be honored
if (i is IRemoteMetadataProvider)
{
+ if (options.MetadataRefreshMode == MetadataRefreshMode.ValidationOnly)
+ {
+ return false;
+ }
+
return anyRemoteProvidersChanged;
}
@@ -536,7 +533,7 @@ namespace MediaBrowser.Providers.Manager
refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport;
// Only one local provider allowed per item
- if (item.IsLocked || IsFullLocalMetadata(localItem.Item))
+ if (item.IsLocked || localItem.Item.IsLocked || IsFullLocalMetadata(localItem.Item))
{
hasLocalMetadata = true;
}
@@ -573,14 +570,16 @@ namespace MediaBrowser.Providers.Manager
{
if (refreshResult.UpdateType > ItemUpdateType.None)
{
- // If no local metadata, take data from item itself
- if (!hasLocalMetadata)
+ if (hasLocalMetadata)
+ {
+ MergeData(temp, metadata, item.LockedFields, true, true);
+ }
+ else
{
// TODO: If the new metadata from above has some blank data, this can cause old data to get filled into those empty fields
- MergeData(metadata, temp, new List<MetadataFields>(), false, true);
+ MergeData(metadata, temp, new List<MetadataFields>(), false, false);
+ MergeData(temp, metadata, item.LockedFields, true, false);
}
-
- MergeData(temp, metadata, item.LockedFields, true, true);
}
}
diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs
index dfeceed7d4..a2f3d04596 100644
--- a/MediaBrowser.Providers/Manager/ProviderManager.cs
+++ b/MediaBrowser.Providers/Manager/ProviderManager.cs
@@ -19,8 +19,10 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Providers.Manager
@@ -64,7 +66,7 @@ namespace MediaBrowser.Providers.Manager
private IExternalId[] _externalIds;
private readonly Func<ILibraryManager> _libraryManagerFactory;
- private readonly IMemoryStreamProvider _memoryStreamProvider;
+ private readonly IMemoryStreamFactory _memoryStreamProvider;
/// <summary>
/// Initializes a new instance of the <see cref="ProviderManager" /> class.
@@ -74,7 +76,7 @@ namespace MediaBrowser.Providers.Manager
/// <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, IMemoryStreamProvider memoryStreamProvider)
+ public ProviderManager(IHttpClient httpClient, IServerConfigurationManager configurationManager, ILibraryMonitor libraryMonitor, ILogManager logManager, IFileSystem fileSystem, IServerApplicationPaths appPaths, Func<ILibraryManager> libraryManagerFactory, IJsonSerializer json, IMemoryStreamFactory memoryStreamProvider)
{
_logger = logManager.GetLogger("ProviderManager");
_httpClient = httpClient;
@@ -127,7 +129,8 @@ namespace MediaBrowser.Providers.Manager
{
CancellationToken = cancellationToken,
ResourcePool = resourcePool,
- Url = url
+ Url = url,
+ BufferContent = false
}).ConfigureAwait(false);
@@ -147,7 +150,7 @@ namespace MediaBrowser.Providers.Manager
throw new ArgumentNullException("source");
}
- var fileStream = _fileSystem.GetFileStream(source, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true);
+ 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);
}
@@ -269,17 +272,17 @@ namespace MediaBrowser.Providers.Manager
{
var options = GetMetadataOptions(item);
- return GetMetadataProvidersInternal<T>(item, options, false, true);
+ return GetMetadataProvidersInternal<T>(item, options, false, false, true);
}
- private IEnumerable<IMetadataProvider<T>> GetMetadataProvidersInternal<T>(IHasMetadata item, MetadataOptions options, bool includeDisabled, bool checkIsOwnedItem)
+ private IEnumerable<IMetadataProvider<T>> GetMetadataProvidersInternal<T>(IHasMetadata item, MetadataOptions options, bool includeDisabled, bool forceEnableInternetMetadata, bool checkIsOwnedItem)
where T : IHasMetadata
{
// Avoid implicitly captured closure
var currentOptions = options;
return _metadataProviders.OfType<IMetadataProvider<T>>()
- .Where(i => CanRefresh(i, item, currentOptions, includeDisabled, checkIsOwnedItem))
+ .Where(i => CanRefresh(i, item, currentOptions, includeDisabled, forceEnableInternetMetadata, checkIsOwnedItem))
.OrderBy(i => GetConfiguredOrder(i, options))
.ThenBy(GetDefaultOrder);
}
@@ -291,7 +294,7 @@ namespace MediaBrowser.Providers.Manager
return GetImageProviders(item, options, new ImageRefreshOptions(new DirectoryService(_logger, _fileSystem)), includeDisabled).OfType<IRemoteImageProvider>();
}
- private bool CanRefresh(IMetadataProvider provider, IHasMetadata item, MetadataOptions options, bool includeDisabled, bool checkIsOwnedItem)
+ private bool CanRefresh(IMetadataProvider provider, IHasMetadata item, MetadataOptions options, bool includeDisabled, bool forceEnableInternetMetadata, bool checkIsOwnedItem)
{
if (!includeDisabled)
{
@@ -303,7 +306,7 @@ namespace MediaBrowser.Providers.Manager
if (provider is IRemoteMetadataProvider)
{
- if (!item.IsInternetMetadataEnabled())
+ if (!forceEnableInternetMetadata && !item.IsInternetMetadataEnabled())
{
return false;
}
@@ -354,7 +357,7 @@ namespace MediaBrowser.Providers.Manager
if (provider is IRemoteImageProvider)
{
- if (!item.IsInternetMetadataEnabled())
+ if (!refreshOptions.ForceEnableInternetMetadata && !item.IsInternetMetadataEnabled())
{
return false;
}
@@ -498,7 +501,7 @@ namespace MediaBrowser.Providers.Manager
private void AddMetadataPlugins<T>(List<MetadataPlugin> list, T item, MetadataOptions options)
where T : IHasMetadata
{
- var providers = GetMetadataProvidersInternal<T>(item, options, true, false).ToList();
+ var providers = GetMetadataProvidersInternal<T>(item, options, true, false, false).ToList();
// Locals
list.AddRange(providers.Where(i => (i is ILocalMetadataProvider)).Select(i => new MetadataPlugin
@@ -558,8 +561,6 @@ namespace MediaBrowser.Providers.Manager
new MetadataOptions();
}
- private readonly ConcurrentDictionary<string, SemaphoreSlim> _fileLocks = new ConcurrentDictionary<string, SemaphoreSlim>();
-
/// <summary>
/// Saves the metadata.
/// </summary>
@@ -583,6 +584,7 @@ namespace MediaBrowser.Providers.Manager
return SaveMetadata(item, updateType, _savers.Where(i => savers.Contains(i.Name, StringComparer.OrdinalIgnoreCase)));
}
+ private readonly SemaphoreSlim _saveLock = new SemaphoreSlim(1,1);
/// <summary>
/// Saves the metadata.
/// </summary>
@@ -612,9 +614,7 @@ namespace MediaBrowser.Providers.Manager
continue;
}
- var semaphore = _fileLocks.GetOrAdd(path, key => new SemaphoreSlim(1, 1));
-
- await semaphore.WaitAsync().ConfigureAwait(false);
+ await _saveLock.WaitAsync().ConfigureAwait(false);
try
{
@@ -627,8 +627,8 @@ namespace MediaBrowser.Providers.Manager
}
finally
{
+ _saveLock.Release();
_libraryMonitor.ReportFileSystemChangeComplete(path, false);
- semaphore.Release();
}
}
else
@@ -715,7 +715,7 @@ namespace MediaBrowser.Providers.Manager
var options = GetMetadataOptions(dummy);
- var providers = GetMetadataProvidersInternal<TItemType>(dummy, options, searchInfo.IncludeDisabledProviders, false)
+ var providers = GetMetadataProvidersInternal<TItemType>(dummy, options, searchInfo.IncludeDisabledProviders, false, false)
.OfType<IRemoteSearchProvider<TLookupType>>();
if (!string.IsNullOrEmpty(searchInfo.SearchProviderName))
@@ -861,8 +861,8 @@ namespace MediaBrowser.Providers.Manager
private readonly ConcurrentQueue<Tuple<Guid, MetadataRefreshOptions>> _refreshQueue =
new ConcurrentQueue<Tuple<Guid, MetadataRefreshOptions>>();
- private readonly object _refreshTimerLock = new object();
- private Timer _refreshTimer;
+ private readonly object _refreshQueueLock = new object();
+ private bool _isProcessingRefreshQueue;
public void QueueRefresh(Guid id, MetadataRefreshOptions options)
{
@@ -872,38 +872,18 @@ namespace MediaBrowser.Providers.Manager
}
_refreshQueue.Enqueue(new Tuple<Guid, MetadataRefreshOptions>(id, options));
- StartRefreshTimer();
- }
-
- private void StartRefreshTimer()
- {
- if (_disposed)
- {
- return;
- }
- lock (_refreshTimerLock)
+ lock (_refreshQueueLock)
{
- if (_refreshTimer == null)
+ if (!_isProcessingRefreshQueue)
{
- _refreshTimer = new Timer(RefreshTimerCallback, null, 100, Timeout.Infinite);
+ _isProcessingRefreshQueue = true;
+ Task.Run(() => StartProcessingRefreshQueue());
}
}
}
- private void StopRefreshTimer()
- {
- lock (_refreshTimerLock)
- {
- if (_refreshTimer != null)
- {
- _refreshTimer.Dispose();
- _refreshTimer = null;
- }
- }
- }
-
- private async void RefreshTimerCallback(object state)
+ private async Task StartProcessingRefreshQueue()
{
Tuple<Guid, MetadataRefreshOptions> refreshItem;
var libraryManager = _libraryManagerFactory();
@@ -937,7 +917,10 @@ namespace MediaBrowser.Providers.Manager
}
}
- StopRefreshTimer();
+ lock (_refreshQueueLock)
+ {
+ _isProcessingRefreshQueue = false;
+ }
}
private async Task RefreshItem(BaseItem item, MetadataRefreshOptions options, CancellationToken cancellationToken)
@@ -1016,7 +999,6 @@ namespace MediaBrowser.Providers.Manager
public void Dispose()
{
_disposed = true;
- StopRefreshTimer();
}
}
} \ No newline at end of file
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index 8b3d1fb7e8..fe554545fa 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<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>
@@ -13,6 +13,8 @@
<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' ">
@@ -23,7 +25,6 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
@@ -32,7 +33,6 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release Mono|AnyCPU' ">
<DebugType>none</DebugType>
@@ -41,47 +41,13 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<ItemGroup>
- <Reference Include="BDInfo, Version=1.0.5167.21152, Culture=neutral, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\BDInfo.dll</HintPath>
- </Reference>
- <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
- </Reference>
- <Reference Include="DvdLib, Version=1.0.5167.21152, Culture=neutral, PublicKeyToken=7a2f3f5ec8d93575, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\DvdLib.dll</HintPath>
- </Reference>
- <Reference Include="MoreLinq">
- <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
- </Reference>
- <Reference Include="Patterns.Logging">
- <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
- </Reference>
- <Reference Include="policy.2.0.taglib-sharp">
- <HintPath>..\packages\taglib.2.1.0.0\lib\policy.2.0.taglib-sharp.dll</HintPath>
- </Reference>
- <Reference Include="System" />
- <Reference Include="System.Core" />
- <Reference Include="System.Net" />
- <Reference Include="System.Xml.Linq" />
- <Reference Include="System.Data.DataSetExtensions" />
- <Reference Include="Microsoft.CSharp" />
- <Reference Include="System.Data" />
- <Reference Include="System.Xml" />
- <Reference Include="taglib-sharp">
- <HintPath>..\packages\taglib.2.1.0.0\lib\taglib-sharp.dll</HintPath>
- <Private>True</Private>
- </Reference>
- </ItemGroup>
- <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="BoxSets\BoxSetMetadataService.cs" />
<Compile Include="BoxSets\MovieDbBoxSetImageProvider.cs" />
@@ -116,8 +82,6 @@
<Compile Include="Movies\MovieDbSearch.cs" />
<Compile Include="Movies\MovieMetadataService.cs" />
<Compile Include="Movies\TmdbSettings.cs" />
- <Compile Include="GameGenres\GameGenreImageProvider.cs" />
- <Compile Include="Genres\GenreImageProvider.cs" />
<Compile Include="ImagesByName\ImageUtils.cs" />
<Compile Include="MediaInfo\AudioImageProvider.cs" />
<Compile Include="MediaInfo\VideoImageProvider.cs" />
@@ -149,16 +113,12 @@
<Compile Include="People\PersonMetadataService.cs" />
<Compile Include="People\MovieDbPersonProvider.cs" />
<Compile Include="Photos\PhotoAlbumMetadataService.cs" />
- <Compile Include="Photos\PhotoHelper.cs" />
<Compile Include="Photos\PhotoMetadataService.cs" />
- <Compile Include="Photos\PhotoProvider.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\ConfigurationExtension.cs" />
- <Compile Include="Subtitles\OpenSubtitleDownloader.cs" />
<Compile Include="Subtitles\SubtitleManager.cs" />
<Compile Include="TV\DummySeasonProvider.cs" />
<Compile Include="TV\EpisodeMetadataService.cs" />
@@ -180,7 +140,6 @@
<Compile Include="TV\SeasonMetadataService.cs" />
<Compile Include="TV\TheTVDB\TvdbEpisodeProvider.cs" />
<Compile Include="TV\TheTVDB\TvdbSeriesProvider.cs" />
- <Compile Include="TV\SeriesPostScanTask.cs" />
<Compile Include="TV\TheTVDB\TvdbPrescanTask.cs" />
<Compile Include="TV\TvExternalIds.cs" />
<Compile Include="Users\UserMetadataService.cs" />
@@ -188,6 +147,10 @@
<Compile Include="Years\YearMetadataService.cs" />
</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>
@@ -200,16 +163,9 @@
<Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project>
<Name>MediaBrowser.Model</Name>
</ProjectReference>
- <ProjectReference Include="..\OpenSubtitlesHandler\OpenSubtitlesHandler.csproj">
- <Project>{4a4402d4-e910-443b-b8fc-2c18286a2ca0}</Project>
- <Name>OpenSubtitlesHandler</Name>
- </ProjectReference>
- </ItemGroup>
- <ItemGroup>
- <None Include="packages.config" />
</ItemGroup>
<ItemGroup />
- <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <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">
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.nuget.targets b/MediaBrowser.Providers/MediaBrowser.Providers.nuget.targets
new file mode 100644
index 0000000000..e69ce0e64f
--- /dev/null
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.nuget.targets
@@ -0,0 +1,6 @@
+<?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 68fc803711..d65084287f 100644
--- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
@@ -6,13 +6,12 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.MediaInfo
{
@@ -21,8 +20,6 @@ namespace MediaBrowser.Providers.MediaInfo
/// </summary>
public class AudioImageProvider : IDynamicImageProvider, IHasItemChangeMonitor
{
- private readonly ConcurrentDictionary<string, SemaphoreSlim> _locks = new ConcurrentDictionary<string, SemaphoreSlim>();
-
private readonly IMediaEncoder _mediaEncoder;
private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
@@ -65,41 +62,25 @@ namespace MediaBrowser.Providers.MediaInfo
if (!_fileSystem.FileExists(path))
{
- var semaphore = GetLock(path);
-
- // Acquire a lock
- await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- // Check again in case it was saved while waiting for the lock
- if (!_fileSystem.FileExists(path))
- {
- _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
-
- var imageStream = imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("front", StringComparison.OrdinalIgnoreCase) != -1) ??
- imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("cover", StringComparison.OrdinalIgnoreCase) != -1) ??
- imageStreams.FirstOrDefault();
+ _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
- var imageStreamIndex = imageStream == null ? (int?)null : imageStream.Index;
+ var imageStream = imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("front", StringComparison.OrdinalIgnoreCase) != -1) ??
+ imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("cover", StringComparison.OrdinalIgnoreCase) != -1) ??
+ imageStreams.FirstOrDefault();
- var tempFile = await _mediaEncoder.ExtractAudioImage(item.Path, imageStreamIndex, cancellationToken).ConfigureAwait(false);
+ var imageStreamIndex = imageStream == null ? (int?)null : imageStream.Index;
- File.Copy(tempFile, path, true);
+ var tempFile = await _mediaEncoder.ExtractAudioImage(item.Path, imageStreamIndex, cancellationToken).ConfigureAwait(false);
- try
- {
- File.Delete(tempFile);
- }
- catch
- {
+ _fileSystem.CopyFile(tempFile, path, true);
- }
- }
+ try
+ {
+ _fileSystem.DeleteFile(tempFile);
}
- finally
+ catch
{
- semaphore.Release();
+
}
}
@@ -143,16 +124,6 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
- /// <summary>
- /// Gets the lock.
- /// </summary>
- /// <param name="filename">The filename.</param>
- /// <returns>SemaphoreSlim.</returns>
- private SemaphoreSlim GetLock(string filename)
- {
- return _locks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1));
- }
-
public string Name
{
get { return "Image Extractor"; }
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
index d255110fb7..eaffc12d78 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
@@ -7,7 +7,6 @@ using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
@@ -21,7 +20,10 @@ using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Globalization;
namespace MediaBrowser.Providers.MediaInfo
{
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
index be0b2ca6d7..0a070d3487 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
@@ -7,7 +7,6 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
@@ -26,7 +25,10 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Globalization;
namespace MediaBrowser.Providers.MediaInfo
{
@@ -132,23 +134,6 @@ namespace MediaBrowser.Providers.MediaInfo
{
cancellationToken.ThrowIfCancellationRequested();
- //var idString = item.Id.ToString("N");
- //var cachePath = Path.Combine(_appPaths.CachePath,
- // "ffprobe-video",
- // 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 protocol = item.LocationType == LocationType.Remote
? MediaProtocol.Http
: MediaProtocol.File;
@@ -218,7 +203,6 @@ namespace MediaBrowser.Providers.MediaInfo
var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
- video.VideoBitRate = videoStream == null ? null : videoStream.BitRate;
video.DefaultVideoStreamIndex = videoStream == null ? (int?)null : videoStream.Index;
video.HasSubtitles = mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle);
@@ -605,7 +589,7 @@ namespace MediaBrowser.Providers.MediaInfo
private void FetchFromDvdLib(Video item, IIsoMount mount)
{
var path = mount == null ? item.Path : mount.MountedPath;
- var dvd = new Dvd(path);
+ var dvd = new Dvd(path, _fileSystem);
var primaryTitle = dvd.Titles.OrderByDescending(GetRuntime).FirstOrDefault();
@@ -653,7 +637,7 @@ namespace MediaBrowser.Providers.MediaInfo
/// <returns>System.Nullable{IsoType}.</returns>
private IsoType? DetermineIsoType(IIsoMount isoMount)
{
- var fileSystemEntries = Directory.EnumerateFileSystemEntries(isoMount.MountedPath).Select(Path.GetFileName).ToList();
+ var fileSystemEntries = _fileSystem.GetFileSystemEntryPaths(isoMount.MountedPath).Select(Path.GetFileName).ToList();
if (fileSystemEntries.Contains("video_ts", StringComparer.OrdinalIgnoreCase) ||
fileSystemEntries.Contains("VIDEO_TS.IFO", StringComparer.OrdinalIgnoreCase))
diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs b/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs
index 9ae8413d19..b69e371364 100644
--- a/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs
+++ b/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs
@@ -139,7 +139,7 @@ namespace MediaBrowser.Providers.MediaInfo
request.IndexNumberEnd = episode.IndexNumberEnd;
request.SeriesName = episode.SeriesName;
}
-
+
try
{
var searchResults = await _subtitleManager.SearchSubtitles(request, cancellationToken).ConfigureAwait(false);
diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs
index 024171f40d..313feda52a 100644
--- a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs
+++ b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs
@@ -1,13 +1,15 @@
using MediaBrowser.Model.Extensions;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Globalization;
namespace MediaBrowser.Providers.MediaInfo
{
@@ -43,6 +45,11 @@ namespace MediaBrowser.Providers.MediaInfo
var codec = Path.GetExtension(fullName).ToLower().TrimStart('.');
+ if (string.Equals(codec, "txt", StringComparison.OrdinalIgnoreCase))
+ {
+ codec = "srt";
+ }
+
// If the subtitle file matches the video file name
if (string.Equals(videoFileNameWithoutExtension, fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase))
{
@@ -72,9 +79,9 @@ namespace MediaBrowser.Providers.MediaInfo
// 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) ||
+ .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));
if (culture != null)
@@ -117,7 +124,7 @@ namespace MediaBrowser.Providers.MediaInfo
{
get
{
- return new[] { ".srt", ".ssa", ".ass", ".sub", ".smi", ".sami" };
+ return new[] { ".srt", ".ssa", ".ass", ".sub", ".smi", ".sami", ".txt" };
}
}
@@ -136,7 +143,7 @@ namespace MediaBrowser.Providers.MediaInfo
return files.Where(i =>
{
- if (!i.Attributes.HasFlag(FileAttributes.Directory) &&
+ if (!i.IsDirectory &&
SubtitleExtensions.Contains(i.Extension, StringComparer.OrdinalIgnoreCase))
{
var fileNameWithoutExtension = fileSystem.GetFileNameWithoutExtension(i);
diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
index 45d1c3d80f..ca701b70f6 100644
--- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
@@ -13,7 +13,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
namespace MediaBrowser.Providers.MediaInfo
{
diff --git a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
index 2a40e4d85e..dd2cad1f94 100644
--- a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
+++ b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
@@ -19,7 +19,9 @@ using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Providers.TV;
@@ -116,7 +118,7 @@ namespace MediaBrowser.Providers.Movies
{
// No biggie. Don't blow up
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
// No biggie. Don't blow up
}
@@ -220,8 +222,7 @@ namespace MediaBrowser.Providers.Movies
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
- Url = url,
- ResourcePool = FanartArtistProvider.Current.FanArtResourcePool
+ Url = url
});
}
@@ -284,11 +285,12 @@ namespace MediaBrowser.Providers.Movies
{
Url = url,
ResourcePool = FanartArtistProvider.Current.FanArtResourcePool,
- CancellationToken = cancellationToken
+ CancellationToken = cancellationToken,
+ BufferContent = true
}).ConfigureAwait(false))
{
- using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true))
+ using (var fileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
{
await response.CopyToAsync(fileStream).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs
index 82a2dfbe94..6788bdc9cf 100644
--- a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs
+++ b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs
@@ -13,7 +13,9 @@ using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Providers.Movies
@@ -24,16 +26,16 @@ namespace MediaBrowser.Providers.Movies
private readonly ILogger _logger;
private readonly IJsonSerializer _jsonSerializer;
private readonly ILibraryManager _libraryManager;
- private readonly IFileSystem _fileSystem;
+ private readonly IFileSystem _fileSystem;
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- public GenericMovieDbInfo(ILogger logger, IJsonSerializer jsonSerializer, ILibraryManager libraryManager, IFileSystem fileSystem)
+ public GenericMovieDbInfo(ILogger logger, IJsonSerializer jsonSerializer, ILibraryManager libraryManager, IFileSystem fileSystem)
{
_logger = logger;
_jsonSerializer = jsonSerializer;
_libraryManager = libraryManager;
- _fileSystem = fileSystem;
+ _fileSystem = fileSystem;
}
public async Task<MetadataResult<T>> GetMetadata(ItemLookupInfo itemId, CancellationToken cancellationToken)
@@ -269,6 +271,8 @@ namespace MediaBrowser.Providers.Movies
//and the rest from crew
if (movieData.casts != null && movieData.casts.crew != null)
{
+ var keepTypes = new[] { PersonType.Director, PersonType.Writer, PersonType.Producer };
+
foreach (var person in movieData.casts.crew)
{
// Normalize this
@@ -278,6 +282,12 @@ namespace MediaBrowser.Providers.Movies
type = PersonType.Writer;
}
+ if (!keepTypes.Contains(type ?? string.Empty, StringComparer.OrdinalIgnoreCase) &&
+ !keepTypes.Contains(person.job ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
var personInfo = new PersonInfo
{
Name = person.name.Trim(),
diff --git a/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs b/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs
index 49f341f26f..59b4b5198b 100644
--- a/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs
+++ b/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs
@@ -14,6 +14,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Movies
{
@@ -21,11 +22,13 @@ namespace MediaBrowser.Providers.Movies
{
private readonly IJsonSerializer _jsonSerializer;
private readonly IHttpClient _httpClient;
+ private readonly IFileSystem _fileSystem;
- public MovieDbImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient)
+ public MovieDbImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem)
{
_jsonSerializer = jsonSerializer;
_httpClient = httpClient;
+ _fileSystem = fileSystem;
}
public string Name
@@ -196,7 +199,7 @@ namespace MediaBrowser.Providers.Movies
if (!string.IsNullOrEmpty(path))
{
- var fileInfo = new FileInfo(path);
+ var fileInfo = _fileSystem.GetFileInfo(path);
if (fileInfo.Exists)
{
@@ -217,8 +220,7 @@ namespace MediaBrowser.Providers.Movies
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
- Url = url,
- ResourcePool = MovieDbProvider.Current.MovieDbResourcePool
+ Url = url
});
}
}
diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs
index b0d119ff98..8e4b86519b 100644
--- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs
+++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs
@@ -4,7 +4,6 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
@@ -17,8 +16,11 @@ using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Common;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Net;
namespace MediaBrowser.Providers.Movies
@@ -432,6 +434,7 @@ namespace MediaBrowser.Providers.Movies
options.ResourcePool = MovieDbResourcePool;
_lastRequestTicks = DateTime.UtcNow.Ticks;
+ options.BufferContent = true;
options.UserAgent = "Emby/" + _appHost.ApplicationVersion;
return await _httpClient.Get(options).ConfigureAwait(false);
@@ -655,8 +658,7 @@ namespace MediaBrowser.Providers.Movies
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
- Url = url,
- ResourcePool = MovieDbResourcePool
+ Url = url
});
}
}
diff --git a/MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs b/MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs
index 81df7b805c..36297914d4 100644
--- a/MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs
+++ b/MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs
@@ -46,8 +46,7 @@ namespace MediaBrowser.Providers.Movies
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
- Url = url,
- ResourcePool = MovieDbProvider.Current.MovieDbResourcePool
+ Url = url
});
}
}
diff --git a/MediaBrowser.Providers/Movies/MovieMetadataService.cs b/MediaBrowser.Providers/Movies/MovieMetadataService.cs
index 83be9ca6f9..2187d6df13 100644
--- a/MediaBrowser.Providers/Movies/MovieMetadataService.cs
+++ b/MediaBrowser.Providers/Movies/MovieMetadataService.cs
@@ -6,8 +6,10 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.IO;
namespace MediaBrowser.Providers.Movies
{
diff --git a/MediaBrowser.Providers/Music/AlbumMetadataService.cs b/MediaBrowser.Providers/Music/AlbumMetadataService.cs
index 4f87b2036b..e5dbba9022 100644
--- a/MediaBrowser.Providers/Music/AlbumMetadataService.cs
+++ b/MediaBrowser.Providers/Music/AlbumMetadataService.cs
@@ -9,7 +9,9 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Music
{
diff --git a/MediaBrowser.Providers/Music/ArtistMetadataService.cs b/MediaBrowser.Providers/Music/ArtistMetadataService.cs
index b2f975b131..1bd427ac9c 100644
--- a/MediaBrowser.Providers/Music/ArtistMetadataService.cs
+++ b/MediaBrowser.Providers/Music/ArtistMetadataService.cs
@@ -9,7 +9,9 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Music
{
diff --git a/MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs b/MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs
index efaa730b24..c13d19c436 100644
--- a/MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs
+++ b/MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs
@@ -87,8 +87,7 @@ namespace MediaBrowser.Providers.Music
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
- Url = url,
- ResourcePool = AudioDbArtistProvider.Current.AudioDbResourcePool
+ Url = url
});
}
diff --git a/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs b/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs
index 5963e90e2e..3f969b609a 100644
--- a/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs
+++ b/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs
@@ -13,7 +13,9 @@ using System.Globalization;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Music
{
@@ -162,12 +164,11 @@ namespace MediaBrowser.Providers.Music
using (var response = await _httpClient.Get(new HttpRequestOptions
{
Url = url,
- ResourcePool = AudioDbArtistProvider.Current.AudioDbResourcePool,
CancellationToken = cancellationToken
}).ConfigureAwait(false))
{
- using (var xmlFileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true))
+ using (var xmlFileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
{
await response.CopyToAsync(xmlFileStream).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs b/MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs
index 2fcc14c832..6ca1d83d8a 100644
--- a/MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs
+++ b/MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs
@@ -129,8 +129,7 @@ namespace MediaBrowser.Providers.Music
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
- Url = url,
- ResourcePool = AudioDbArtistProvider.Current.AudioDbResourcePool
+ Url = url
});
}
diff --git a/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs b/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs
index 8bcb012282..0f0c31e6e9 100644
--- a/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs
+++ b/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs
@@ -12,7 +12,9 @@ using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Music
{
@@ -150,13 +152,14 @@ namespace MediaBrowser.Providers.Music
{
Url = url,
ResourcePool = AudioDbResourcePool,
- CancellationToken = cancellationToken
+ CancellationToken = cancellationToken,
+ BufferContent = true
}).ConfigureAwait(false))
{
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
- using (var xmlFileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true))
+ using (var xmlFileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
{
await response.CopyToAsync(xmlFileStream).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Providers/Music/AudioMetadataService.cs b/MediaBrowser.Providers/Music/AudioMetadataService.cs
index 67ddd89812..faa8bb3ad7 100644
--- a/MediaBrowser.Providers/Music/AudioMetadataService.cs
+++ b/MediaBrowser.Providers/Music/AudioMetadataService.cs
@@ -7,7 +7,9 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
using System.Linq;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Music
{
diff --git a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
index 2f09d64af0..d22929664c 100644
--- a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
+++ b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
@@ -14,7 +14,9 @@ using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Providers.Music
@@ -92,7 +94,7 @@ namespace MediaBrowser.Providers.Music
{
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
}
@@ -209,8 +211,7 @@ namespace MediaBrowser.Providers.Music
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
- Url = url,
- ResourcePool = FanartArtistProvider.Current.FanArtResourcePool
+ Url = url
});
}
}
diff --git a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs
index 6afa80507e..6fd0d82bdc 100644
--- a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs
+++ b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs
@@ -17,7 +17,9 @@ using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Serialization;
@@ -96,7 +98,7 @@ namespace MediaBrowser.Providers.Music
{
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
}
@@ -204,8 +206,7 @@ namespace MediaBrowser.Providers.Music
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
- Url = url,
- ResourcePool = FanArtResourcePool
+ Url = url
});
}
@@ -255,11 +256,12 @@ namespace MediaBrowser.Providers.Music
{
Url = url,
ResourcePool = FanArtResourcePool,
- CancellationToken = cancellationToken
+ CancellationToken = cancellationToken,
+ BufferContent = true
}).ConfigureAwait(false))
{
- using (var saveFileStream = _fileSystem.GetFileStream(jsonPath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
+ using (var saveFileStream = _fileSystem.GetFileStream(jsonPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
{
await response.CopyToAsync(saveFileStream).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
index 9ed8f0a007..ec31824db9 100644
--- a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
+++ b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
@@ -7,6 +7,7 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Providers;
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
@@ -15,6 +16,7 @@ using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.Providers.Music
{
@@ -26,15 +28,17 @@ namespace MediaBrowser.Providers.Music
private readonly IApplicationHost _appHost;
private readonly ILogger _logger;
private readonly IJsonSerializer _json;
+ private readonly IXmlReaderSettingsFactory _xmlSettings;
public static string MusicBrainzBaseUrl = "https://www.musicbrainz.org";
- public MusicBrainzAlbumProvider(IHttpClient httpClient, IApplicationHost appHost, ILogger logger, IJsonSerializer json)
+ public MusicBrainzAlbumProvider(IHttpClient httpClient, IApplicationHost appHost, ILogger logger, IJsonSerializer json, IXmlReaderSettingsFactory xmlSettings)
{
_httpClient = httpClient;
_appHost = appHost;
_logger = logger;
_json = json;
+ _xmlSettings = xmlSettings;
Current = this;
}
@@ -71,35 +75,50 @@ namespace MediaBrowser.Providers.Music
if (!string.IsNullOrWhiteSpace(url))
{
- var doc = await GetMusicBrainzResponse(url, isNameSearch, cancellationToken).ConfigureAwait(false);
-
- return GetResultsFromResponse(doc);
+ using (var stream = await GetMusicBrainzResponse(url, isNameSearch, cancellationToken).ConfigureAwait(false))
+ {
+ return GetResultsFromResponse(stream);
+ }
}
return new List<RemoteSearchResult>();
}
- private IEnumerable<RemoteSearchResult> GetResultsFromResponse(XmlDocument doc)
+ private List<RemoteSearchResult> GetResultsFromResponse(Stream stream)
{
- return ReleaseResult.Parse(doc).Select(i =>
+ using (var oReader = new StreamReader(stream, Encoding.UTF8))
{
- var result = new RemoteSearchResult
- {
- Name = i.Title,
- ProductionYear = i.Year
- };
+ var settings = _xmlSettings.Create(false);
- if (!string.IsNullOrWhiteSpace(i.ReleaseId))
- {
- result.SetProviderId(MetadataProviders.MusicBrainzAlbum, i.ReleaseId);
- }
- if (!string.IsNullOrWhiteSpace(i.ReleaseGroupId))
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
+
+ using (var reader = XmlReader.Create(oReader, settings))
{
- result.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, i.ReleaseGroupId);
- }
+ var results = ReleaseResult.Parse(reader);
- return result;
- });
+ return results.Select(i =>
+ {
+ var result = new RemoteSearchResult
+ {
+ Name = i.Title,
+ ProductionYear = i.Year
+ };
+
+ if (!string.IsNullOrWhiteSpace(i.ReleaseId))
+ {
+ result.SetProviderId(MetadataProviders.MusicBrainzAlbum, i.ReleaseId);
+ }
+ if (!string.IsNullOrWhiteSpace(i.ReleaseGroupId))
+ {
+ result.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, i.ReleaseGroupId);
+ }
+
+ return result;
+ }).ToList();
+ }
+ }
}
public async Task<MetadataResult<MusicAlbum>> GetMetadata(AlbumInfo id, CancellationToken cancellationToken)
@@ -191,9 +210,22 @@ namespace MediaBrowser.Providers.Music
WebUtility.UrlEncode(albumName),
artistId);
- var doc = await GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false);
+ using (var stream = await GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false))
+ {
+ using (var oReader = new StreamReader(stream, Encoding.UTF8))
+ {
+ var settings = _xmlSettings.Create(false);
+
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
- return ReleaseResult.Parse(doc, 1).FirstOrDefault();
+ using (var reader = XmlReader.Create(oReader, settings))
+ {
+ return ReleaseResult.Parse(reader).FirstOrDefault();
+ }
+ }
+ }
}
private async Task<ReleaseResult> GetReleaseResultByArtistName(string albumName, string artistName, CancellationToken cancellationToken)
@@ -202,9 +234,22 @@ namespace MediaBrowser.Providers.Music
WebUtility.UrlEncode(albumName),
WebUtility.UrlEncode(artistName));
- var doc = await GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false);
+ using (var stream = await GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false))
+ {
+ using (var oReader = new StreamReader(stream, Encoding.UTF8))
+ {
+ var settings = _xmlSettings.Create(false);
+
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
- return ReleaseResult.Parse(doc, 1).FirstOrDefault();
+ using (var reader = XmlReader.Create(oReader, settings))
+ {
+ return ReleaseResult.Parse(reader).FirstOrDefault();
+ }
+ }
+ }
}
private class ReleaseResult
@@ -215,108 +260,154 @@ namespace MediaBrowser.Providers.Music
public string Overview;
public int? Year;
- public static List<ReleaseResult> Parse(XmlDocument doc, int? limit = null)
+ public static List<ReleaseResult> Parse(XmlReader reader)
{
- var docElem = doc.DocumentElement;
- var list = new List<ReleaseResult>();
-
- if (docElem == null)
- {
- return list;
- }
-
- var releaseList = docElem.FirstChild;
- if (releaseList == null)
- {
- return list;
- }
+ reader.MoveToContent();
+ reader.Read();
- var nodes = releaseList.ChildNodes;
-
- if (nodes != null)
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
- foreach (var node in nodes.Cast<XmlNode>())
+ if (reader.NodeType == XmlNodeType.Element)
{
- if (string.Equals(node.Name, "release", StringComparison.OrdinalIgnoreCase))
+ switch (reader.Name)
{
- var releaseId = node.Attributes["id"].Value;
- var releaseGroupId = GetReleaseGroupIdFromReleaseNode(node);
-
- list.Add(new ReleaseResult
- {
- ReleaseId = releaseId,
- ReleaseGroupId = releaseGroupId,
- Title = GetValueFromReleaseNode(node, "title"),
- Overview = GetValueFromReleaseNode(node, "annotation"),
- Year = GetYearFromReleaseNode(node, "date")
- });
-
- if (limit.HasValue && list.Count >= limit.Value)
- {
- break;
- }
+ case "release-list":
+ {
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ continue;
+ }
+ using (var subReader = reader.ReadSubtree())
+ {
+ return ParseReleaseList(subReader);
+ }
+ }
+ default:
+ {
+ reader.Skip();
+ break;
+ }
}
}
+ else
+ {
+ reader.Read();
+ }
}
- return list;
+ return new List<ReleaseResult>();
}
- private static int? GetYearFromReleaseNode(XmlNode node, string name)
+ private static List<ReleaseResult> ParseReleaseList(XmlReader reader)
{
- var subNodes = node.ChildNodes;
- if (subNodes != null)
+ var list = new List<ReleaseResult>();
+
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
- foreach (var subNode in subNodes.Cast<XmlNode>())
+ if (reader.NodeType == XmlNodeType.Element)
{
- if (string.Equals(subNode.Name, name, StringComparison.OrdinalIgnoreCase))
+ switch (reader.Name)
{
- DateTime date;
- if (DateTime.TryParse(subNode.InnerText, out date))
- {
- return date.Year;
- }
-
- return null;
+ case "release":
+ {
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ continue;
+ }
+ var releaseId = reader.GetAttribute("id");
+
+ using (var subReader = reader.ReadSubtree())
+ {
+ var release = ParseRelease(subReader, releaseId);
+ if (release != null)
+ {
+ list.Add(release);
+ }
+ }
+ break;
+ }
+ default:
+ {
+ reader.Skip();
+ break;
+ }
}
}
+ else
+ {
+ reader.Read();
+ }
}
- return null;
+ return list;
}
- private static string GetValueFromReleaseNode(XmlNode node, string name)
+ private static ReleaseResult ParseRelease(XmlReader reader, string releaseId)
{
- var subNodes = node.ChildNodes;
- if (subNodes != null)
+ var result = new ReleaseResult
{
- foreach (var subNode in subNodes.Cast<XmlNode>())
- {
- if (string.Equals(subNode.Name, name, StringComparison.OrdinalIgnoreCase))
- {
- return subNode.InnerText;
- }
- }
- }
+ ReleaseId = releaseId
+ };
- return null;
- }
+ reader.MoveToContent();
+ reader.Read();
- private static string GetReleaseGroupIdFromReleaseNode(XmlNode node)
- {
- var subNodes = node.ChildNodes;
- if (subNodes != null)
+ // http://stackoverflow.com/questions/2299632/why-does-xmlreader-skip-every-other-element-if-there-is-no-whitespace-separator
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
- foreach (var subNode in subNodes.Cast<XmlNode>())
+ if (reader.NodeType == XmlNodeType.Element)
{
- if (string.Equals(subNode.Name, "release-group", StringComparison.OrdinalIgnoreCase))
+ switch (reader.Name)
{
- return subNode.Attributes["id"].Value;
+ case "title":
+ {
+ result.Title = reader.ReadElementContentAsString();
+ break;
+ }
+ case "date":
+ {
+ var val = reader.ReadElementContentAsString();
+ DateTime date;
+ if (DateTime.TryParse(val, out date))
+ {
+ result.Year = date.Year;
+ }
+ break;
+ }
+ case "annotation":
+ {
+ result.Overview = reader.ReadElementContentAsString();
+ break;
+ }
+ case "release-group":
+ {
+ result.ReleaseGroupId = reader.GetAttribute("id");
+ reader.Skip();
+ break;
+ }
+ default:
+ {
+ reader.Skip();
+ break;
+ }
}
}
+ else
+ {
+ reader.Read();
+ }
}
- return null;
+ return result;
}
}
@@ -330,33 +421,87 @@ namespace MediaBrowser.Providers.Music
{
var url = string.Format("/ws/2/release-group/?query=reid:{0}", releaseEntryId);
- var doc = await GetMusicBrainzResponse(url, false, cancellationToken).ConfigureAwait(false);
+ using (var stream = await GetMusicBrainzResponse(url, false, cancellationToken).ConfigureAwait(false))
+ {
+ using (var oReader = new StreamReader(stream, Encoding.UTF8))
+ {
+ var settings = _xmlSettings.Create(false);
- var docElem = doc.DocumentElement;
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
- if (docElem == null)
- {
- return null;
- }
+ using (var reader = XmlReader.Create(oReader, settings))
+ {
+ reader.MoveToContent();
+ reader.Read();
- var releaseList = docElem.FirstChild;
- if (releaseList == null)
- {
- return null;
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "release-group-list":
+ {
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ continue;
+ }
+ using (var subReader = reader.ReadSubtree())
+ {
+ return GetFirstReleaseGroupId(subReader);
+ }
+ }
+ default:
+ {
+ reader.Skip();
+ break;
+ }
+ }
+ }
+ else
+ {
+ reader.Read();
+ }
+ }
+ return null;
+ }
+ }
}
+ }
- var nodes = releaseList.ChildNodes;
+ private string GetFirstReleaseGroupId(XmlReader reader)
+ {
+ reader.MoveToContent();
+ reader.Read();
- if (nodes != null)
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
- foreach (var node in nodes.Cast<XmlNode>())
+ if (reader.NodeType == XmlNodeType.Element)
{
- if (string.Equals(node.Name, "release-group", StringComparison.OrdinalIgnoreCase))
+ switch (reader.Name)
{
- return node.Attributes["id"].Value;
+ case "release-group":
+ {
+ return reader.GetAttribute("id");
+ }
+ default:
+ {
+ reader.Skip();
+ break;
+ }
}
}
+ else
+ {
+ reader.Read();
+ }
}
+
return null;
}
@@ -402,7 +547,9 @@ namespace MediaBrowser.Providers.Music
using (var stream = await _httpClient.Get(options).ConfigureAwait(false))
{
- list = _json.DeserializeFromStream<List<MbzUrl>>(stream);
+ var results = _json.DeserializeFromStream<List<MbzUrl>>(stream);
+
+ list = results;
}
_lastMbzUrlQueryTicks = DateTime.UtcNow.Ticks;
}
@@ -432,37 +579,30 @@ namespace MediaBrowser.Providers.Music
/// <param name="isSearch">if set to <c>true</c> [is search].</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{XmlDocument}.</returns>
- internal async Task<XmlDocument> GetMusicBrainzResponse(string url, bool isSearch, CancellationToken cancellationToken)
+ internal async Task<Stream> GetMusicBrainzResponse(string url, bool isSearch, CancellationToken cancellationToken)
{
var urlInfo = await GetMbzUrl().ConfigureAwait(false);
+ var throttleMs = urlInfo.throttleMs;
- if (urlInfo.throttleMs > 0)
+ if (throttleMs > 0)
{
// MusicBrainz is extremely adamant about limiting to one request per second
- await Task.Delay(urlInfo.throttleMs, cancellationToken).ConfigureAwait(false);
+ _logger.Debug("Throttling MusicBrainz by {0}ms", throttleMs.ToString(CultureInfo.InvariantCulture));
+ await Task.Delay(throttleMs, cancellationToken).ConfigureAwait(false);
}
url = urlInfo.url.TrimEnd('/') + url;
- var doc = new XmlDocument();
-
var options = new HttpRequestOptions
{
Url = url,
CancellationToken = cancellationToken,
UserAgent = _appHost.Name + "/" + _appHost.ApplicationVersion,
- ResourcePool = _musicBrainzResourcePool
+ ResourcePool = _musicBrainzResourcePool,
+ BufferContent = throttleMs > 0
};
- using (var xml = await _httpClient.Get(options).ConfigureAwait(false))
- {
- using (var oReader = new StreamReader(xml, Encoding.UTF8))
- {
- doc.Load(oReader);
- }
- }
-
- return doc;
+ return await _httpClient.Get(options).ConfigureAwait(false);
}
public int Order
diff --git a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs
index 88128bdc7a..1a2b13e946 100644
--- a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs
+++ b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs
@@ -6,17 +6,27 @@ using MediaBrowser.Model.Providers;
using System;
using System.Collections.Generic;
using System.Globalization;
+using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
+using MediaBrowser.Controller.Extensions;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.Providers.Music
{
public class MusicBrainzArtistProvider : IRemoteMetadataProvider<MusicArtist, ArtistInfo>
{
+ private readonly IXmlReaderSettingsFactory _xmlSettings;
+
+ public MusicBrainzArtistProvider(IXmlReaderSettingsFactory xmlSettings)
+ {
+ _xmlSettings = xmlSettings;
+ }
+
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken)
{
var musicBrainzId = searchInfo.GetMusicBrainzArtistId();
@@ -25,10 +35,11 @@ namespace MediaBrowser.Providers.Music
{
var url = string.Format("/ws/2/artist/?query=arid:{0}", musicBrainzId);
- var doc = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, false, cancellationToken)
- .ConfigureAwait(false);
-
- return GetResultsFromResponse(doc);
+ using (var stream = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, false, cancellationToken)
+ .ConfigureAwait(false))
+ {
+ return GetResultsFromResponse(stream);
+ }
}
else
{
@@ -37,13 +48,14 @@ namespace MediaBrowser.Providers.Music
var url = String.Format("/ws/2/artist/?query=artist:\"{0}\"", UrlEncode(nameToSearch));
- var doc = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false);
-
- var results = GetResultsFromResponse(doc).ToList();
-
- if (results.Count > 0)
+ using (var stream = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false))
{
- return results;
+ var results = GetResultsFromResponse(stream).ToList();
+
+ if (results.Count > 0)
+ {
+ return results;
+ }
}
if (HasDiacritics(searchInfo.Name))
@@ -51,73 +63,165 @@ namespace MediaBrowser.Providers.Music
// Try again using the search with accent characters url
url = String.Format("/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch));
- doc = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false);
-
- return GetResultsFromResponse(doc);
+ using (var stream = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false))
+ {
+ return GetResultsFromResponse(stream);
+ }
}
}
return new List<RemoteSearchResult>();
}
- private IEnumerable<RemoteSearchResult> GetResultsFromResponse(XmlDocument doc)
+ private IEnumerable<RemoteSearchResult> GetResultsFromResponse(Stream stream)
{
- var list = new List<RemoteSearchResult>();
+ using (var oReader = new StreamReader(stream, Encoding.UTF8))
+ {
+ var settings = _xmlSettings.Create(false);
- var docElem = doc.DocumentElement;
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
- if (docElem == null)
- {
- return list;
- }
+ using (var reader = XmlReader.Create(oReader, settings))
+ {
+ reader.MoveToContent();
+ reader.Read();
- var artistList = docElem.FirstChild;
- if (artistList == null)
- {
- return list;
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "artist-list":
+ {
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ continue;
+ }
+ using (var subReader = reader.ReadSubtree())
+ {
+ return ParseArtistList(subReader);
+ }
+ }
+ default:
+ {
+ reader.Skip();
+ break;
+ }
+ }
+ }
+ else
+ {
+ reader.Read();
+ }
+ }
+
+ return new List<RemoteSearchResult>();
+ }
}
+ }
- var nodes = artistList.ChildNodes;
+ private IEnumerable<RemoteSearchResult> ParseArtistList(XmlReader reader)
+ {
+ var list = new List<RemoteSearchResult>();
- if (nodes != null)
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
- foreach (var node in nodes.Cast<XmlNode>())
+ if (reader.NodeType == XmlNodeType.Element)
{
- if (node.Attributes != null)
+ switch (reader.Name)
{
- string name = null;
- string overview = null;
- string mbzId = node.Attributes["id"].Value;
-
- foreach (var child in node.ChildNodes.Cast<XmlNode>())
- {
- if (string.Equals(child.Name, "name", StringComparison.OrdinalIgnoreCase))
+ case "artist":
{
- name = child.InnerText;
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ continue;
+ }
+ var mbzId = reader.GetAttribute("id");
+
+ using (var subReader = reader.ReadSubtree())
+ {
+ var artist = ParseArtist(subReader, mbzId);
+ if (artist != null)
+ {
+ list.Add(artist);
+ }
+ }
+ break;
}
- if (string.Equals(child.Name, "annotation", StringComparison.OrdinalIgnoreCase))
+ default:
{
- overview = child.InnerText;
+ reader.Skip();
+ break;
}
- }
+ }
+ }
+ else
+ {
+ reader.Read();
+ }
+ }
- if (!string.IsNullOrWhiteSpace(mbzId) && !string.IsNullOrWhiteSpace(name))
- {
- var result = new RemoteSearchResult
- {
- Name = name,
- Overview = overview
- };
+ return list;
+ }
- result.SetProviderId(MetadataProviders.MusicBrainzArtist, mbzId);
+ private RemoteSearchResult ParseArtist(XmlReader reader, string artistId)
+ {
+ var result = new RemoteSearchResult();
- list.Add(result);
- }
+ reader.MoveToContent();
+ reader.Read();
+
+ // http://stackoverflow.com/questions/2299632/why-does-xmlreader-skip-every-other-element-if-there-is-no-whitespace-separator
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "name":
+ {
+ result.Name = reader.ReadElementContentAsString();
+ break;
+ }
+ case "annotation":
+ {
+ result.Overview = reader.ReadElementContentAsString();
+ break;
+ }
+ default:
+ {
+ // there is sort-name if ever needed
+ reader.Skip();
+ break;
+ }
}
}
+ else
+ {
+ reader.Read();
+ }
}
- return list;
+ result.SetProviderId(MetadataProviders.MusicBrainzArtist, artistId);
+
+ if (string.IsNullOrWhiteSpace(artistId) || string.IsNullOrWhiteSpace(result.Name))
+ {
+ return null;
+ }
+
+ return result;
}
public async Task<MetadataResult<MusicArtist>> GetMetadata(ArtistInfo id, CancellationToken cancellationToken)
@@ -159,21 +263,7 @@ namespace MediaBrowser.Providers.Music
/// <returns><c>true</c> if the specified text has diacritics; otherwise, <c>false</c>.</returns>
private bool HasDiacritics(string text)
{
- return !String.Equals(text, RemoveDiacritics(text), StringComparison.Ordinal);
- }
-
- /// <summary>
- /// Removes the diacritics.
- /// </summary>
- /// <param name="text">The text.</param>
- /// <returns>System.String.</returns>
- private string RemoveDiacritics(string text)
- {
- return String.Concat(
- text.Normalize(NormalizationForm.FormD)
- .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) !=
- UnicodeCategory.NonSpacingMark)
- ).Normalize(NormalizationForm.FormC);
+ return !String.Equals(text, text.RemoveDiacritics(), StringComparison.Ordinal);
}
/// <summary>
diff --git a/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs b/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs
index a50c077212..f46b6b128b 100644
--- a/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs
+++ b/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs
@@ -7,7 +7,9 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
using System.Linq;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Music
{
diff --git a/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs b/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs
index d7b96271b7..f1ffd09db7 100644
--- a/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs
+++ b/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs
@@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.MusicGenres
{
diff --git a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs b/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs
index 90d172bc2c..812f0443ea 100644
--- a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs
+++ b/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs
@@ -1,4 +1,4 @@
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@@ -12,6 +12,8 @@ using MediaBrowser.Model.Serialization;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
namespace MediaBrowser.Providers.Omdb
{
@@ -68,9 +70,7 @@ namespace MediaBrowser.Providers.Omdb
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
- Url = url,
- ResourcePool = OmdbProvider.ResourcePool
-
+ Url = url
});
}
diff --git a/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs
index 428bde2f28..db551b7638 100644
--- a/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs
+++ b/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs
@@ -1,4 +1,4 @@
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@@ -18,6 +18,8 @@ using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
namespace MediaBrowser.Providers.Omdb
{
@@ -129,7 +131,8 @@ namespace MediaBrowser.Providers.Omdb
{
Url = url,
ResourcePool = OmdbProvider.ResourcePool,
- CancellationToken = cancellationToken
+ CancellationToken = cancellationToken,
+ BufferContent = true
}).ConfigureAwait(false))
{
@@ -294,8 +297,7 @@ namespace MediaBrowser.Providers.Omdb
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
- Url = url,
- ResourcePool = OmdbProvider.ResourcePool
+ Url = url
});
}
diff --git a/MediaBrowser.Providers/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Omdb/OmdbProvider.cs
index 9c7a16fab8..721e31b07e 100644
--- a/MediaBrowser.Providers/Omdb/OmdbProvider.cs
+++ b/MediaBrowser.Providers/Omdb/OmdbProvider.cs
@@ -1,4 +1,4 @@
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@@ -14,6 +14,8 @@ using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
namespace MediaBrowser.Providers.Omdb
{
@@ -35,7 +37,7 @@ namespace MediaBrowser.Providers.Omdb
}
public async Task Fetch<T>(MetadataResult<T> itemResult, string imdbId, string language, string country, CancellationToken cancellationToken)
- where T :BaseItem
+ where T : BaseItem
{
if (string.IsNullOrWhiteSpace(imdbId))
{
@@ -46,25 +48,25 @@ namespace MediaBrowser.Providers.Omdb
var result = await GetRootObject(imdbId, cancellationToken);
- // Only take the name and rating if the user's language is set to english, since Omdb has no localization
- if (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase))
- {
- item.Name = result.Title;
+ // Only take the name and rating if the user's language is set to english, since Omdb has no localization
+ if (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase))
+ {
+ item.Name = result.Title;
- if (string.Equals(country, "us", StringComparison.OrdinalIgnoreCase))
- {
- item.OfficialRating = result.Rated;
- }
+ if (string.Equals(country, "us", StringComparison.OrdinalIgnoreCase))
+ {
+ item.OfficialRating = result.Rated;
}
+ }
- int year;
+ int year;
- if (!string.IsNullOrEmpty(result.Year) && result.Year.Length >= 4
- && int.TryParse(result.Year.Substring(0, 4), NumberStyles.Number, _usCulture, out year)
- && year >= 0)
- {
- item.ProductionYear = year;
- }
+ if (!string.IsNullOrEmpty(result.Year) && result.Year.Length >= 4
+ && int.TryParse(result.Year.Substring(0, 4), NumberStyles.Number, _usCulture, out year)
+ && year >= 0)
+ {
+ item.ProductionYear = year;
+ }
// Seeing some bogus RT data on omdb for series, so filter it out here
// RT doesn't even have tv series
@@ -85,33 +87,33 @@ namespace MediaBrowser.Providers.Omdb
int voteCount;
- if (!string.IsNullOrEmpty(result.imdbVotes)
- && int.TryParse(result.imdbVotes, NumberStyles.Number, _usCulture, out voteCount)
- && voteCount >= 0)
- {
- item.VoteCount = voteCount;
- }
+ if (!string.IsNullOrEmpty(result.imdbVotes)
+ && int.TryParse(result.imdbVotes, NumberStyles.Number, _usCulture, out voteCount)
+ && voteCount >= 0)
+ {
+ item.VoteCount = voteCount;
+ }
- float imdbRating;
+ float imdbRating;
- if (!string.IsNullOrEmpty(result.imdbRating)
- && float.TryParse(result.imdbRating, NumberStyles.Any, _usCulture, out imdbRating)
- && imdbRating >= 0)
- {
- item.CommunityRating = imdbRating;
- }
+ if (!string.IsNullOrEmpty(result.imdbRating)
+ && float.TryParse(result.imdbRating, NumberStyles.Any, _usCulture, out imdbRating)
+ && imdbRating >= 0)
+ {
+ item.CommunityRating = imdbRating;
+ }
- if (!string.IsNullOrEmpty(result.Website))
- {
- item.HomePageUrl = result.Website;
- }
+ if (!string.IsNullOrEmpty(result.Website))
+ {
+ item.HomePageUrl = result.Website;
+ }
- if (!string.IsNullOrWhiteSpace(result.imdbID))
- {
- item.SetProviderId(MetadataProviders.Imdb, result.imdbID);
- }
+ if (!string.IsNullOrWhiteSpace(result.imdbID))
+ {
+ item.SetProviderId(MetadataProviders.Imdb, result.imdbID);
+ }
- ParseAdditionalMetadata(itemResult, result);
+ ParseAdditionalMetadata(itemResult, result);
}
public async Task<bool> FetchEpisodeData<T>(MetadataResult<T> itemResult, int episodeNumber, int seasonNumber, string imdbId, string language, string country, CancellationToken cancellationToken)
@@ -133,7 +135,7 @@ namespace MediaBrowser.Providers.Omdb
RootObject result = null;
- foreach (var episode in seasonResult.Episodes)
+ foreach (var episode in (seasonResult.Episodes ?? new RootObject[] { }))
{
if (episode.Episode == episodeNumber)
{
@@ -223,7 +225,7 @@ namespace MediaBrowser.Providers.Omdb
string resultString;
- using (Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 131072))
+ using (Stream stream = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
{
using (var reader = new StreamReader(stream, new UTF8Encoding(false)))
{
@@ -242,7 +244,7 @@ namespace MediaBrowser.Providers.Omdb
string resultString;
- using (Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 131072))
+ using (Stream stream = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
{
using (var reader = new StreamReader(stream, new UTF8Encoding(false)))
{
@@ -298,7 +300,8 @@ namespace MediaBrowser.Providers.Omdb
{
Url = url,
ResourcePool = ResourcePool,
- CancellationToken = cancellationToken
+ CancellationToken = cancellationToken,
+ BufferContent = true
}).ConfigureAwait(false))
{
@@ -338,7 +341,8 @@ namespace MediaBrowser.Providers.Omdb
{
Url = url,
ResourcePool = ResourcePool,
- CancellationToken = cancellationToken
+ CancellationToken = cancellationToken,
+ BufferContent = true
}).ConfigureAwait(false))
{
diff --git a/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs b/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs
index 93eee69ae7..3728137902 100644
--- a/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs
+++ b/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs
@@ -135,8 +135,7 @@ namespace MediaBrowser.Providers.People
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
- Url = url,
- ResourcePool = MovieDbProvider.Current.MovieDbResourcePool
+ Url = url
});
}
}
diff --git a/MediaBrowser.Providers/People/MovieDbPersonProvider.cs b/MediaBrowser.Providers/People/MovieDbPersonProvider.cs
index c954e6323a..3645a5f8df 100644
--- a/MediaBrowser.Providers/People/MovieDbPersonProvider.cs
+++ b/MediaBrowser.Providers/People/MovieDbPersonProvider.cs
@@ -16,7 +16,9 @@ using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
@@ -80,7 +82,7 @@ namespace MediaBrowser.Providers.People
};
result.SetProviderId(MetadataProviders.Tmdb, info.id.ToString(_usCulture));
- result.SetProviderId(MetadataProviders.Imdb, info.imdb_id.ToString(_usCulture));
+ result.SetProviderId(MetadataProviders.Imdb, info.imdb_id);
return new[] { result };
}
@@ -250,7 +252,7 @@ namespace MediaBrowser.Providers.People
{
_fileSystem.CreateDirectory(Path.GetDirectoryName(dataFilePath));
- using (var fs = _fileSystem.GetFileStream(dataFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
+ using (var fs = _fileSystem.GetFileStream(dataFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
{
await json.CopyToAsync(fs).ConfigureAwait(false);
}
@@ -406,8 +408,7 @@ namespace MediaBrowser.Providers.People
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
- Url = url,
- ResourcePool = MovieDbProvider.Current.MovieDbResourcePool
+ Url = url
});
}
}
diff --git a/MediaBrowser.Providers/People/PersonMetadataService.cs b/MediaBrowser.Providers/People/PersonMetadataService.cs
index 0f8bd8b662..a306212ffd 100644
--- a/MediaBrowser.Providers/People/PersonMetadataService.cs
+++ b/MediaBrowser.Providers/People/PersonMetadataService.cs
@@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.People
{
diff --git a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs
index 2d4d484e60..dd3bfb4f82 100644
--- a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs
+++ b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs
@@ -15,6 +15,8 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.Providers.People
{
@@ -23,12 +25,16 @@ namespace MediaBrowser.Providers.People
private readonly IServerConfigurationManager _config;
private readonly ILibraryManager _libraryManager;
private readonly IHttpClient _httpClient;
+ private readonly IFileSystem _fileSystem;
+ private readonly IXmlReaderSettingsFactory _xmlSettings;
- public TvdbPersonImageProvider(IServerConfigurationManager config, ILibraryManager libraryManager, IHttpClient httpClient)
+ public TvdbPersonImageProvider(IServerConfigurationManager config, ILibraryManager libraryManager, IHttpClient httpClient, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlSettings)
{
_config = config;
_libraryManager = libraryManager;
_httpClient = httpClient;
+ _fileSystem = fileSystem;
+ _xmlSettings = xmlSettings;
}
public string Name
@@ -56,13 +62,10 @@ namespace MediaBrowser.Providers.People
public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
{
- // Avoid implicitly captured closure
- var itemName = item.Name;
-
var seriesWithPerson = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = new[] { typeof(Series).Name },
- Person = itemName
+ PersonIds = new[] { item.Id.ToString("N") }
}).Cast<Series>()
.Where(i => TvdbSeriesProvider.IsValidSeries(i.ProviderIds))
@@ -89,7 +92,7 @@ namespace MediaBrowser.Providers.People
{
return null;
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
return null;
}
@@ -97,46 +100,57 @@ namespace MediaBrowser.Providers.People
private RemoteImageInfo GetImageInfo(string xmlFile, string personName, CancellationToken cancellationToken)
{
- var settings = new XmlReaderSettings
- {
- CheckCharacters = false,
- IgnoreProcessingInstructions = true,
- IgnoreComments = true,
- ValidationType = ValidationType.None
- };
+ var settings = _xmlSettings.Create(false);
- using (var streamReader = new StreamReader(xmlFile, Encoding.UTF8))
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
+
+ using (var fileStream = _fileSystem.GetFileStream(xmlFile, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
{
- // Use XmlReader for best performance
- using (var reader = XmlReader.Create(streamReader, settings))
+ using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
{
- reader.MoveToContent();
-
- // Loop through each element
- while (reader.Read())
+ // Use XmlReader for best performance
+ using (var reader = XmlReader.Create(streamReader, settings))
{
- cancellationToken.ThrowIfCancellationRequested();
+ reader.MoveToContent();
+ reader.Read();
- if (reader.NodeType == XmlNodeType.Element)
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
- switch (reader.Name)
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (reader.NodeType == XmlNodeType.Element)
{
- case "Actor":
- {
- using (var subtree = reader.ReadSubtree())
+ switch (reader.Name)
+ {
+ case "Actor":
{
- var info = FetchImageInfoFromActorNode(personName, subtree);
-
- if (info != null)
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ continue;
+ }
+ using (var subtree = reader.ReadSubtree())
{
- return info;
+ var info = FetchImageInfoFromActorNode(personName, subtree);
+
+ if (info != null)
+ {
+ return info;
+ }
}
+ break;
}
+ default:
+ reader.Skip();
break;
- }
- default:
- reader.Skip();
- break;
+ }
+ }
+ else
+ {
+ reader.Read();
}
}
}
@@ -154,12 +168,14 @@ namespace MediaBrowser.Providers.People
/// <returns>System.String.</returns>
private RemoteImageInfo FetchImageInfoFromActorNode(string personName, XmlReader reader)
{
- reader.MoveToContent();
-
string name = null;
string image = null;
- while (reader.Read())
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -182,6 +198,10 @@ namespace MediaBrowser.Providers.People
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(image) &&
@@ -209,8 +229,7 @@ namespace MediaBrowser.Providers.People
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
- Url = url,
- ResourcePool = TvdbSeriesProvider.Current.TvDbResourcePool
+ Url = url
});
}
}
diff --git a/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs b/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs
index 9bfff6b842..b64b2f24e2 100644
--- a/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs
+++ b/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs
@@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Photos
{
diff --git a/MediaBrowser.Providers/Photos/PhotoHelper.cs b/MediaBrowser.Providers/Photos/PhotoHelper.cs
deleted file mode 100644
index 2334c792e7..0000000000
--- a/MediaBrowser.Providers/Photos/PhotoHelper.cs
+++ /dev/null
@@ -1,98 +0,0 @@
-using System;
-using System.Text;
-
-namespace MediaBrowser.Providers.Photos
-{
- public static class PhotoHelper
- {
- public static string Dec2Frac(double dbl)
- {
- char neg = ' ';
- double dblDecimal = dbl;
- if (dblDecimal == (int)dblDecimal) return dblDecimal.ToString(); //return no if it's not a decimal
- if (dblDecimal < 0)
- {
- dblDecimal = Math.Abs(dblDecimal);
- neg = '-';
- }
- var whole = (int)Math.Truncate(dblDecimal);
- string decpart = dblDecimal.ToString().Replace(Math.Truncate(dblDecimal) + ".", "");
- double rN = Convert.ToDouble(decpart);
- double rD = Math.Pow(10, decpart.Length);
-
- string rd = Recur(decpart);
- int rel = Convert.ToInt32(rd);
- if (rel != 0)
- {
- rN = rel;
- rD = (int)Math.Pow(10, rd.Length) - 1;
- }
- //just a few prime factors for testing purposes
- var primes = new[] { 47, 43, 37, 31, 29, 23, 19, 17, 13, 11, 7, 5, 3, 2 };
- foreach (int i in primes) ReduceNo(i, ref rD, ref rN);
-
- rN = rN + (whole * rD);
- return string.Format("{0}{1}/{2}", neg, rN, rD);
- }
-
- /// <summary>
- /// Finds out the recurring decimal in a specified number
- /// </summary>
- /// <param name="db">Number to check</param>
- /// <returns></returns>
- private static string Recur(string db)
- {
- if (db.Length < 13) return "0";
- var sb = new StringBuilder();
- for (int i = 0; i < 7; i++)
- {
- sb.Append(db[i]);
- int dlength = (db.Length / sb.ToString().Length);
- int occur = Occurence(sb.ToString(), db);
- if (dlength == occur || dlength == occur - sb.ToString().Length)
- {
- return sb.ToString();
- }
- }
- return "0";
- }
-
- /// <summary>
- /// Checks for number of occurence of specified no in a number
- /// </summary>
- /// <param name="s">The no to check occurence times</param>
- /// <param name="check">The number where to check this</param>
- /// <returns></returns>
- private static int Occurence(string s, string check)
- {
- int i = 0;
- int d = s.Length;
- string ds = check;
- for (int n = (ds.Length / d); n > 0; n--)
- {
- if (ds.Contains(s))
- {
- i++;
- ds = ds.Remove(ds.IndexOf(s, System.StringComparison.Ordinal), d);
- }
- }
- return i;
- }
-
- /// <summary>
- /// Reduces a fraction given the numerator and denominator
- /// </summary>
- /// <param name="i">Number to use in an attempt to reduce fraction</param>
- /// <param name="rD">the Denominator</param>
- /// <param name="rN">the Numerator</param>
- private static void ReduceNo(int i, ref double rD, ref double rN)
- {
- //keep reducing until divisibility ends
- while ((rD % i) < 1e-10 && (rN % i) < 1e-10)
- {
- rN = rN / i;
- rD = rD / i;
- }
- }
- }
-}
diff --git a/MediaBrowser.Providers/Photos/PhotoMetadataService.cs b/MediaBrowser.Providers/Photos/PhotoMetadataService.cs
index fb6ff6f09d..617a510b89 100644
--- a/MediaBrowser.Providers/Photos/PhotoMetadataService.cs
+++ b/MediaBrowser.Providers/Photos/PhotoMetadataService.cs
@@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Photos
{
diff --git a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
index d1d5ba7de4..cfdba0fb2f 100644
--- a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
+++ b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
@@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Playlists
{
diff --git a/MediaBrowser.Providers/Studios/StudioMetadataService.cs b/MediaBrowser.Providers/Studios/StudioMetadataService.cs
index 9ee594a662..c773b78968 100644
--- a/MediaBrowser.Providers/Studios/StudioMetadataService.cs
+++ b/MediaBrowser.Providers/Studios/StudioMetadataService.cs
@@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Studios
{
diff --git a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
index bfb7eb8fdd..bf017d1481 100644
--- a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
+++ b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
@@ -11,7 +11,9 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Studios
{
@@ -88,7 +90,7 @@ namespace MediaBrowser.Providers.Studios
private RemoteImageInfo GetImage(IHasImages item, string filename, ImageType type, string remoteFilename)
{
- var list = ImageUtils.GetAvailableImages(filename);
+ var list = ImageUtils.GetAvailableImages(filename, _fileSystem);
var match = ImageUtils.FindMatch(item, list);
diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
index a57e7d2c0c..cd741bed52 100644
--- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
+++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
@@ -16,7 +16,9 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Subtitles
{
@@ -131,7 +133,7 @@ namespace MediaBrowser.Providers.Subtitles
{
//var isText = MediaStream.IsTextFormat(response.Format);
- using (var fs = _fileSystem.GetFileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
+ using (var fs = _fileSystem.GetFileStream(savePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
{
await stream.CopyToAsync(fs).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Providers/TV/DummySeasonProvider.cs b/MediaBrowser.Providers/TV/DummySeasonProvider.cs
index b74eac219e..f9f2479998 100644
--- a/MediaBrowser.Providers/TV/DummySeasonProvider.cs
+++ b/MediaBrowser.Providers/TV/DummySeasonProvider.cs
@@ -1,7 +1,6 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
@@ -9,7 +8,10 @@ using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Globalization;
namespace MediaBrowser.Providers.TV
{
diff --git a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs
index 90a185ce00..538d96c176 100644
--- a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs
+++ b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs
@@ -8,7 +8,9 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.TV
{
@@ -18,40 +20,46 @@ namespace MediaBrowser.Providers.TV
{
var updateType = await base.BeforeSave(item, isFullRefresh, currentUpdateType).ConfigureAwait(false);
- if (updateType <= ItemUpdateType.None)
+ var seriesName = item.FindSeriesName();
+ if (!string.Equals(item.SeriesName, seriesName, StringComparison.Ordinal))
{
- if (!string.Equals(item.SeriesName, item.FindSeriesName(), StringComparison.Ordinal))
- {
- updateType |= ItemUpdateType.MetadataImport;
- }
+ item.SeriesName = seriesName;
+ updateType |= ItemUpdateType.MetadataImport;
}
- if (updateType <= ItemUpdateType.None)
+
+ var seriesSortName = item.FindSeriesSortName();
+ if (!string.Equals(item.SeriesSortName, seriesSortName, StringComparison.Ordinal))
{
- if (!string.Equals(item.SeriesSortName, item.FindSeriesSortName(), StringComparison.Ordinal))
- {
- updateType |= ItemUpdateType.MetadataImport;
- }
+ item.SeriesSortName = seriesSortName;
+ updateType |= ItemUpdateType.MetadataImport;
}
- if (updateType <= ItemUpdateType.None)
+
+ var seasonName = item.FindSeasonName();
+ if (!string.Equals(item.SeasonName, seasonName, StringComparison.Ordinal))
{
- if (!string.Equals(item.SeasonName, item.FindSeasonName(), StringComparison.Ordinal))
- {
- updateType |= ItemUpdateType.MetadataImport;
- }
+ item.SeasonName = seasonName;
+ updateType |= ItemUpdateType.MetadataImport;
}
- if (updateType <= ItemUpdateType.None)
+
+ var seriesId = item.FindSeriesId();
+ if (item.SeriesId != seriesId)
{
- if (item.SeriesId != item.FindSeriesId())
- {
- updateType |= ItemUpdateType.MetadataImport;
- }
+ item.SeriesId = seriesId;
+ updateType |= ItemUpdateType.MetadataImport;
}
- if (updateType <= ItemUpdateType.None)
+
+ var seasonId = item.FindSeasonId();
+ if (item.SeasonId != seasonId)
+ {
+ item.SeasonId = seasonId;
+ updateType |= ItemUpdateType.MetadataImport;
+ }
+
+ var seriesPresentationUniqueKey = item.FindSeriesPresentationUniqueKey();
+ if (!string.Equals(item.SeriesPresentationUniqueKey, seriesPresentationUniqueKey, StringComparison.Ordinal))
{
- if (item.SeasonId != item.FindSeasonId())
- {
- updateType |= ItemUpdateType.MetadataImport;
- }
+ item.SeriesPresentationUniqueKey = seriesPresentationUniqueKey;
+ updateType |= ItemUpdateType.MetadataImport;
}
return updateType;
diff --git a/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs b/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs
index e6a47d9d2f..884fa297f5 100644
--- a/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs
+++ b/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs
@@ -18,7 +18,9 @@ using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.TV
{
@@ -101,7 +103,7 @@ namespace MediaBrowser.Providers.TV
{
// No biggie. Don't blow up
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
// No biggie. Don't blow up
}
@@ -222,8 +224,7 @@ namespace MediaBrowser.Providers.TV
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
- Url = url,
- ResourcePool = FanartArtistProvider.Current.FanArtResourcePool
+ Url = url
});
}
}
diff --git a/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs b/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs
index 827a3c50ff..8db3eaa79f 100644
--- a/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs
+++ b/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs
@@ -20,7 +20,9 @@ using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.TV
{
@@ -108,7 +110,7 @@ namespace MediaBrowser.Providers.TV
{
// No biggie. Don't blow up
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
// No biggie. Don't blow up
}
@@ -222,8 +224,7 @@ namespace MediaBrowser.Providers.TV
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
- Url = url,
- ResourcePool = FanartArtistProvider.Current.FanArtResourcePool
+ Url = url
});
}
@@ -319,11 +320,12 @@ namespace MediaBrowser.Providers.TV
{
Url = url,
ResourcePool = FanartArtistProvider.Current.FanArtResourcePool,
- CancellationToken = cancellationToken
+ CancellationToken = cancellationToken,
+ BufferContent = true
}).ConfigureAwait(false))
{
- using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true))
+ using (var fileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
{
await response.CopyToAsync(fileStream).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
index 9c212e8a0a..4992675da2 100644
--- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
@@ -1,7 +1,6 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
@@ -14,11 +13,15 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.Providers.TV
{
- class MissingEpisodeProvider
+ public class MissingEpisodeProvider
{
private readonly IServerConfigurationManager _config;
private readonly ILogger _logger;
@@ -27,21 +30,23 @@ 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);
+ 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)
+ public MissingEpisodeProvider(ILogger logger, IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlSettings)
{
_logger = logger;
_config = config;
_libraryManager = libraryManager;
_localization = localization;
_fileSystem = fileSystem;
+ _xmlSettings = xmlSettings;
}
public async Task Run(List<IGrouping<string, Series>> series, bool addNewItems, CancellationToken cancellationToken)
{
- await _resourceLock.WaitAsync(cancellationToken).ConfigureAwait(false);
+ await ResourceLock.WaitAsync(cancellationToken).ConfigureAwait(false);
IsRunning = true;
foreach (var seriesGroup in series)
@@ -54,9 +59,10 @@ namespace MediaBrowser.Providers.TV
{
break;
}
- catch (DirectoryNotFoundException)
+ 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)
{
@@ -65,12 +71,15 @@ namespace MediaBrowser.Providers.TV
}
IsRunning = false;
- _resourceLock.Release();
+ ResourceLock.Release();
}
private async Task Run(IGrouping<string, Series> group, bool addNewItems, CancellationToken cancellationToken)
{
- var tvdbId = group.Key;
+ var seriesList = group.ToList();
+ var tvdbId = seriesList
+ .Select(i => i.GetProviderId(MetadataProviders.Tvdb))
+ .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
// Todo: Support series by imdb id
var seriesProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
@@ -78,7 +87,8 @@ namespace MediaBrowser.Providers.TV
var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds);
- var episodeFiles = Directory.EnumerateFiles(seriesDataPath, "*.xml", SearchOption.TopDirectoryOnly)
+ var episodeFiles = _fileSystem.GetFilePaths(seriesDataPath)
+ .Where(i => string.Equals(Path.GetExtension(i), ".xml", StringComparison.OrdinalIgnoreCase))
.Select(Path.GetFileNameWithoutExtension)
.Where(i => i.StartsWith("episode-", StringComparison.OrdinalIgnoreCase))
.ToList();
@@ -108,30 +118,33 @@ namespace MediaBrowser.Providers.TV
.Where(i => i.Item1 != -1 && i.Item2 != -1)
.ToList();
- var hasBadData = HasInvalidContent(group);
+ var hasBadData = HasInvalidContent(seriesList);
+
+ // Be conservative here to avoid creating missing episodes for ones they already have
+ var addMissingEpisodes = !hasBadData && seriesList.All(i => _libraryManager.GetLibraryOptions(i).ImportMissingEpisodes);
- var anySeasonsRemoved = await RemoveObsoleteOrMissingSeasons(group, episodeLookup)
+ var anySeasonsRemoved = await RemoveObsoleteOrMissingSeasons(seriesList, episodeLookup)
.ConfigureAwait(false);
- var anyEpisodesRemoved = await RemoveObsoleteOrMissingEpisodes(group, episodeLookup)
+ var anyEpisodesRemoved = await RemoveObsoleteOrMissingEpisodes(seriesList, episodeLookup, addMissingEpisodes)
.ConfigureAwait(false);
var hasNewEpisodes = false;
- if (addNewItems && !group.Any(i => !i.IsInternetMetadataEnabled()))
+ if (addNewItems && seriesList.All(i => i.IsInternetMetadataEnabled()))
{
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))
{
- hasNewEpisodes = await AddMissingEpisodes(group.ToList(), hasBadData, seriesDataPath, episodeLookup, cancellationToken)
+ hasNewEpisodes = await AddMissingEpisodes(seriesList, addMissingEpisodes, seriesDataPath, episodeLookup, cancellationToken)
.ConfigureAwait(false);
}
}
if (hasNewEpisodes || anySeasonsRemoved || anyEpisodesRemoved)
{
- foreach (var series in group)
+ foreach (var series in seriesList)
{
var directoryService = new DirectoryService(_logger, _fileSystem);
@@ -170,13 +183,9 @@ namespace MediaBrowser.Providers.TV
/// Adds the missing episodes.
/// </summary>
/// <param name="series">The series.</param>
- /// <param name="seriesHasBadData">if set to <c>true</c> [series has bad data].</param>
- /// <param name="seriesDataPath">The series data path.</param>
- /// <param name="episodeLookup">The episode lookup.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
private async Task<bool> AddMissingEpisodes(List<Series> series,
- bool seriesHasBadData,
+ bool addMissingEpisodes,
string seriesDataPath,
IEnumerable<Tuple<int, int>> episodeLookup,
CancellationToken cancellationToken)
@@ -223,15 +232,18 @@ namespace MediaBrowser.Providers.TV
{
continue;
}
+
var now = DateTime.UtcNow;
var targetSeries = DetermineAppropriateSeries(series, tuple.Item1);
var seasonOffset = TvdbSeriesProvider.GetSeriesOffset(targetSeries.ProviderIds) ?? ((targetSeries.AnimeSeriesIndex ?? 1) - 1);
+ var unairedThresholdDays = 1;
+ now = now.AddDays(0 - unairedThresholdDays);
+
if (airDate.Value < now)
{
- // Be conservative here to avoid creating missing episodes for ones they already have
- if (!seriesHasBadData)
+ 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);
@@ -268,7 +280,8 @@ namespace MediaBrowser.Providers.TV
/// 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)
+ IEnumerable<Tuple<int, int>> episodeLookup,
+ bool allowMissingEpisodes)
{
var existingEpisodes = (from s in series
let seasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1)
@@ -306,6 +319,11 @@ namespace MediaBrowser.Providers.TV
return true;
}
+ if (!allowMissingEpisodes && i.Episode.IsMissingEpisode)
+ {
+ return true;
+ }
+
return false;
}
@@ -362,7 +380,7 @@ namespace MediaBrowser.Providers.TV
var seasonNumber = i.Season.IndexNumber.Value + i.SeasonOffset;
// If there's a physical season with the same number, delete it
- if (physicalSeasons.Any(p => p.Season.IndexNumber.HasValue && (p.Season.IndexNumber.Value + p.SeasonOffset) == seasonNumber))
+ if (physicalSeasons.Any(p => p.Season.IndexNumber.HasValue && (p.Season.IndexNumber.Value + p.SeasonOffset) == seasonNumber && string.Equals(p.Season.Series.PresentationUniqueKey, i.Season.Series.PresentationUniqueKey, StringComparison.Ordinal)))
{
return true;
}
@@ -492,56 +510,65 @@ namespace MediaBrowser.Providers.TV
DateTime? airDate = null;
- // It appears the best way to filter out invalid entries is to only include those with valid air dates
- using (var streamReader = new StreamReader(xmlPath, Encoding.UTF8))
+ using (var fileStream = _fileSystem.GetFileStream(xmlPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
{
- // Use XmlReader for best performance
- using (var reader = XmlReader.Create(streamReader, new XmlReaderSettings
- {
- CheckCharacters = false,
- IgnoreProcessingInstructions = true,
- IgnoreComments = true,
- ValidationType = ValidationType.None
- }))
+ // It appears the best way to filter out invalid entries is to only include those with valid air dates
+ using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
{
- reader.MoveToContent();
-
- // Loop through each element
- while (reader.Read())
+ var settings = _xmlSettings.Create(false);
+
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
+
+ // Use XmlReader for best performance
+ using (var reader = XmlReader.Create(streamReader, settings))
{
- if (reader.NodeType == XmlNodeType.Element)
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
- switch (reader.Name)
+ if (reader.NodeType == XmlNodeType.Element)
{
- case "EpisodeName":
- {
- var val = reader.ReadElementContentAsString();
- if (string.IsNullOrWhiteSpace(val))
+ switch (reader.Name)
+ {
+ case "EpisodeName":
{
- // Not valid, ignore these
- return null;
- }
- break;
- }
- case "FirstAired":
- {
- var val = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(val))
- {
- DateTime date;
- if (DateTime.TryParse(val, out date))
+ var val = reader.ReadElementContentAsString();
+ if (string.IsNullOrWhiteSpace(val))
{
- airDate = date.ToUniversalTime();
+ // Not valid, ignore these
+ return null;
}
+ break;
}
+ case "FirstAired":
+ {
+ var val = reader.ReadElementContentAsString();
- break;
- }
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ DateTime date;
+ if (DateTime.TryParse(val, out date))
+ {
+ airDate = date.ToUniversalTime();
+ }
+ }
- default:
- reader.Skip();
- break;
+ break;
+ }
+ default:
+ {
+ reader.Skip();
+ break;
+ }
+ }
+ }
+ else
+ {
+ reader.Read();
}
}
}
diff --git a/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs
index 78bce241f4..21e327a8f2 100644
--- a/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs
@@ -1,4 +1,4 @@
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV;
@@ -12,6 +12,8 @@ using MediaBrowser.Providers.Omdb;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
namespace MediaBrowser.Providers.TV
{
@@ -55,7 +57,7 @@ namespace MediaBrowser.Providers.TV
if (OmdbProvider.IsValidSeries(info.SeriesProviderIds) && info.IndexNumber.HasValue && info.ParentIndexNumber.HasValue)
{
- var seriesImdbId = info.SeriesProviderIds[MetadataProviders.Imdb.ToString()];
+ var seriesImdbId = info.GetProviderId(MetadataProviders.Imdb);
result.HasMetadata = await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager).FetchEpisodeData(result, info.IndexNumber.Value, info.ParentIndexNumber.Value, seriesImdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Providers/TV/SeasonMetadataService.cs b/MediaBrowser.Providers/TV/SeasonMetadataService.cs
index cf04a14184..af7dea59ee 100644
--- a/MediaBrowser.Providers/TV/SeasonMetadataService.cs
+++ b/MediaBrowser.Providers/TV/SeasonMetadataService.cs
@@ -9,7 +9,9 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.TV
{
@@ -35,16 +37,31 @@ namespace MediaBrowser.Providers.TV
updateType |= SaveIsVirtualItem(item, episodes);
}
- if (!string.Equals(item.SeriesName, item.FindSeriesName(), StringComparison.Ordinal))
+ var seriesName = item.FindSeriesName();
+ if (!string.Equals(item.SeriesName, seriesName, StringComparison.Ordinal))
{
+ item.SeriesName = seriesName;
updateType |= ItemUpdateType.MetadataImport;
}
- if (!string.Equals(item.SeriesSortName, item.FindSeriesSortName(), StringComparison.Ordinal))
+
+ var seriesSortName = item.FindSeriesSortName();
+ if (!string.Equals(item.SeriesSortName, seriesSortName, StringComparison.Ordinal))
+ {
+ item.SeriesSortName = seriesSortName;
+ updateType |= ItemUpdateType.MetadataImport;
+ }
+
+ var seriesPresentationUniqueKey = item.FindSeriesPresentationUniqueKey();
+ if (!string.Equals(item.SeriesPresentationUniqueKey, seriesPresentationUniqueKey, StringComparison.Ordinal))
{
+ item.SeriesPresentationUniqueKey = seriesPresentationUniqueKey;
updateType |= ItemUpdateType.MetadataImport;
}
- if (item.SeriesId != item.FindSeriesId())
+
+ var seriesId = item.FindSeriesId();
+ if (item.SeriesId != seriesId)
{
+ item.SeriesId = seriesId;
updateType |= ItemUpdateType.MetadataImport;
}
diff --git a/MediaBrowser.Providers/TV/SeriesMetadataService.cs b/MediaBrowser.Providers/TV/SeriesMetadataService.cs
index f440baf5b4..0d89e307f9 100644
--- a/MediaBrowser.Providers/TV/SeriesMetadataService.cs
+++ b/MediaBrowser.Providers/TV/SeriesMetadataService.cs
@@ -1,7 +1,6 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
@@ -10,7 +9,10 @@ using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Globalization;
namespace MediaBrowser.Providers.TV
{
diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeImageProvider.cs
index 0feb92e897..952c4f9349 100644
--- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeImageProvider.cs
+++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeImageProvider.cs
@@ -1,8 +1,7 @@
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@@ -15,6 +14,9 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.Globalization;
namespace MediaBrowser.Providers.TV
{
diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs
index 748124c033..fcd7532648 100644
--- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs
@@ -1,9 +1,9 @@
-using CommonIO;
+using System;
+using MediaBrowser.Model.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
@@ -16,6 +16,9 @@ using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.Globalization;
namespace MediaBrowser.Providers.TV
{
@@ -164,9 +167,24 @@ namespace MediaBrowser.Providers.TV
//and the rest from crew
if (credits.crew != null)
{
+ var keepTypes = new[] { PersonType.Director, PersonType.Writer, PersonType.Producer };
+
foreach (var person in credits.crew)
{
- result.AddPerson(new PersonInfo { Name = person.name.Trim(), Role = person.job, Type = person.department });
+ // Normalize this
+ var type = person.department;
+ if (string.Equals(type, "writing", StringComparison.OrdinalIgnoreCase))
+ {
+ type = PersonType.Writer;
+ }
+
+ if (!keepTypes.Contains(type ?? string.Empty, StringComparer.OrdinalIgnoreCase) &&
+ !keepTypes.Contains(person.job ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
+ result.AddPerson(new PersonInfo { Name = person.name.Trim(), Role = person.job, Type = type });
}
}
}
diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs
index 821c26e4be..38831feb6a 100644
--- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs
+++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs
@@ -1,7 +1,6 @@
-using CommonIO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Providers.Movies;
@@ -11,6 +10,9 @@ using System.Globalization;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.Globalization;
namespace MediaBrowser.Providers.TV
{
diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs
index 194af5b99b..1f0cc9e521 100644
--- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs
+++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs
@@ -1,7 +1,6 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
@@ -16,7 +15,10 @@ using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Globalization;
namespace MediaBrowser.Providers.TV
{
@@ -119,8 +121,7 @@ namespace MediaBrowser.Providers.TV
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
- Url = url,
- ResourcePool = MovieDbProvider.Current.MovieDbResourcePool
+ Url = url
});
}
diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesImageProvider.cs
index ad46db6773..c746e04880 100644
--- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesImageProvider.cs
+++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesImageProvider.cs
@@ -13,6 +13,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.TV
{
@@ -20,11 +21,13 @@ namespace MediaBrowser.Providers.TV
{
private readonly IJsonSerializer _jsonSerializer;
private readonly IHttpClient _httpClient;
+ private readonly IFileSystem _fileSystem;
- public MovieDbSeriesImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient)
+ public MovieDbSeriesImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem)
{
_jsonSerializer = jsonSerializer;
_httpClient = httpClient;
+ _fileSystem = fileSystem;
}
public string Name
@@ -166,7 +169,7 @@ namespace MediaBrowser.Providers.TV
if (!string.IsNullOrEmpty(path))
{
- var fileInfo = new FileInfo(path);
+ var fileInfo = _fileSystem.GetFileInfo(path);
if (fileInfo.Exists)
{
@@ -191,8 +194,7 @@ namespace MediaBrowser.Providers.TV
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
- Url = url,
- ResourcePool = MovieDbProvider.Current.MovieDbResourcePool
+ Url = url
});
}
}
diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs
index fb06780293..5b4ae9745a 100644
--- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs
+++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs
@@ -4,7 +4,6 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
@@ -18,7 +17,10 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Globalization;
namespace MediaBrowser.Providers.TV
{
@@ -654,8 +656,7 @@ namespace MediaBrowser.Providers.TV
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
- Url = url,
- ResourcePool = MovieDbProvider.Current.MovieDbResourcePool
+ Url = url
});
}
}
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs
index a0280cf409..791f56beb7 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs
@@ -12,7 +12,9 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.TV
{
@@ -80,67 +82,73 @@ namespace MediaBrowser.Providers.TV
// Use XmlReader for best performance
using (reader)
{
- reader.MoveToContent();
-
- // Loop through each element
- while (reader.Read())
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- if (reader.NodeType == XmlNodeType.Element)
- {
- switch (reader.Name)
- {
- case "thumb_width":
- {
- 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))
- {
- width = rval;
- }
- }
- break;
- }
-
- case "thumb_height":
- {
- 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))
- {
- height = rval;
- }
- }
- break;
- }
-
- case "filename":
- {
- var val = reader.ReadElementContentAsString();
- if (!string.IsNullOrWhiteSpace(val))
- {
- url = TVUtils.BannerUrl + val;
- }
- break;
- }
-
- default:
- reader.Skip();
- break;
- }
- }
- }
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ switch (reader.Name)
+ {
+ case "thumb_width":
+ {
+ 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))
+ {
+ width = rval;
+ }
+ }
+ break;
+ }
+
+ case "thumb_height":
+ {
+ 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))
+ {
+ height = rval;
+ }
+ }
+ break;
+ }
+
+ case "filename":
+ {
+ var val = reader.ReadElementContentAsString();
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ url = TVUtils.BannerUrl + val;
+ }
+ break;
+ }
+ default:
+ {
+ reader.Skip();
+ break;
+ }
+ }
+ }
+ else
+ {
+ reader.Read();
+ }
+ }
}
if (string.IsNullOrEmpty(url))
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs
index 41a2282d80..99136cd858 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs
@@ -16,7 +16,10 @@ using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.Providers.TV
{
@@ -33,13 +36,15 @@ namespace MediaBrowser.Providers.TV
private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient;
private readonly ILogger _logger;
+ private readonly IXmlReaderSettingsFactory _xmlSettings;
- public TvdbEpisodeProvider(IFileSystem fileSystem, IServerConfigurationManager config, IHttpClient httpClient, ILogger logger)
+ public TvdbEpisodeProvider(IFileSystem fileSystem, IServerConfigurationManager config, IHttpClient httpClient, ILogger logger, IXmlReaderSettingsFactory xmlSettings)
{
_fileSystem = fileSystem;
_config = config;
_httpClient = httpClient;
_logger = logger;
+ _xmlSettings = xmlSettings;
Current = this;
}
@@ -47,11 +52,11 @@ namespace MediaBrowser.Providers.TV
{
var list = new List<RemoteSearchResult>();
- // The search query must either provide an episode number or date
- if (!searchInfo.IndexNumber.HasValue && !searchInfo.PremiereDate.HasValue)
- {
- return list;
- }
+ // The search query must either provide an episode number or date
+ if (!searchInfo.IndexNumber.HasValue && !searchInfo.PremiereDate.HasValue)
+ {
+ return list;
+ }
if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds))
{
@@ -59,12 +64,13 @@ namespace MediaBrowser.Providers.TV
var searchNumbers = new EpisodeNumbers();
- if (searchInfo.IndexNumber.HasValue) {
- searchNumbers.EpisodeNumber = searchInfo.IndexNumber.Value;
- }
-
+ if (searchInfo.IndexNumber.HasValue)
+ {
+ searchNumbers.EpisodeNumber = searchInfo.IndexNumber.Value;
+ }
+
searchNumbers.SeasonNumber = searchInfo.ParentIndexNumber;
- searchNumbers.EpisodeNumberEnd = searchInfo.IndexNumberEnd ?? searchNumbers.EpisodeNumber;
+ searchNumbers.EpisodeNumberEnd = searchInfo.IndexNumberEnd ?? searchNumbers.EpisodeNumber;
try
{
@@ -91,7 +97,7 @@ namespace MediaBrowser.Providers.TV
{
// Don't fail the provider because this will just keep on going and going.
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
// Don't fail the provider because this will just keep on going and going.
}
@@ -110,19 +116,20 @@ namespace MediaBrowser.Providers.TV
var result = new MetadataResult<Episode>();
result.QueriedById = true;
- if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds) &&
- (searchInfo.IndexNumber.HasValue || searchInfo.PremiereDate.HasValue))
+ if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds) &&
+ (searchInfo.IndexNumber.HasValue || searchInfo.PremiereDate.HasValue))
{
await TvdbSeriesProvider.Current.EnsureSeriesInfo(searchInfo.SeriesProviderIds, searchInfo.MetadataLanguage, cancellationToken).ConfigureAwait(false);
var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, searchInfo.SeriesProviderIds);
var searchNumbers = new EpisodeNumbers();
- if (searchInfo.IndexNumber.HasValue) {
- searchNumbers.EpisodeNumber = searchInfo.IndexNumber.Value;
- }
+ if (searchInfo.IndexNumber.HasValue)
+ {
+ searchNumbers.EpisodeNumber = searchInfo.IndexNumber.Value;
+ }
searchNumbers.SeasonNumber = searchInfo.ParentIndexNumber;
- searchNumbers.EpisodeNumberEnd = searchInfo.IndexNumberEnd ?? searchNumbers.EpisodeNumber;
+ searchNumbers.EpisodeNumberEnd = searchInfo.IndexNumberEnd ?? searchNumbers.EpisodeNumber;
try
{
@@ -132,7 +139,7 @@ namespace MediaBrowser.Providers.TV
{
// Don't fail the provider because this will just keep on going and going.
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
// Don't fail the provider because this will just keep on going and going.
}
@@ -153,20 +160,20 @@ namespace MediaBrowser.Providers.TV
/// <returns>List{FileInfo}.</returns>
internal List<XmlReader> GetEpisodeXmlNodes(string seriesDataPath, EpisodeInfo searchInfo)
{
- var seriesXmlPath = TvdbSeriesProvider.Current.GetSeriesXmlPath (searchInfo.SeriesProviderIds, searchInfo.MetadataLanguage);
-
- try
- {
- return GetXmlNodes(seriesXmlPath, searchInfo);
- }
- catch (DirectoryNotFoundException)
- {
- return new List<XmlReader> ();
- }
- catch (FileNotFoundException)
- {
- return new List<XmlReader> ();
- }
+ var seriesXmlPath = TvdbSeriesProvider.Current.GetSeriesXmlPath(searchInfo.SeriesProviderIds, searchInfo.MetadataLanguage);
+
+ try
+ {
+ return GetXmlNodes(seriesXmlPath, searchInfo);
+ }
+ catch (FileNotFoundException)
+ {
+ return new List<XmlReader>();
+ }
+ catch (IOException)
+ {
+ return new List<XmlReader>();
+ }
}
private class EpisodeNumbers
@@ -186,59 +193,62 @@ namespace MediaBrowser.Providers.TV
/// <returns>Task{System.Boolean}.</returns>
private MetadataResult<Episode> FetchEpisodeData(EpisodeInfo id, EpisodeNumbers searchNumbers, string seriesDataPath, CancellationToken cancellationToken)
{
- var result = new MetadataResult<Episode>()
- {
- Item = new Episode
- {
- IndexNumber = id.IndexNumber,
- ParentIndexNumber = id.ParentIndexNumber,
- IndexNumberEnd = id.IndexNumberEnd
- }
- };
+ var result = new MetadataResult<Episode>()
+ {
+ Item = new Episode
+ {
+ IndexNumber = id.IndexNumber,
+ ParentIndexNumber = id.ParentIndexNumber,
+ IndexNumberEnd = id.IndexNumberEnd
+ }
+ };
- var xmlNodes = GetEpisodeXmlNodes (seriesDataPath, id);
+ var xmlNodes = GetEpisodeXmlNodes(seriesDataPath, id);
- if (xmlNodes.Count > 0) {
- FetchMainEpisodeInfo(result, xmlNodes[0], cancellationToken);
+ if (xmlNodes.Count > 0)
+ {
+ FetchMainEpisodeInfo(result, xmlNodes[0], cancellationToken);
- result.HasMetadata = true;
- }
+ result.HasMetadata = true;
+ }
- foreach (var node in xmlNodes.Skip(1)) {
- FetchAdditionalPartInfo(result, node, cancellationToken);
- }
+ foreach (var node in xmlNodes.Skip(1))
+ {
+ FetchAdditionalPartInfo(result, node, cancellationToken);
+ }
return result;
}
- private List<XmlReader> GetXmlNodes(string xmlFile, EpisodeInfo searchInfo)
- {
- var list = new List<XmlReader> ();
+ private List<XmlReader> GetXmlNodes(string xmlFile, EpisodeInfo searchInfo)
+ {
+ var list = new List<XmlReader>();
- if (searchInfo.IndexNumber.HasValue)
- {
- var files = GetEpisodeXmlFiles (searchInfo.ParentIndexNumber, searchInfo.IndexNumber, searchInfo.IndexNumberEnd, Path.GetDirectoryName (xmlFile));
+ if (searchInfo.IndexNumber.HasValue)
+ {
+ var files = GetEpisodeXmlFiles(searchInfo.ParentIndexNumber, searchInfo.IndexNumber, searchInfo.IndexNumberEnd, Path.GetDirectoryName(xmlFile));
- list = files.Select (GetXmlReader).ToList ();
- }
+ list = files.Select(GetXmlReader).ToList();
+ }
- if (list.Count == 0 && searchInfo.PremiereDate.HasValue) {
- list = GetXmlNodesByPremiereDate (xmlFile, searchInfo.PremiereDate.Value);
- }
+ if (list.Count == 0 && searchInfo.PremiereDate.HasValue)
+ {
+ list = GetXmlNodesByPremiereDate(xmlFile, searchInfo.PremiereDate.Value);
+ }
- return list;
- }
+ return list;
+ }
- private List<FileSystemMetadata> GetEpisodeXmlFiles(int? seasonNumber, int? episodeNumber, int? endingEpisodeNumber, string seriesDataPath)
- {
- var files = new List<FileSystemMetadata>();
+ private List<FileSystemMetadata> GetEpisodeXmlFiles(int? seasonNumber, int? episodeNumber, int? endingEpisodeNumber, string seriesDataPath)
+ {
+ var files = new List<FileSystemMetadata>();
- if (episodeNumber == null)
- {
- return files;
- }
+ if (episodeNumber == null)
+ {
+ return files;
+ }
- var usingAbsoluteData = false;
+ var usingAbsoluteData = false;
if (seasonNumber.HasValue)
{
@@ -261,230 +271,292 @@ namespace MediaBrowser.Providers.TV
}
}
- var end = endingEpisodeNumber ?? episodeNumber;
- episodeNumber++;
+ var end = endingEpisodeNumber ?? episodeNumber;
+ episodeNumber++;
- while (episodeNumber <= end)
- {
+ while (episodeNumber <= end)
+ {
string file;
- 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);
- }
- else
- {
- break;
- }
-
- episodeNumber++;
- }
-
- return files;
- }
-
- private XmlReader GetXmlReader(FileSystemMetadata xmlFile)
- {
- return GetXmlReader (_fileSystem.ReadAllText(xmlFile.FullName, Encoding.UTF8));
- }
-
- private XmlReader GetXmlReader(String xml)
- {
- var streamReader = new StringReader (xml);
-
- return XmlReader.Create (streamReader, new XmlReaderSettings {
- CheckCharacters = false,
- IgnoreProcessingInstructions = true,
- IgnoreComments = true,
- ValidationType = ValidationType.None
- });
- }
-
- private List<XmlReader> GetXmlNodesByPremiereDate(string xmlFile, DateTime premiereDate)
- {
- var list = new List<XmlReader> ();
-
- using (var streamReader = new StreamReader (xmlFile, Encoding.UTF8)) {
- // Use XmlReader for best performance
- using (var reader = XmlReader.Create (streamReader, new XmlReaderSettings {
- CheckCharacters = false,
- IgnoreProcessingInstructions = true,
- IgnoreComments = true,
- ValidationType = ValidationType.None
- }))
- {
- reader.MoveToContent();
-
- // Loop through each element
- while (reader.Read())
- {
- if (reader.NodeType == XmlNodeType.Element)
- {
- switch (reader.Name)
- {
- case "Episode":
- {
- var outerXml = reader.ReadOuterXml();
-
- var airDate = GetEpisodeAirDate (outerXml);
-
- if (airDate.HasValue && premiereDate.Date == airDate.Value.Date)
- {
- list.Add (GetXmlReader(outerXml));
- return list;
- }
-
- break;
- }
-
- default:
- reader.Skip();
- break;
- }
- }
- }
- }
- }
-
- return list;
- }
-
- private DateTime? GetEpisodeAirDate(string xml)
- {
- using (var streamReader = new StringReader (xml))
- {
- // Use XmlReader for best performance
- using (var reader = XmlReader.Create (streamReader, new XmlReaderSettings {
- CheckCharacters = false,
- IgnoreProcessingInstructions = true,
- IgnoreComments = true,
- ValidationType = ValidationType.None
- }))
- {
- reader.MoveToContent ();
-
- // Loop through each element
- while (reader.Read ()) {
-
- if (reader.NodeType == XmlNodeType.Element) {
- switch (reader.Name) {
-
- case "FirstAired":
- {
- var val = reader.ReadElementContentAsString ();
-
- if (!string.IsNullOrWhiteSpace (val)) {
- DateTime date;
- if (DateTime.TryParse (val, out date)) {
- date = date.ToUniversalTime ();
-
- return date;
- }
- }
-
- break;
- }
-
- default:
- reader.Skip ();
- break;
- }
- }
- }
- }
- }
- return null;
- }
+ 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);
+ }
+ else
+ {
+ break;
+ }
+
+ episodeNumber++;
+ }
+
+ return files;
+ }
+
+ private XmlReader GetXmlReader(FileSystemMetadata xmlFile)
+ {
+ return GetXmlReader(_fileSystem.ReadAllText(xmlFile.FullName, Encoding.UTF8));
+ }
+
+ private XmlReader GetXmlReader(String xml)
+ {
+ var streamReader = new StringReader(xml);
+
+ var settings = _xmlSettings.Create(false);
+
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
+
+ return XmlReader.Create(streamReader, settings);
+ }
+
+ private List<XmlReader> GetXmlNodesByPremiereDate(string xmlFile, DateTime premiereDate)
+ {
+ var list = new List<XmlReader>();
+
+ using (var fileStream = _fileSystem.GetFileStream(xmlFile, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
+ {
+ using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
+ {
+ // Use XmlReader for best performance
+
+ var settings = _xmlSettings.Create(false);
+
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
+
+ using (var reader = XmlReader.Create(streamReader, settings))
+ {
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "Episode":
+ {
+ var outerXml = reader.ReadOuterXml();
+
+ var airDate = GetEpisodeAirDate(outerXml);
+
+ if (airDate.HasValue && premiereDate.Date == airDate.Value.Date)
+ {
+ list.Add(GetXmlReader(outerXml));
+ return list;
+ }
+
+ break;
+ }
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ else
+ {
+ reader.Read();
+ }
+ }
+ }
+ }
+ }
+
+ return list;
+ }
+
+ private DateTime? GetEpisodeAirDate(string xml)
+ {
+ using (var streamReader = new StringReader(xml))
+ {
+ var settings = _xmlSettings.Create(false);
+
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
+
+ // Use XmlReader for best performance
+ using (var reader = XmlReader.Create(streamReader, settings))
+ {
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "FirstAired":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ DateTime date;
+ if (DateTime.TryParse(val, out date))
+ {
+ date = date.ToUniversalTime();
+
+ return date;
+ }
+ }
+
+ break;
+ }
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ else
+ {
+ reader.Read();
+ }
+ }
+ }
+ }
+ return null;
+ }
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, CancellationToken cancellationToken)
{
var item = result.Item;
- // Use XmlReader for best performance
- using (reader)
- {
- reader.MoveToContent();
-
- result.ResetPeople();
-
- // Loop through each element
- while (reader.Read())
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- if (reader.NodeType == XmlNodeType.Element)
- {
- switch (reader.Name)
- {
- case "id":
- {
- var val = reader.ReadElementContentAsString();
- if (!string.IsNullOrWhiteSpace(val))
- {
- item.SetProviderId(MetadataProviders.Tvdb, val);
- }
- break;
- }
-
- case "IMDB_ID":
- {
- var val = reader.ReadElementContentAsString();
- if (!string.IsNullOrWhiteSpace(val))
- {
- item.SetProviderId(MetadataProviders.Imdb, val);
- }
- break;
- }
-
- case "DVD_episodenumber":
- {
- var val = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(val))
- {
- float num;
-
- if (float.TryParse(val, NumberStyles.Any, _usCulture, out num))
- {
- item.DvdEpisodeNumber = num;
- }
- }
-
- break;
- }
-
- case "DVD_season":
- {
- var val = reader.ReadElementContentAsString();
+ // Use XmlReader for best performance
+ using (reader)
+ {
+ result.ResetPeople();
+
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
- if (!string.IsNullOrWhiteSpace(val))
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "id":
{
- float num;
+ var val = reader.ReadElementContentAsString();
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ item.SetProviderId(MetadataProviders.Tvdb, val);
+ }
+ break;
+ }
- if (float.TryParse(val, NumberStyles.Any, _usCulture, out num))
+ case "IMDB_ID":
+ {
+ var val = reader.ReadElementContentAsString();
+ if (!string.IsNullOrWhiteSpace(val))
{
- item.DvdSeasonNumber = Convert.ToInt32(num);
+ item.SetProviderId(MetadataProviders.Imdb, val);
}
+ break;
}
- break;
- }
+ case "DVD_episodenumber":
+ {
+ var val = reader.ReadElementContentAsString();
- case "EpisodeNumber":
- {
- if (!item.IndexNumber.HasValue)
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ float num;
+
+ if (float.TryParse(val, NumberStyles.Any, _usCulture, out num))
+ {
+ item.DvdEpisodeNumber = num;
+ }
+ }
+
+ break;
+ }
+
+ case "DVD_season":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ float num;
+
+ if (float.TryParse(val, NumberStyles.Any, _usCulture, out num))
+ {
+ item.DvdSeasonNumber = Convert.ToInt32(num);
+ }
+ }
+
+ break;
+ }
+
+ case "EpisodeNumber":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!item.IndexNumber.HasValue)
+ {
+ 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();
+
+ if (!item.ParentIndexNumber.HasValue)
+ {
+ 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.ParentIndexNumber = rval;
+ }
+ }
+ }
+
+ break;
+ }
+
+ case "absolute_number":
{
var val = reader.ReadElementContentAsString();
@@ -495,17 +567,32 @@ namespace MediaBrowser.Providers.TV
// 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;
+ item.AbsoluteEpisodeNumber = rval;
}
}
+
+ break;
}
- break;
- }
+ case "airsbefore_episode":
+ {
+ var val = reader.ReadElementContentAsString();
- case "SeasonNumber":
- {
- if (!item.ParentIndexNumber.HasValue)
+ 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.AirsBeforeEpisodeNumber = rval;
+ }
+ }
+
+ break;
+ }
+
+ case "airsafter_season":
{
var val = reader.ReadElementContentAsString();
@@ -516,225 +603,174 @@ namespace MediaBrowser.Providers.TV
// 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;
+ item.AirsAfterSeasonNumber = rval;
}
}
+
+ break;
}
- break;
- }
+ case "airsbefore_season":
+ {
+ 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.AirsBeforeSeasonNumber = rval;
+ }
+ }
+
+ break;
+ }
+
+ case "EpisodeName":
+ {
+ var val = reader.ReadElementContentAsString();
+ if (!item.LockedFields.Contains(MetadataFields.Name))
+ {
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ item.Name = val;
+ }
+ }
+ break;
+ }
+
+ case "Overview":
+ {
+ var val = reader.ReadElementContentAsString();
+ if (!item.LockedFields.Contains(MetadataFields.Overview))
+ {
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ item.Overview = val;
+ }
+ }
+ break;
+ }
+ case "Rating":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ float rval;
+
+ // float.TryParse is local aware, so it can be probamatic, force us culture
+ if (float.TryParse(val, NumberStyles.AllowDecimalPoint, _usCulture, out rval))
+ {
+ item.CommunityRating = rval;
+ }
+ }
+ break;
+ }
+ case "RatingCount":
+ {
+ 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.VoteCount = rval;
+ }
+ }
+
+ break;
+ }
+
+ case "FirstAired":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ DateTime date;
+ if (DateTime.TryParse(val, out date))
+ {
+ date = date.ToUniversalTime();
+
+ item.PremiereDate = date;
+ item.ProductionYear = date.Year;
+ }
+ }
+
+ break;
+ }
+
+ case "Director":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ if (!item.LockedFields.Contains(MetadataFields.Cast))
+ {
+ AddPeople(result, val, PersonType.Director);
+ }
+ }
+
+ break;
+ }
+ case "GuestStars":
+ {
+ var val = reader.ReadElementContentAsString();
- 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 "airsbefore_episode":
- {
- 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.AirsBeforeEpisodeNumber = rval;
- }
- }
-
- break;
- }
-
- case "airsafter_season":
- {
- 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.AirsAfterSeasonNumber = rval;
- }
- }
-
- break;
- }
-
- case "airsbefore_season":
- {
- 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.AirsBeforeSeasonNumber = rval;
- }
- }
-
- break;
- }
-
- case "EpisodeName":
- {
- if (!item.LockedFields.Contains(MetadataFields.Name))
- {
- var val = reader.ReadElementContentAsString();
- if (!string.IsNullOrWhiteSpace(val))
- {
- item.Name = val;
- }
- }
- break;
- }
-
- case "Overview":
- {
- if (!item.LockedFields.Contains(MetadataFields.Overview))
- {
- var val = reader.ReadElementContentAsString();
- if (!string.IsNullOrWhiteSpace(val))
- {
- item.Overview = val;
- }
- }
- break;
- }
- case "Rating":
- {
- var val = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(val))
- {
- float rval;
-
- // float.TryParse is local aware, so it can be probamatic, force us culture
- if (float.TryParse(val, NumberStyles.AllowDecimalPoint, _usCulture, out rval))
- {
- item.CommunityRating = rval;
- }
- }
- break;
- }
- case "RatingCount":
- {
- 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.VoteCount = rval;
- }
- }
-
- break;
- }
-
- case "FirstAired":
- {
- var val = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(val))
- {
- DateTime date;
- if (DateTime.TryParse(val, out date))
- {
- date = date.ToUniversalTime();
-
- item.PremiereDate = date;
- item.ProductionYear = date.Year;
- }
- }
-
- break;
- }
-
- case "Director":
- {
- var val = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(val))
- {
- if (!item.LockedFields.Contains(MetadataFields.Cast))
- {
- AddPeople(result, val, PersonType.Director);
- }
- }
-
- break;
- }
- case "GuestStars":
- {
- var val = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(val))
- {
- if (!item.LockedFields.Contains(MetadataFields.Cast))
- {
- AddGuestStars(result, val);
- }
- }
-
- break;
- }
- case "Writer":
- {
- var val = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(val))
- {
- if (!item.LockedFields.Contains(MetadataFields.Cast))
- {
- AddPeople(result, val, PersonType.Writer);
- }
- }
-
- break;
- }
- case "Language":
- {
- var val = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(val))
- {
- result.ResultLanguage = val;
- }
-
- break;
- }
-
- default:
- reader.Skip();
- break;
- }
- }
- }
- }
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ if (!item.LockedFields.Contains(MetadataFields.Cast))
+ {
+ AddGuestStars(result, val);
+ }
+ }
+
+ break;
+ }
+ case "Writer":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ if (!item.LockedFields.Contains(MetadataFields.Cast))
+ {
+ AddPeople(result, val, PersonType.Writer);
+ }
+ }
+
+ break;
+ }
+ case "Language":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ result.ResultLanguage = val;
+ }
+
+ break;
+ }
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ else
+ {
+ reader.Read();
+ }
+ }
+ }
}
private void AddPeople<T>(MetadataResult<T> result, string val, string personType)
@@ -778,99 +814,104 @@ namespace MediaBrowser.Providers.TV
}
}
- private void FetchAdditionalPartInfo(MetadataResult<Episode> result, XmlReader reader, CancellationToken cancellationToken)
+ private void FetchAdditionalPartInfo(MetadataResult<Episode> result, XmlReader reader, CancellationToken cancellationToken)
{
var item = result.Item;
- // Use XmlReader for best performance
- using (reader)
- {
- reader.MoveToContent();
-
- // Loop through each element
- while (reader.Read())
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- if (reader.NodeType == XmlNodeType.Element)
- {
- switch (reader.Name)
- {
- case "EpisodeName":
- {
- if (!item.LockedFields.Contains(MetadataFields.Name))
- {
- var val = reader.ReadElementContentAsString();
- if (!string.IsNullOrWhiteSpace(val))
- {
- item.Name += ", " + val;
- }
- }
- break;
- }
-
- case "Overview":
- {
- if (!item.LockedFields.Contains(MetadataFields.Overview))
- {
- var val = reader.ReadElementContentAsString();
- if (!string.IsNullOrWhiteSpace(val))
- {
- item.Overview += Environment.NewLine + Environment.NewLine + val;
- }
- }
- break;
- }
- case "Director":
- {
- var val = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(val))
- {
- if (!item.LockedFields.Contains(MetadataFields.Cast))
- {
- AddPeople(result, val, PersonType.Director);
- }
- }
-
- break;
- }
- case "GuestStars":
- {
- var val = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(val))
- {
- if (!item.LockedFields.Contains(MetadataFields.Cast))
- {
- AddGuestStars(result, val);
- }
- }
-
- break;
- }
- case "Writer":
- {
- var val = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(val))
- {
- if (!item.LockedFields.Contains(MetadataFields.Cast))
- {
- AddPeople(result, val, PersonType.Writer);
- }
- }
-
- break;
- }
-
- default:
- reader.Skip();
- break;
- }
- }
- }
- }
+ // Use XmlReader for best performance
+ using (reader)
+ {
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "EpisodeName":
+ {
+ var val = reader.ReadElementContentAsString();
+ if (!item.LockedFields.Contains(MetadataFields.Name))
+ {
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ item.Name += ", " + val;
+ }
+ }
+ break;
+ }
+
+ case "Overview":
+ {
+ var val = reader.ReadElementContentAsString();
+ if (!item.LockedFields.Contains(MetadataFields.Overview))
+ {
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ item.Overview += Environment.NewLine + Environment.NewLine + val;
+ }
+ }
+ break;
+ }
+ case "Director":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ if (!item.LockedFields.Contains(MetadataFields.Cast))
+ {
+ AddPeople(result, val, PersonType.Director);
+ }
+ }
+
+ break;
+ }
+ case "GuestStars":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ if (!item.LockedFields.Contains(MetadataFields.Cast))
+ {
+ AddGuestStars(result, val);
+ }
+ }
+
+ break;
+ }
+ case "Writer":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ if (!item.LockedFields.Contains(MetadataFields.Cast))
+ {
+ AddPeople(result, val, PersonType.Writer);
+ }
+ }
+
+ break;
+ }
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ else
+ {
+ reader.Read();
+ }
+ }
+ }
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs
index d9e1037d8b..8488c56694 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs
@@ -14,8 +14,11 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.Providers.TV
{
@@ -48,6 +51,7 @@ namespace MediaBrowser.Providers.TV
private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
private readonly ILibraryManager _libraryManager;
+ private readonly IXmlReaderSettingsFactory _xmlSettings;
/// <summary>
/// Initializes a new instance of the <see cref="TvdbPrescanTask"/> class.
@@ -55,13 +59,14 @@ namespace MediaBrowser.Providers.TV
/// <param name="logger">The logger.</param>
/// <param name="httpClient">The HTTP client.</param>
/// <param name="config">The config.</param>
- public TvdbPrescanTask(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config, IFileSystem fileSystem, ILibraryManager libraryManager)
+ public TvdbPrescanTask(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config, IFileSystem fileSystem, ILibraryManager libraryManager, IXmlReaderSettingsFactory xmlSettings)
{
_logger = logger;
_httpClient = httpClient;
_config = config;
_fileSystem = fileSystem;
_libraryManager = libraryManager;
+ _xmlSettings = xmlSettings;
}
protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
@@ -101,7 +106,7 @@ namespace MediaBrowser.Providers.TV
string newUpdateTime;
- var existingDirectories = Directory.EnumerateDirectories(path)
+ var existingDirectories = _fileSystem.GetDirectoryPaths(path)
.Select(Path.GetFileName)
.ToList();
@@ -178,13 +183,11 @@ namespace MediaBrowser.Providers.TV
/// <returns>System.String.</returns>
private string GetUpdateTime(Stream response)
{
- var settings = new XmlReaderSettings
- {
- CheckCharacters = false,
- IgnoreProcessingInstructions = true,
- IgnoreComments = true,
- ValidationType = ValidationType.None
- };
+ var settings = _xmlSettings.Create(false);
+
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
using (var streamReader = new StreamReader(response, Encoding.UTF8))
{
@@ -192,9 +195,10 @@ namespace MediaBrowser.Providers.TV
using (var reader = XmlReader.Create(streamReader, settings))
{
reader.MoveToContent();
+ reader.Read();
// Loop through each element
- while (reader.Read())
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -209,6 +213,10 @@ namespace MediaBrowser.Providers.TV
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
}
}
@@ -251,13 +259,11 @@ namespace MediaBrowser.Providers.TV
string updateTime = null;
var idList = new List<string>();
- var settings = new XmlReaderSettings
- {
- CheckCharacters = false,
- IgnoreProcessingInstructions = true,
- IgnoreComments = true,
- ValidationType = ValidationType.None
- };
+ var settings = _xmlSettings.Create(false);
+
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
using (var streamReader = new StreamReader(stream, Encoding.UTF8))
{
@@ -265,9 +271,10 @@ namespace MediaBrowser.Providers.TV
using (var reader = XmlReader.Create(streamReader, settings))
{
reader.MoveToContent();
+ reader.Read();
// Loop through each element
- while (reader.Read())
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -289,6 +296,10 @@ namespace MediaBrowser.Providers.TV
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
}
}
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs
index 4b619665cd..e189c292c1 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs
@@ -16,7 +16,10 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.Providers.TV
{
@@ -27,12 +30,14 @@ namespace MediaBrowser.Providers.TV
private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient;
private readonly IFileSystem _fileSystem;
+ private readonly IXmlReaderSettingsFactory _xmlSettings;
- public TvdbSeasonImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem)
+ public TvdbSeasonImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlSettings)
{
_config = config;
_httpClient = httpClient;
_fileSystem = fileSystem;
+ _xmlSettings = xmlSettings;
}
public string Name
@@ -78,13 +83,13 @@ namespace MediaBrowser.Providers.TV
try
{
- return GetImages(path, item.GetPreferredMetadataLanguage(), seasonNumber, cancellationToken);
+ return GetImages(path, item.GetPreferredMetadataLanguage(), seasonNumber, _xmlSettings, _fileSystem, cancellationToken);
}
catch (FileNotFoundException)
{
// No tvdb data yet. Don't blow up
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
// No tvdb data yet. Don't blow up
}
@@ -103,45 +108,56 @@ namespace MediaBrowser.Providers.TV
return seasonNumber;
}
- internal static IEnumerable<RemoteImageInfo> GetImages(string xmlPath, string preferredLanguage, int seasonNumber, CancellationToken cancellationToken)
+ internal static IEnumerable<RemoteImageInfo> GetImages(string xmlPath, string preferredLanguage, int seasonNumber, IXmlReaderSettingsFactory xmlReaderSettingsFactory, IFileSystem fileSystem, CancellationToken cancellationToken)
{
- var settings = new XmlReaderSettings
- {
- CheckCharacters = false,
- IgnoreProcessingInstructions = true,
- IgnoreComments = true,
- ValidationType = ValidationType.None
- };
+ var settings = xmlReaderSettingsFactory.Create(false);
+
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
var list = new List<RemoteImageInfo>();
- using (var streamReader = new StreamReader(xmlPath, Encoding.UTF8))
+ using (var fileStream = fileSystem.GetFileStream(xmlPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
{
- // Use XmlReader for best performance
- using (var reader = XmlReader.Create(streamReader, settings))
+ using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
{
- reader.MoveToContent();
-
- // Loop through each element
- while (reader.Read())
+ // Use XmlReader for best performance
+ using (var reader = XmlReader.Create(streamReader, settings))
{
- cancellationToken.ThrowIfCancellationRequested();
+ reader.MoveToContent();
+ reader.Read();
- if (reader.NodeType == XmlNodeType.Element)
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
- switch (reader.Name)
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (reader.NodeType == XmlNodeType.Element)
{
- case "Banner":
- {
- using (var subtree = reader.ReadSubtree())
+ switch (reader.Name)
+ {
+ case "Banner":
{
- AddImage(subtree, list, seasonNumber);
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ continue;
+ }
+ using (var subtree = reader.ReadSubtree())
+ {
+ AddImage(subtree, list, seasonNumber);
+ }
+ break;
}
+ default:
+ reader.Skip();
break;
- }
- default:
- reader.Skip();
- break;
+ }
+ }
+ else
+ {
+ reader.Read();
}
}
}
@@ -189,7 +205,11 @@ namespace MediaBrowser.Providers.TV
int? voteCount = null;
string thumbnailUrl = null;
- while (reader.Read())
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -290,6 +310,10 @@ namespace MediaBrowser.Providers.TV
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
if (!string.IsNullOrEmpty(url) && bannerSeason.HasValue && bannerSeason.Value == seasonNumber)
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs
index c8efbfb141..2595ad5858 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs
@@ -16,7 +16,10 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.Providers.TV
{
@@ -26,12 +29,14 @@ namespace MediaBrowser.Providers.TV
private readonly IHttpClient _httpClient;
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly IFileSystem _fileSystem;
+ private readonly IXmlReaderSettingsFactory _xmlReaderSettingsFactory;
- public TvdbSeriesImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem)
+ public TvdbSeriesImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
{
_config = config;
_httpClient = httpClient;
_fileSystem = fileSystem;
+ _xmlReaderSettingsFactory = xmlReaderSettingsFactory;
}
public string Name
@@ -73,7 +78,7 @@ namespace MediaBrowser.Providers.TV
{
var seriesOffset = TvdbSeriesProvider.GetSeriesOffset(item.ProviderIds);
if (seriesOffset != null && seriesOffset.Value != 0)
- return TvdbSeasonImageProvider.GetImages(path, language, seriesOffset.Value + 1, cancellationToken);
+ return TvdbSeasonImageProvider.GetImages(path, language, seriesOffset.Value + 1, _xmlReaderSettingsFactory, _fileSystem, cancellationToken);
return GetImages(path, language, cancellationToken);
}
@@ -81,7 +86,7 @@ namespace MediaBrowser.Providers.TV
{
// No tvdb data yet. Don't blow up
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
// No tvdb data yet. Don't blow up
}
@@ -92,43 +97,54 @@ namespace MediaBrowser.Providers.TV
private IEnumerable<RemoteImageInfo> GetImages(string xmlPath, string preferredLanguage, CancellationToken cancellationToken)
{
- var settings = new XmlReaderSettings
- {
- CheckCharacters = false,
- IgnoreProcessingInstructions = true,
- IgnoreComments = true,
- ValidationType = ValidationType.None
- };
+ var settings = _xmlReaderSettingsFactory.Create(false);
+
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
var list = new List<RemoteImageInfo>();
- using (var streamReader = new StreamReader(xmlPath, Encoding.UTF8))
+ using (var fileStream = _fileSystem.GetFileStream(xmlPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
{
- // Use XmlReader for best performance
- using (var reader = XmlReader.Create(streamReader, settings))
+ using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
{
- reader.MoveToContent();
-
- // Loop through each element
- while (reader.Read())
+ // Use XmlReader for best performance
+ using (var reader = XmlReader.Create(streamReader, settings))
{
- cancellationToken.ThrowIfCancellationRequested();
+ reader.MoveToContent();
+ reader.Read();
- if (reader.NodeType == XmlNodeType.Element)
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
- switch (reader.Name)
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (reader.NodeType == XmlNodeType.Element)
{
- case "Banner":
- {
- using (var subtree = reader.ReadSubtree())
+ switch (reader.Name)
+ {
+ case "Banner":
{
- AddImage(subtree, list);
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ continue;
+ }
+ using (var subtree = reader.ReadSubtree())
+ {
+ AddImage(subtree, list);
+ }
+ break;
}
+ default:
+ reader.Skip();
break;
- }
- default:
- reader.Skip();
- break;
+ }
+ }
+ else
+ {
+ reader.Read();
}
}
}
@@ -175,7 +191,11 @@ namespace MediaBrowser.Providers.TV
int? voteCount = null;
string thumbnailUrl = null;
- while (reader.Read())
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -277,6 +297,10 @@ namespace MediaBrowser.Providers.TV
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
if (!string.IsNullOrEmpty(url) && !bannerSeason.HasValue)
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
index 2572a4f581..cc8a90fe38 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
@@ -20,8 +20,8 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
-using CommonIO;
-using MediaBrowser.Common.IO;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.Providers.TV
{
@@ -35,13 +35,15 @@ namespace MediaBrowser.Providers.TV
private readonly IZipClient _zipClient;
private readonly IHttpClient _httpClient;
private readonly IFileSystem _fileSystem;
+ private readonly IXmlReaderSettingsFactory _xmlSettings;
private readonly IServerConfigurationManager _config;
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly ILogger _logger;
private readonly ILibraryManager _libraryManager;
- private readonly IMemoryStreamProvider _memoryStreamProvider;
+ private readonly IMemoryStreamFactory _memoryStreamProvider;
+ private readonly ILocalizationManager _localizationManager;
- public TvdbSeriesProvider(IZipClient zipClient, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager config, ILogger logger, ILibraryManager libraryManager, IMemoryStreamProvider memoryStreamProvider)
+ public TvdbSeriesProvider(IZipClient zipClient, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager config, ILogger logger, ILibraryManager libraryManager, IMemoryStreamFactory memoryStreamProvider, IXmlReaderSettingsFactory xmlSettings, ILocalizationManager localizationManager)
{
_zipClient = zipClient;
_httpClient = httpClient;
@@ -50,6 +52,8 @@ namespace MediaBrowser.Providers.TV
_logger = logger;
_libraryManager = libraryManager;
_memoryStreamProvider = memoryStreamProvider;
+ _xmlSettings = xmlSettings;
+ _localizationManager = localizationManager;
Current = this;
}
@@ -251,7 +255,8 @@ namespace MediaBrowser.Providers.TV
}
// Sanitize all files, except for extracted episode files
- foreach (var file in Directory.EnumerateFiles(seriesDataPath, "*.xml", SearchOption.AllDirectories).ToList()
+ foreach (var file in _fileSystem.GetFilePaths(seriesDataPath, true).ToList()
+ .Where(i => string.Equals(Path.GetExtension(i), ".xml", StringComparison.OrdinalIgnoreCase))
.Where(i => !Path.GetFileName(i).StartsWith("episode-", StringComparison.OrdinalIgnoreCase)))
{
await SanitizeXmlFile(file).ConfigureAwait(false);
@@ -280,22 +285,95 @@ namespace MediaBrowser.Providers.TV
}).ConfigureAwait(false))
{
- var doc = new XmlDocument();
- doc.Load(result);
+ return FindSeriesId(result);
+ }
+ }
+
+ private string FindSeriesId(Stream stream)
+ {
+ using (var streamReader = new StreamReader(stream, Encoding.UTF8))
+ {
+ var settings = _xmlSettings.Create(false);
- if (doc.HasChildNodes)
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
+
+ // Use XmlReader for best performance
+ using (var reader = XmlReader.Create(streamReader, settings))
{
- var node = doc.SelectSingleNode("//Series/seriesid");
+ reader.MoveToContent();
+ reader.Read();
- if (node != null)
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
- var idResult = node.InnerText;
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "Series":
+ {
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ continue;
+ }
+ using (var subtree = reader.ReadSubtree())
+ {
+ return FindSeriesId(subtree);
+ }
+ }
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ else
+ {
+ reader.Read();
+ }
+ }
+ }
+ }
- _logger.Info("Tvdb GetSeriesByRemoteId produced id of {0}", idResult ?? string.Empty);
+ return null;
+ }
- return idResult;
+ private string FindSeriesId(XmlReader reader)
+ {
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "seriesid":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ return val;
+ }
+
+ return null;
+ }
+
+ default:
+ reader.Skip();
+ break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
return null;
@@ -304,7 +382,7 @@ namespace MediaBrowser.Providers.TV
internal static bool IsValidSeries(Dictionary<string, string> seriesProviderIds)
{
string id;
- if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out id) && !string.IsNullOrEmpty(id))
+ if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.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))
@@ -313,7 +391,7 @@ namespace MediaBrowser.Providers.TV
}
}
- if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out id) && !string.IsNullOrEmpty(id))
+ if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.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))
@@ -332,7 +410,7 @@ namespace MediaBrowser.Providers.TV
try
{
string seriesId;
- if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out seriesId) && !string.IsNullOrEmpty(seriesId))
+ if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out seriesId) && !string.IsNullOrWhiteSpace(seriesId))
{
var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds);
@@ -346,7 +424,7 @@ namespace MediaBrowser.Providers.TV
return seriesDataPath;
}
- if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out seriesId) && !string.IsNullOrEmpty(seriesId))
+ if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out seriesId) && !string.IsNullOrWhiteSpace(seriesId))
{
var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds);
@@ -401,11 +479,11 @@ namespace MediaBrowser.Providers.TV
}
return true;
}
- catch (DirectoryNotFoundException)
+ catch (FileNotFoundException)
{
return false;
}
- catch (FileNotFoundException)
+ catch (IOException)
{
return false;
}
@@ -449,9 +527,11 @@ namespace MediaBrowser.Providers.TV
private async Task<IEnumerable<RemoteSearchResult>> FindSeriesInternal(string name, string language, CancellationToken cancellationToken)
{
var url = string.Format(SeriesSearchUrl, WebUtility.UrlEncode(name), NormalizeLanguage(language));
- var doc = new XmlDocument();
+ var searchResults = new List<RemoteSearchResult>();
- using (var results = await _httpClient.Get(new HttpRequestOptions
+ var comparableName = GetComparableName(name);
+
+ using (var stream = await _httpClient.Get(new HttpRequestOptions
{
Url = url,
ResourcePool = TvDbResourcePool,
@@ -459,100 +539,186 @@ namespace MediaBrowser.Providers.TV
}).ConfigureAwait(false))
{
- doc.Load(results);
- }
+ var settings = _xmlSettings.Create(false);
- var searchResults = new List<RemoteSearchResult>();
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
- if (doc.HasChildNodes)
- {
- var nodes = doc.SelectNodes("//Series");
- var comparableName = GetComparableName(name);
- if (nodes != null)
+ using (var streamReader = new StreamReader(stream, Encoding.UTF8))
{
- foreach (XmlNode node in nodes)
+ // Use XmlReader for best performance
+ using (var reader = XmlReader.Create(streamReader, settings))
{
- var searchResult = new RemoteSearchResult
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
- SearchProviderName = Name
- };
+ cancellationToken.ThrowIfCancellationRequested();
- var titles = new List<string>();
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "Series":
+ {
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ continue;
+ }
+ using (var subtree = reader.ReadSubtree())
+ {
+ var searchResult = GetSeriesSearchResultFromSubTree(subtree, comparableName);
+ if (searchResult != null)
+ {
+ searchResult.SearchProviderName = Name;
+ searchResults.Add(searchResult);
+ }
+ }
+ break;
+ }
- var nameNode = node.SelectSingleNode("./SeriesName");
- if (nameNode != null)
- {
- titles.Add(GetComparableName(nameNode.InnerText));
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ else
+ {
+ reader.Read();
+ }
}
+ }
+ }
+ }
- var aliasNode = node.SelectSingleNode("./AliasNames");
- if (aliasNode != null)
- {
- var alias = aliasNode.InnerText.Split('|').Select(GetComparableName);
- titles.AddRange(alias);
- }
+ if (searchResults.Count == 0)
+ {
+ _logger.Info("TVDb Provider - Could not find " + name + ". Check name on Thetvdb.org.");
+ }
- var imdbIdNode = node.SelectSingleNode("./IMDB_ID");
- if (imdbIdNode != null)
- {
- var val = imdbIdNode.InnerText;
- if (!string.IsNullOrWhiteSpace(val))
+ return searchResults;
+ }
+
+ private RemoteSearchResult GetSeriesSearchResultFromSubTree(XmlReader reader, string comparableName)
+ {
+ var searchResult = new RemoteSearchResult
+ {
+ SearchProviderName = Name
+ };
+
+ var titles = new List<string>();
+ string seriesId = null;
+
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "SeriesName":
{
- searchResult.SetProviderId(MetadataProviders.Imdb, val);
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ titles.Add(GetComparableName(val));
+ }
+ break;
}
- }
- var bannerNode = node.SelectSingleNode("./banner");
- if (bannerNode != null)
- {
- var val = bannerNode.InnerText;
- if (!string.IsNullOrWhiteSpace(val))
+ case "AliasNames":
{
- searchResult.ImageUrl = TVUtils.BannerUrl + val;
+ var val = reader.ReadElementContentAsString();
+
+ var alias = (val ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries).Select(GetComparableName);
+ titles.AddRange(alias);
+ break;
}
- }
- var airDateNode = node.SelectSingleNode("./FirstAired");
- if (airDateNode != null)
- {
- var val = airDateNode.InnerText;
- if (!string.IsNullOrWhiteSpace(val))
+ case "IMDB_ID":
{
- DateTime date;
- if (DateTime.TryParse(val, out date))
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
{
- searchResult.ProductionYear = date.Year;
+ searchResult.SetProviderId(MetadataProviders.Imdb, val);
}
+ break;
}
- }
- foreach (var title in titles)
- {
- if (string.Equals(title, comparableName, StringComparison.OrdinalIgnoreCase))
+ case "banner":
{
- var id = node.SelectSingleNode("./seriesid") ??
- node.SelectSingleNode("./id");
+ var val = reader.ReadElementContentAsString();
- if (id != null)
+ if (!string.IsNullOrWhiteSpace(val))
{
- searchResult.Name = title;
- searchResult.SetProviderId(MetadataProviders.Tvdb, id.InnerText);
- searchResults.Add(searchResult);
+ searchResult.ImageUrl = TVUtils.BannerUrl + val;
}
break;
}
- _logger.Info("TVDb Provider - " + title + " did not match " + comparableName);
- }
+
+ case "FirstAired":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ DateTime date;
+ if (DateTime.TryParse(val, out date))
+ {
+ searchResult.ProductionYear = date.Year;
+ }
+ }
+ break;
+ }
+
+ case "id":
+ case "seriesid":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ seriesId = val;
+ }
+ break;
+ }
+
+ default:
+ reader.Skip();
+ break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
- if (searchResults.Count == 0)
+ foreach (var title in titles)
{
- _logger.Info("TVDb Provider - Could not find " + name + ". Check name on Thetvdb.org.");
+ 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 searchResults;
+ return null;
}
/// <summary>
@@ -569,10 +735,10 @@ namespace MediaBrowser.Providers.TV
/// </summary>
/// <param name="name">The name.</param>
/// <returns>System.String.</returns>
- internal static string GetComparableName(string name)
+ private string GetComparableName(string name)
{
name = name.ToLower();
- name = name.Normalize(NormalizationForm.FormKD);
+ name = _localizationManager.NormalizeFormKD(name);
var sb = new StringBuilder();
foreach (var c in name)
{
@@ -614,58 +780,74 @@ namespace MediaBrowser.Providers.TV
private void FetchSeriesInfo(MetadataResult<Series> result, string seriesXmlPath, CancellationToken cancellationToken)
{
- var settings = new XmlReaderSettings
- {
- CheckCharacters = false,
- IgnoreProcessingInstructions = true,
- IgnoreComments = true,
- ValidationType = ValidationType.None
- };
+ var settings = _xmlSettings.Create(false);
+
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
var episiodeAirDates = new List<DateTime>();
- using (var streamReader = new StreamReader(seriesXmlPath, Encoding.UTF8))
+ using (var fileStream = _fileSystem.GetFileStream(seriesXmlPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
{
- // Use XmlReader for best performance
- using (var reader = XmlReader.Create(streamReader, settings))
+ using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
{
- reader.MoveToContent();
-
- // Loop through each element
- while (reader.Read())
+ // Use XmlReader for best performance
+ using (var reader = XmlReader.Create(streamReader, settings))
{
- cancellationToken.ThrowIfCancellationRequested();
+ reader.MoveToContent();
+ reader.Read();
- if (reader.NodeType == XmlNodeType.Element)
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
- switch (reader.Name)
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (reader.NodeType == XmlNodeType.Element)
{
- case "Series":
- {
- using (var subtree = reader.ReadSubtree())
+ switch (reader.Name)
+ {
+ case "Series":
{
- FetchDataFromSeriesNode(result, subtree, cancellationToken);
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ continue;
+ }
+ using (var subtree = reader.ReadSubtree())
+ {
+ FetchDataFromSeriesNode(result, subtree, cancellationToken);
+ }
+ break;
}
- break;
- }
- case "Episode":
- {
- using (var subtree = reader.ReadSubtree())
+ case "Episode":
{
- var date = GetFirstAiredDateFromEpisodeNode(subtree, cancellationToken);
-
- if (date.HasValue)
+ if (reader.IsEmptyElement)
{
- episiodeAirDates.Add(date.Value);
+ reader.Read();
+ continue;
}
+ using (var subtree = reader.ReadSubtree())
+ {
+ var date = GetFirstAiredDateFromEpisodeNode(subtree, cancellationToken);
+
+ if (date.HasValue)
+ {
+ episiodeAirDates.Add(date.Value);
+ }
+ }
+ break;
}
- break;
- }
- default:
- reader.Skip();
- break;
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ else
+ {
+ reader.Read();
}
}
}
@@ -684,9 +866,10 @@ namespace MediaBrowser.Providers.TV
int? seasonNumber = null;
reader.MoveToContent();
+ reader.Read();
// Loop through each element
- while (reader.Read())
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -733,6 +916,10 @@ namespace MediaBrowser.Providers.TV
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
if (seasonNumber.HasValue && seasonNumber.Value != 0)
@@ -750,39 +937,50 @@ namespace MediaBrowser.Providers.TV
/// <param name="actorsXmlPath">The actors XML path.</param>
private void FetchActors(MetadataResult<Series> result, string actorsXmlPath)
{
- var settings = new XmlReaderSettings
- {
- CheckCharacters = false,
- IgnoreProcessingInstructions = true,
- IgnoreComments = true,
- ValidationType = ValidationType.None
- };
+ var settings = _xmlSettings.Create(false);
+
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
- using (var streamReader = new StreamReader(actorsXmlPath, Encoding.UTF8))
+ using (var fileStream = _fileSystem.GetFileStream(actorsXmlPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
{
- // Use XmlReader for best performance
- using (var reader = XmlReader.Create(streamReader, settings))
+ using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
{
- reader.MoveToContent();
-
- // Loop through each element
- while (reader.Read())
+ // Use XmlReader for best performance
+ using (var reader = XmlReader.Create(streamReader, settings))
{
- if (reader.NodeType == XmlNodeType.Element)
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
- switch (reader.Name)
+ if (reader.NodeType == XmlNodeType.Element)
{
- case "Actor":
- {
- using (var subtree = reader.ReadSubtree())
+ switch (reader.Name)
+ {
+ case "Actor":
{
- FetchDataFromActorNode(result, subtree);
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ continue;
+ }
+ using (var subtree = reader.ReadSubtree())
+ {
+ FetchDataFromActorNode(result, subtree);
+ }
+ break;
}
+ default:
+ reader.Skip();
break;
- }
- default:
- reader.Skip();
- break;
+ }
+ }
+ else
+ {
+ reader.Read();
}
}
}
@@ -801,7 +999,11 @@ namespace MediaBrowser.Providers.TV
var personInfo = new PersonInfo();
- while (reader.Read())
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -821,6 +1023,7 @@ namespace MediaBrowser.Providers.TV
case "id":
{
+ reader.Skip();
break;
}
@@ -856,6 +1059,10 @@ namespace MediaBrowser.Providers.TV
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
personInfo.Type = PersonType.Actor;
@@ -871,9 +1078,10 @@ namespace MediaBrowser.Providers.TV
Series item = result.Item;
reader.MoveToContent();
+ reader.Read();
// Loop through each element
- while (reader.Read())
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -1099,6 +1307,10 @@ namespace MediaBrowser.Providers.TV
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
}
@@ -1111,39 +1323,45 @@ namespace MediaBrowser.Providers.TV
/// <returns>Task.</returns>
private async Task ExtractEpisodes(string seriesDataPath, string xmlFile, long? lastTvDbUpdateTime)
{
- var settings = new XmlReaderSettings
- {
- CheckCharacters = false,
- IgnoreProcessingInstructions = true,
- IgnoreComments = true,
- ValidationType = ValidationType.None
- };
+ var settings = _xmlSettings.Create(false);
+
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
- using (var streamReader = new StreamReader(xmlFile, Encoding.UTF8))
+ using (var fileStream = _fileSystem.GetFileStream(xmlFile, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
{
- // Use XmlReader for best performance
- using (var reader = XmlReader.Create(streamReader, settings))
+ using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
{
- reader.MoveToContent();
-
- // Loop through each element
- while (reader.Read())
+ // Use XmlReader for best performance
+ using (var reader = XmlReader.Create(streamReader, settings))
{
- if (reader.NodeType == XmlNodeType.Element)
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
- switch (reader.Name)
+ if (reader.NodeType == XmlNodeType.Element)
{
- case "Episode":
- {
- var outerXml = reader.ReadOuterXml();
+ switch (reader.Name)
+ {
+ case "Episode":
+ {
+ var outerXml = reader.ReadOuterXml();
- await SaveEpsiodeXml(seriesDataPath, outerXml, lastTvDbUpdateTime).ConfigureAwait(false);
- break;
- }
+ await SaveEpsiodeXml(seriesDataPath, outerXml, lastTvDbUpdateTime).ConfigureAwait(false);
+ break;
+ }
- default:
- reader.Skip();
- break;
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ else
+ {
+ reader.Read();
}
}
}
@@ -1153,13 +1371,11 @@ namespace MediaBrowser.Providers.TV
private async Task SaveEpsiodeXml(string seriesDataPath, string xml, long? lastTvDbUpdateTime)
{
- var settings = new XmlReaderSettings
- {
- CheckCharacters = false,
- IgnoreProcessingInstructions = true,
- IgnoreComments = true,
- ValidationType = ValidationType.None
- };
+ var settings = _xmlSettings.Create(false);
+
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
var seasonNumber = -1;
var episodeNumber = -1;
@@ -1172,9 +1388,10 @@ namespace MediaBrowser.Providers.TV
using (var reader = XmlReader.Create(streamReader, settings))
{
reader.MoveToContent();
+ reader.Read();
// Loop through each element
- while (reader.Read())
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -1233,6 +1450,10 @@ namespace MediaBrowser.Providers.TV
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
}
}
@@ -1252,13 +1473,16 @@ namespace MediaBrowser.Providers.TV
// Only save the file if not already there, or if the episode has changed
if (hasEpisodeChanged || !_fileSystem.FileExists(file))
{
- using (var writer = XmlWriter.Create(file, new XmlWriterSettings
- {
- Encoding = Encoding.UTF8,
- Async = true
- }))
+ using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.None, true))
{
- await writer.WriteRawAsync(xml).ConfigureAwait(false);
+ using (var writer = XmlWriter.Create(fileStream, new XmlWriterSettings
+ {
+ Encoding = Encoding.UTF8,
+ Async = true
+ }))
+ {
+ await writer.WriteRawAsync(xml).ConfigureAwait(false);
+ }
}
}
@@ -1269,13 +1493,16 @@ namespace MediaBrowser.Providers.TV
// Only save the file if not already there, or if the episode has changed
if (hasEpisodeChanged || !_fileSystem.FileExists(file))
{
- using (var writer = XmlWriter.Create(file, new XmlWriterSettings
- {
- Encoding = Encoding.UTF8,
- Async = true
- }))
+ using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.None, true))
{
- await writer.WriteRawAsync(xml).ConfigureAwait(false);
+ using (var writer = XmlWriter.Create(fileStream, new XmlWriterSettings
+ {
+ Encoding = Encoding.UTF8,
+ Async = true
+ }))
+ {
+ await writer.WriteRawAsync(xml).ConfigureAwait(false);
+ }
}
}
}
@@ -1338,7 +1565,7 @@ namespace MediaBrowser.Providers.TV
_fileSystem.DeleteFile(file);
}
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
// No biggie
}
@@ -1353,7 +1580,7 @@ namespace MediaBrowser.Providers.TV
{
string validXml;
- using (var fileStream = _fileSystem.GetFileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read, true))
+ using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true))
{
using (var reader = new StreamReader(fileStream))
{
@@ -1363,7 +1590,7 @@ namespace MediaBrowser.Providers.TV
}
}
- using (var fileStream = _fileSystem.GetFileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read, true))
+ using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
{
using (var writer = new StreamWriter(fileStream))
{
diff --git a/MediaBrowser.Providers/Users/UserMetadataService.cs b/MediaBrowser.Providers/Users/UserMetadataService.cs
index 0637e9a02a..274d04efd0 100644
--- a/MediaBrowser.Providers/Users/UserMetadataService.cs
+++ b/MediaBrowser.Providers/Users/UserMetadataService.cs
@@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Users
{
diff --git a/MediaBrowser.Providers/Videos/VideoMetadataService.cs b/MediaBrowser.Providers/Videos/VideoMetadataService.cs
index a4fc462efd..49f7e11ba2 100644
--- a/MediaBrowser.Providers/Videos/VideoMetadataService.cs
+++ b/MediaBrowser.Providers/Videos/VideoMetadataService.cs
@@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Videos
{
diff --git a/MediaBrowser.Providers/Years/YearMetadataService.cs b/MediaBrowser.Providers/Years/YearMetadataService.cs
index fd65c379d7..72f8eb471c 100644
--- a/MediaBrowser.Providers/Years/YearMetadataService.cs
+++ b/MediaBrowser.Providers/Years/YearMetadataService.cs
@@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.Years
{
diff --git a/MediaBrowser.Providers/packages.config b/MediaBrowser.Providers/packages.config
deleted file mode 100644
index da365b0ce8..0000000000
--- a/MediaBrowser.Providers/packages.config
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
- <package id="CommonIO" version="1.0.0.9" targetFramework="net45" />
- <package id="MediaBrowser.BdInfo" version="1.0.0.10" targetFramework="net45" />
- <package id="morelinq" version="1.4.0" targetFramework="net45" />
- <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
- <package id="taglib" version="2.1.0.0" targetFramework="net45" />
-</packages> \ No newline at end of file
diff --git a/MediaBrowser.Providers/project.json b/MediaBrowser.Providers/project.json
new file mode 100644
index 0000000000..fbbe9eaf32
--- /dev/null
+++ b/MediaBrowser.Providers/project.json
@@ -0,0 +1,17 @@
+{
+ "frameworks":{
+ "netstandard1.6":{
+ "dependencies":{
+ "NETStandard.Library":"1.6.0",
+ }
+ },
+ ".NETPortable,Version=v4.5,Profile=Profile7":{
+ "buildOptions": {
+ "define": [ ]
+ },
+ "frameworkAssemblies":{
+
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Activity/ActivityRepository.cs b/MediaBrowser.Server.Implementations/Activity/ActivityRepository.cs
deleted file mode 100644
index c992def39d..0000000000
--- a/MediaBrowser.Server.Implementations/Activity/ActivityRepository.cs
+++ /dev/null
@@ -1,253 +0,0 @@
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Activity;
-using MediaBrowser.Model.Activity;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Server.Implementations.Persistence;
-using System;
-using System.Collections.Generic;
-using System.Data;
-using System.Globalization;
-using System.IO;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Activity
-{
- public class ActivityRepository : BaseSqliteRepository, IActivityRepository
- {
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
- public ActivityRepository(ILogManager logManager, IServerApplicationPaths appPaths, IDbConnector connector)
- : base(logManager, connector)
- {
- DbFilePath = Path.Combine(appPaths.DataPath, "activitylog.db");
- }
-
- public async Task Initialize()
- {
- using (var connection = await CreateConnection().ConfigureAwait(false))
- {
- string[] queries = {
-
- "create table if not exists ActivityLogEntries (Id GUID PRIMARY KEY, Name TEXT, Overview TEXT, ShortOverview TEXT, Type TEXT, ItemId TEXT, UserId TEXT, DateCreated DATETIME, LogSeverity TEXT)",
- "create index if not exists idx_ActivityLogEntries on ActivityLogEntries(Id)"
- };
-
- connection.RunQueries(queries, Logger);
- }
- }
-
- private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLogEntries";
-
- public Task Create(ActivityLogEntry entry)
- {
- return Update(entry);
- }
-
- public async Task Update(ActivityLogEntry entry)
- {
- if (entry == null)
- {
- throw new ArgumentNullException("entry");
- }
-
- using (var connection = await CreateConnection().ConfigureAwait(false))
- {
- using (var saveActivityCommand = connection.CreateCommand())
- {
- saveActivityCommand.CommandText = "replace into ActivityLogEntries (Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) values (@Id, @Name, @Overview, @ShortOverview, @Type, @ItemId, @UserId, @DateCreated, @LogSeverity)";
-
- saveActivityCommand.Parameters.Add(saveActivityCommand, "@Id");
- saveActivityCommand.Parameters.Add(saveActivityCommand, "@Name");
- saveActivityCommand.Parameters.Add(saveActivityCommand, "@Overview");
- saveActivityCommand.Parameters.Add(saveActivityCommand, "@ShortOverview");
- saveActivityCommand.Parameters.Add(saveActivityCommand, "@Type");
- saveActivityCommand.Parameters.Add(saveActivityCommand, "@ItemId");
- saveActivityCommand.Parameters.Add(saveActivityCommand, "@UserId");
- saveActivityCommand.Parameters.Add(saveActivityCommand, "@DateCreated");
- saveActivityCommand.Parameters.Add(saveActivityCommand, "@LogSeverity");
-
- IDbTransaction transaction = null;
-
- try
- {
- transaction = connection.BeginTransaction();
-
- var index = 0;
-
- saveActivityCommand.GetParameter(index++).Value = new Guid(entry.Id);
- saveActivityCommand.GetParameter(index++).Value = entry.Name;
- saveActivityCommand.GetParameter(index++).Value = entry.Overview;
- saveActivityCommand.GetParameter(index++).Value = entry.ShortOverview;
- saveActivityCommand.GetParameter(index++).Value = entry.Type;
- saveActivityCommand.GetParameter(index++).Value = entry.ItemId;
- saveActivityCommand.GetParameter(index++).Value = entry.UserId;
- saveActivityCommand.GetParameter(index++).Value = entry.Date;
- saveActivityCommand.GetParameter(index++).Value = entry.Severity.ToString();
-
- saveActivityCommand.Transaction = transaction;
-
- saveActivityCommand.ExecuteNonQuery();
-
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to save record:", e);
-
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
- }
- }
- }
- }
- }
-
- public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit)
- {
- using (var connection = CreateConnection(true).Result)
- {
- using (var cmd = connection.CreateCommand())
- {
- cmd.CommandText = BaseActivitySelectText;
-
- var whereClauses = new List<string>();
-
- if (minDate.HasValue)
- {
- whereClauses.Add("DateCreated>=@DateCreated");
- cmd.Parameters.Add(cmd, "@DateCreated", DbType.Date).Value = minDate.Value;
- }
-
- var whereTextWithoutPaging = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
-
- if (startIndex.HasValue && startIndex.Value > 0)
- {
- var pagingWhereText = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
-
- whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM ActivityLogEntries {0} ORDER BY DateCreated DESC LIMIT {1})",
- pagingWhereText,
- startIndex.Value.ToString(_usCulture)));
- }
-
- var whereText = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
-
- cmd.CommandText += whereText;
-
- cmd.CommandText += " ORDER BY DateCreated DESC";
-
- if (limit.HasValue)
- {
- cmd.CommandText += " LIMIT " + limit.Value.ToString(_usCulture);
- }
-
- cmd.CommandText += "; select count (Id) from ActivityLogEntries" + whereTextWithoutPaging;
-
- var list = new List<ActivityLogEntry>();
- var count = 0;
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
- {
- while (reader.Read())
- {
- list.Add(GetEntry(reader));
- }
-
- if (reader.NextResult() && reader.Read())
- {
- count = reader.GetInt32(0);
- }
- }
-
- return new QueryResult<ActivityLogEntry>()
- {
- Items = list.ToArray(),
- TotalRecordCount = count
- };
- }
- }
- }
-
- private ActivityLogEntry GetEntry(IDataReader reader)
- {
- var index = 0;
-
- var info = new ActivityLogEntry
- {
- Id = reader.GetGuid(index).ToString("N")
- };
-
- index++;
- if (!reader.IsDBNull(index))
- {
- info.Name = reader.GetString(index);
- }
-
- index++;
- if (!reader.IsDBNull(index))
- {
- info.Overview = reader.GetString(index);
- }
-
- index++;
- if (!reader.IsDBNull(index))
- {
- info.ShortOverview = reader.GetString(index);
- }
-
- index++;
- if (!reader.IsDBNull(index))
- {
- info.Type = reader.GetString(index);
- }
-
- index++;
- if (!reader.IsDBNull(index))
- {
- info.ItemId = reader.GetString(index);
- }
-
- index++;
- if (!reader.IsDBNull(index))
- {
- info.UserId = reader.GetString(index);
- }
-
- index++;
- info.Date = reader.GetDateTime(index).ToUniversalTime();
-
- index++;
- if (!reader.IsDBNull(index))
- {
- info.Severity = (LogSeverity)Enum.Parse(typeof(LogSeverity), reader.GetString(index), true);
- }
-
- return info;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs b/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs
index 3dfc04c267..979a929ca7 100644
--- a/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs
+++ b/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs
@@ -3,11 +3,13 @@ using MediaBrowser.Controller.Entities;
using System;
using System.IO;
using System.Linq;
-using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Server.Implementations.Devices
{
@@ -62,29 +64,4 @@ namespace MediaBrowser.Server.Implementations.Devices
get { return true; }
}
}
-
- 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/MediaBrowser.Server.Implementations/HttpServer/AsyncStreamWriter.cs b/MediaBrowser.Server.Implementations/HttpServer/AsyncStreamWriter.cs
deleted file mode 100644
index e44b0c6af1..0000000000
--- a/MediaBrowser.Server.Implementations/HttpServer/AsyncStreamWriter.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading.Tasks;
-using ServiceStack;
-using ServiceStack.Web;
-using MediaBrowser.Controller.Net;
-
-namespace MediaBrowser.Server.Implementations.HttpServer
-{
- public class AsyncStreamWriter : IStreamWriter, IAsyncStreamWriter, IHasOptions
- {
- /// <summary>
- /// Gets or sets the source stream.
- /// </summary>
- /// <value>The source stream.</value>
- private IAsyncStreamSource _source;
-
- public Action OnComplete { get; set; }
- public Action OnError { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="AsyncStreamWriter" /> class.
- /// </summary>
- public AsyncStreamWriter(IAsyncStreamSource source)
- {
- _source = source;
- }
-
- public IDictionary<string, string> Options
- {
- get
- {
- var hasOptions = _source as IHasOptions;
- if (hasOptions != null)
- {
- return hasOptions.Options;
- }
-
- return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- }
- }
-
- /// <summary>
- /// Writes to.
- /// </summary>
- /// <param name="responseStream">The response stream.</param>
- public void WriteTo(Stream responseStream)
- {
- var task = _source.WriteToAsync(responseStream);
- Task.WaitAll(task);
- }
-
- public async Task WriteToAsync(Stream responseStream)
- {
- await _source.WriteToAsync(responseStream).ConfigureAwait(false);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/HttpServer/ContainerAdapter.cs b/MediaBrowser.Server.Implementations/HttpServer/ContainerAdapter.cs
deleted file mode 100644
index 93d224b8db..0000000000
--- a/MediaBrowser.Server.Implementations/HttpServer/ContainerAdapter.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using MediaBrowser.Common;
-using ServiceStack.Configuration;
-
-namespace MediaBrowser.Server.Implementations.HttpServer
-{
- /// <summary>
- /// Class ContainerAdapter
- /// </summary>
- class ContainerAdapter : IContainerAdapter, IRelease
- {
- /// <summary>
- /// The _app host
- /// </summary>
- private readonly IApplicationHost _appHost;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ContainerAdapter" /> class.
- /// </summary>
- /// <param name="appHost">The app host.</param>
- public ContainerAdapter(IApplicationHost appHost)
- {
- _appHost = appHost;
- }
- /// <summary>
- /// Resolves this instance.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <returns>``0.</returns>
- public T Resolve<T>()
- {
- return _appHost.Resolve<T>();
- }
-
- /// <summary>
- /// Tries the resolve.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <returns>``0.</returns>
- public T TryResolve<T>()
- {
- return _appHost.TryResolve<T>();
- }
-
- /// <summary>
- /// Releases the specified instance.
- /// </summary>
- /// <param name="instance">The instance.</param>
- public void Release(object instance)
- {
- // Leave this empty so SS doesn't try to dispose our objects
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs
deleted file mode 100644
index b3d3ec13cc..0000000000
--- a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ /dev/null
@@ -1,676 +0,0 @@
-using Funq;
-using MediaBrowser.Common;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Server.Implementations.HttpServer.SocketSharp;
-using ServiceStack;
-using ServiceStack.Api.Swagger;
-using ServiceStack.Host;
-using ServiceStack.Host.Handlers;
-using ServiceStack.Host.HttpListener;
-using ServiceStack.Logging;
-using ServiceStack.Web;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Common.Security;
-using MediaBrowser.Model.Extensions;
-
-namespace MediaBrowser.Server.Implementations.HttpServer
-{
- public class HttpListenerHost : ServiceStackHost, IHttpServer
- {
- private string DefaultRedirectPath { get; set; }
-
- private readonly ILogger _logger;
- public IEnumerable<string> UrlPrefixes { get; private set; }
-
- private readonly List<IRestfulService> _restServices = new List<IRestfulService>();
-
- private IHttpListener _listener;
-
- private readonly ContainerAdapter _containerAdapter;
-
- public event EventHandler<WebSocketConnectEventArgs> WebSocketConnected;
- public event EventHandler<WebSocketConnectingEventArgs> WebSocketConnecting;
-
- public string CertificatePath { get; private set; }
-
- private readonly IServerConfigurationManager _config;
- private readonly INetworkManager _networkManager;
- private readonly IMemoryStreamProvider _memoryStreamProvider;
-
- public HttpListenerHost(IApplicationHost applicationHost,
- ILogManager logManager,
- IServerConfigurationManager config,
- string serviceName,
- string defaultRedirectPath, INetworkManager networkManager, IMemoryStreamProvider memoryStreamProvider, params Assembly[] assembliesWithServices)
- : base(serviceName, assembliesWithServices)
- {
- DefaultRedirectPath = defaultRedirectPath;
- _networkManager = networkManager;
- _memoryStreamProvider = memoryStreamProvider;
- _config = config;
-
- _logger = logManager.GetLogger("HttpServer");
-
- _containerAdapter = new ContainerAdapter(applicationHost);
- }
-
- public string GlobalResponse { get; set; }
-
- public override void Configure(Container container)
- {
- HostConfig.Instance.DefaultRedirectPath = DefaultRedirectPath;
- HostConfig.Instance.LogUnobservedTaskExceptions = false;
-
- HostConfig.Instance.MapExceptionToStatusCode = new Dictionary<Type, int>
- {
- {typeof (InvalidOperationException), 500},
- {typeof (NotImplementedException), 500},
- {typeof (ResourceNotFoundException), 404},
- {typeof (FileNotFoundException), 404},
- {typeof (DirectoryNotFoundException), 404},
- {typeof (SecurityException), 401},
- {typeof (PaymentRequiredException), 402},
- {typeof (UnauthorizedAccessException), 500},
- {typeof (ApplicationException), 500},
- {typeof (PlatformNotSupportedException), 500},
- {typeof (NotSupportedException), 500}
- };
-
- HostConfig.Instance.GlobalResponseHeaders = new Dictionary<string, string>();
- HostConfig.Instance.DebugMode = false;
-
- HostConfig.Instance.LogFactory = LogManager.LogFactory;
- HostConfig.Instance.AllowJsonpRequests = false;
-
- // The Markdown feature causes slow startup times (5 mins+) on cold boots for some users
- // Custom format allows images
- HostConfig.Instance.EnableFeatures = Feature.Html | Feature.Json | Feature.Xml | Feature.CustomFormat;
-
- container.Adapter = _containerAdapter;
-
- Plugins.RemoveAll(x => x is NativeTypesFeature);
- Plugins.Add(new SwaggerFeature());
- Plugins.Add(new CorsFeature(allowedHeaders: "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization"));
-
- //Plugins.Add(new AuthFeature(() => new AuthUserSession(), new IAuthProvider[] {
- // new SessionAuthProvider(_containerAdapter.Resolve<ISessionContext>()),
- //}));
-
- //PreRequestFilters.Add((httpReq, httpRes) =>
- //{
- // //Handles Request and closes Responses after emitting global HTTP Headers
- // if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase))
- // {
- // httpRes.EndRequest(); //add a 'using ServiceStack;'
- // }
- //});
-
- HostContext.GlobalResponseFilters.Add(new ResponseFilter(_logger).FilterResponse);
- }
-
- public override void OnAfterInit()
- {
- SetAppDomainData();
-
- base.OnAfterInit();
- }
-
- public override void OnConfigLoad()
- {
- base.OnConfigLoad();
-
- Config.HandlerFactoryPath = null;
-
- Config.MetadataRedirectPath = "metadata";
- }
-
- protected override ServiceController CreateServiceController(params Assembly[] assembliesWithServices)
- {
- var types = _restServices.Select(r => r.GetType()).ToArray();
-
- return new ServiceController(this, () => types);
- }
-
- public virtual void SetAppDomainData()
- {
- //Required for Mono to resolve VirtualPathUtility and Url.Content urls
- var domain = Thread.GetDomain(); // or AppDomain.Current
- domain.SetData(".appDomain", "1");
- domain.SetData(".appVPath", "/");
- domain.SetData(".appPath", domain.BaseDirectory);
- if (string.IsNullOrEmpty(domain.GetData(".appId") as string))
- {
- domain.SetData(".appId", "1");
- }
- if (string.IsNullOrEmpty(domain.GetData(".domainId") as string))
- {
- domain.SetData(".domainId", "1");
- }
- }
-
- public override ServiceStackHost Start(string listeningAtUrlBase)
- {
- StartListener();
- return this;
- }
-
- /// <summary>
- /// Starts the Web Service
- /// </summary>
- private void StartListener()
- {
- HostContext.Config.HandlerFactoryPath = ListenerRequest.GetHandlerPathIfAny(UrlPrefixes.First());
-
- _listener = GetListener();
-
- _listener.WebSocketConnected = OnWebSocketConnected;
- _listener.WebSocketConnecting = OnWebSocketConnecting;
- _listener.ErrorHandler = ErrorHandler;
- _listener.RequestHandler = RequestHandler;
-
- _listener.Start(UrlPrefixes);
- }
-
- private IHttpListener GetListener()
- {
- return new WebSocketSharpListener(_logger, CertificatePath, _memoryStreamProvider);
- }
-
- private void OnWebSocketConnecting(WebSocketConnectingEventArgs args)
- {
- if (_disposed)
- {
- return;
- }
-
- if (WebSocketConnecting != null)
- {
- WebSocketConnecting(this, args);
- }
- }
-
- private void OnWebSocketConnected(WebSocketConnectEventArgs args)
- {
- if (_disposed)
- {
- return;
- }
-
- if (WebSocketConnected != null)
- {
- WebSocketConnected(this, args);
- }
- }
-
- private void ErrorHandler(Exception ex, IRequest httpReq)
- {
- try
- {
- var httpRes = httpReq.Response;
-
- if (httpRes.IsClosed)
- {
- return;
- }
-
- var errorResponse = new ErrorResponse
- {
- ResponseStatus = new ResponseStatus
- {
- ErrorCode = ex.GetType().GetOperationName(),
- Message = ex.Message,
- StackTrace = ex.StackTrace
- }
- };
-
- var contentType = httpReq.ResponseContentType;
-
- var serializer = HostContext.ContentTypes.GetResponseSerializer(contentType);
- if (serializer == null)
- {
- contentType = HostContext.Config.DefaultContentType;
- serializer = HostContext.ContentTypes.GetResponseSerializer(contentType);
- }
-
- var httpError = ex as IHttpError;
- if (httpError != null)
- {
- httpRes.StatusCode = httpError.Status;
- httpRes.StatusDescription = httpError.StatusDescription;
- }
- else
- {
- httpRes.StatusCode = 500;
- }
-
- httpRes.ContentType = contentType;
-
- serializer(httpReq, errorResponse, httpRes);
-
- httpRes.Close();
- }
- catch
- {
- //_logger.ErrorException("Error this.ProcessRequest(context)(Exception while writing error to the response)", errorEx);
- }
- }
-
- /// <summary>
- /// Shut down the Web Service
- /// </summary>
- public void Stop()
- {
- if (_listener != null)
- {
- _listener.Stop();
- }
- }
-
- private readonly Dictionary<string, int> _skipLogExtensions = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
- {
- {".js", 0},
- {".css", 0},
- {".woff", 0},
- {".woff2", 0},
- {".ttf", 0},
- {".html", 0}
- };
-
- private bool EnableLogging(string url, string localPath)
- {
- var extension = GetExtension(url);
-
- if (string.IsNullOrWhiteSpace(extension) || !_skipLogExtensions.ContainsKey(extension))
- {
- if (string.IsNullOrWhiteSpace(localPath) || localPath.IndexOf("system/ping", StringComparison.OrdinalIgnoreCase) == -1)
- {
- return true;
- }
- }
-
- return false;
- }
-
- private string GetExtension(string url)
- {
- var parts = url.Split(new[] { '?' }, 2);
-
- return Path.GetExtension(parts[0]);
- }
-
- public static string RemoveQueryStringByKey(string url, string key)
- {
- var uri = new Uri(url);
-
- // this gets all the query string key value pairs as a collection
- var newQueryString = MyHttpUtility.ParseQueryString(uri.Query);
-
- if (newQueryString.Count == 0)
- {
- return url;
- }
-
- // this removes the key if exists
- newQueryString.Remove(key);
-
- // this gets the page path from root without QueryString
- string pagePathWithoutQueryString = uri.GetLeftPart(UriPartial.Path);
-
- return newQueryString.Count > 0
- ? String.Format("{0}?{1}", pagePathWithoutQueryString, newQueryString)
- : pagePathWithoutQueryString;
- }
-
- private string GetUrlToLog(string url)
- {
- url = RemoveQueryStringByKey(url, "api_key");
-
- return url;
- }
-
- private string NormalizeConfiguredLocalAddress(string address)
- {
- var index = address.Trim('/').IndexOf('/');
-
- if (index != -1)
- {
- address = address.Substring(index + 1);
- }
-
- return address.Trim('/');
- }
-
- private bool ValidateHost(Uri url)
- {
- var hosts = _config
- .Configuration
- .LocalNetworkAddresses
- .Select(NormalizeConfiguredLocalAddress)
- .ToList();
-
- if (hosts.Count == 0)
- {
- return true;
- }
-
- var host = url.Host ?? string.Empty;
-
- _logger.Debug("Validating host {0}", host);
-
- if (_networkManager.IsInPrivateAddressSpace(host))
- {
- hosts.Add("localhost");
- hosts.Add("127.0.0.1");
-
- return hosts.Any(i => host.IndexOf(i, StringComparison.OrdinalIgnoreCase) != -1);
- }
-
- return true;
- }
-
- /// <summary>
- /// Overridable method that can be used to implement a custom hnandler
- /// </summary>
- /// <param name="httpReq">The HTTP req.</param>
- /// <param name="url">The URL.</param>
- /// <returns>Task.</returns>
- protected async Task RequestHandler(IHttpRequest httpReq, Uri url)
- {
- var date = DateTime.Now;
-
- var httpRes = httpReq.Response;
-
- if (_disposed)
- {
- httpRes.StatusCode = 503;
- httpRes.Close();
- return ;
- }
-
- if (!ValidateHost(url))
- {
- httpRes.StatusCode = 400;
- httpRes.ContentType = "text/plain";
- httpRes.Write("Invalid host");
-
- httpRes.Close();
- return;
- }
-
- if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase))
- {
- httpRes.StatusCode = 200;
- httpRes.AddHeader("Access-Control-Allow-Origin", "*");
- 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/html";
-
- httpRes.Close();
- }
-
- var operationName = httpReq.OperationName;
- var localPath = url.LocalPath;
-
- var urlString = url.OriginalString;
- var enableLog = EnableLogging(urlString, localPath);
- var urlToLog = urlString;
-
- if (enableLog)
- {
- urlToLog = GetUrlToLog(urlString);
- LoggerUtils.LogRequest(_logger, urlToLog, httpReq.HttpMethod, httpReq.UserAgent);
- }
-
- if (string.Equals(localPath, "/emby/", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase))
- {
- httpRes.RedirectToUrl(DefaultRedirectPath);
- return;
- }
- if (string.Equals(localPath, "/emby", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase))
- {
- httpRes.RedirectToUrl("emby/" + DefaultRedirectPath);
- return;
- }
-
- if (string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase) ||
- localPath.IndexOf("mediabrowser/web", StringComparison.OrdinalIgnoreCase) != -1)
- {
- httpRes.StatusCode = 200;
- httpRes.ContentType = "text/html";
- var newUrl = urlString.Replace("mediabrowser", "emby", StringComparison.OrdinalIgnoreCase)
- .Replace("/dashboard/", "/web/", StringComparison.OrdinalIgnoreCase);
-
- if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase))
- {
- httpRes.Write("<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" + newUrl + "\">" + newUrl + "</a></body></html>");
-
- httpRes.Close();
- return;
- }
- }
-
- if (localPath.IndexOf("dashboard/", StringComparison.OrdinalIgnoreCase) != -1 &&
- localPath.IndexOf("web/dashboard", StringComparison.OrdinalIgnoreCase) == -1)
- {
- httpRes.StatusCode = 200;
- httpRes.ContentType = "text/html";
- var newUrl = urlString.Replace("mediabrowser", "emby", StringComparison.OrdinalIgnoreCase)
- .Replace("/dashboard/", "/web/", StringComparison.OrdinalIgnoreCase);
-
- if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase))
- {
- httpRes.Write("<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" + newUrl + "\">" + newUrl + "</a></body></html>");
-
- httpRes.Close();
- return;
- }
- }
-
- if (string.Equals(localPath, "/web", StringComparison.OrdinalIgnoreCase))
- {
- httpRes.RedirectToUrl(DefaultRedirectPath);
- return;
- }
- if (string.Equals(localPath, "/web/", StringComparison.OrdinalIgnoreCase))
- {
- httpRes.RedirectToUrl("../" + DefaultRedirectPath);
- return;
- }
- if (string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase))
- {
- httpRes.RedirectToUrl(DefaultRedirectPath);
- return;
- }
- if (string.IsNullOrEmpty(localPath))
- {
- httpRes.RedirectToUrl("/" + DefaultRedirectPath);
- return;
- }
-
- if (string.Equals(localPath, "/emby/pin", StringComparison.OrdinalIgnoreCase))
- {
- httpRes.RedirectToUrl("web/pin.html");
- return;
- }
-
- if (!string.IsNullOrWhiteSpace(GlobalResponse))
- {
- httpRes.StatusCode = 503;
- httpRes.ContentType = "text/html";
- httpRes.Write(GlobalResponse);
-
- httpRes.Close();
- return;
- }
-
- var handler = HttpHandlerFactory.GetHandler(httpReq);
-
- var remoteIp = httpReq.RemoteIp;
-
- var serviceStackHandler = handler as IServiceStackHandler;
- if (serviceStackHandler != null)
- {
- var restHandler = serviceStackHandler as RestHandler;
- if (restHandler != null)
- {
- httpReq.OperationName = operationName = restHandler.RestPath.RequestType.GetOperationName();
- }
-
- try
- {
- await serviceStackHandler.ProcessRequestAsync(httpReq, httpRes, operationName).ConfigureAwait(false);
- }
- finally
- {
- httpRes.Close();
- var statusCode = httpRes.StatusCode;
-
- var duration = DateTime.Now - date;
-
- if (enableLog)
- {
- LoggerUtils.LogResponse(_logger, statusCode, urlToLog, remoteIp, duration);
- }
- }
- }
- else
- {
- httpRes.Close();
- }
- }
-
- /// <summary>
- /// Adds the rest handlers.
- /// </summary>
- /// <param name="services">The services.</param>
- public void Init(IEnumerable<IRestfulService> services)
- {
- _restServices.AddRange(services);
-
- ServiceController = CreateServiceController();
-
- _logger.Info("Calling ServiceStack AppHost.Init");
-
- base.Init();
- }
-
- public override RouteAttribute[] GetRouteAttributes(Type requestType)
- {
- var routes = base.GetRouteAttributes(requestType).ToList();
- var clone = routes.ToList();
-
- foreach (var route in clone)
- {
- routes.Add(new RouteAttribute(NormalizeEmbyRoutePath(route.Path), route.Verbs)
- {
- Notes = route.Notes,
- Priority = route.Priority,
- Summary = route.Summary
- });
-
- routes.Add(new RouteAttribute(NormalizeRoutePath(route.Path), route.Verbs)
- {
- Notes = route.Notes,
- Priority = route.Priority,
- Summary = route.Summary
- });
-
- routes.Add(new RouteAttribute(DoubleNormalizeEmbyRoutePath(route.Path), route.Verbs)
- {
- Notes = route.Notes,
- Priority = route.Priority,
- Summary = route.Summary
- });
- }
-
- return routes.ToArray();
- }
-
- private string NormalizeEmbyRoutePath(string path)
- {
- if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase))
- {
- return "/emby" + path;
- }
-
- return "emby/" + path;
- }
-
- private string DoubleNormalizeEmbyRoutePath(string path)
- {
- if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase))
- {
- return "/emby/emby" + path;
- }
-
- return "emby/emby/" + path;
- }
-
- private string NormalizeRoutePath(string path)
- {
- if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase))
- {
- return "/mediabrowser" + path;
- }
-
- return "mediabrowser/" + path;
- }
-
- /// <summary>
- /// Releases the specified instance.
- /// </summary>
- /// <param name="instance">The instance.</param>
- public override void Release(object instance)
- {
- // Leave this empty so SS doesn't try to dispose our objects
- }
-
- private bool _disposed;
- private readonly object _disposeLock = new object();
- protected virtual void Dispose(bool disposing)
- {
- if (_disposed) return;
- base.Dispose();
-
- lock (_disposeLock)
- {
- if (_disposed) return;
-
- if (disposing)
- {
- Stop();
- }
-
- //release unmanaged resources here...
- _disposed = true;
- }
- }
-
- public override void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- public void StartServer(IEnumerable<string> urlPrefixes, string certificatePath)
- {
- CertificatePath = certificatePath;
- UrlPrefixes = urlPrefixes.ToList();
- Start(UrlPrefixes.First());
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/HttpServer/Security/SessionAuthProvider.cs b/MediaBrowser.Server.Implementations/HttpServer/Security/SessionAuthProvider.cs
deleted file mode 100644
index 7c31731012..0000000000
--- a/MediaBrowser.Server.Implementations/HttpServer/Security/SessionAuthProvider.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using MediaBrowser.Controller.Net;
-using ServiceStack;
-using ServiceStack.Auth;
-
-namespace MediaBrowser.Server.Implementations.HttpServer.Security
-{
- public class SessionAuthProvider : CredentialsAuthProvider
- {
- private readonly ISessionContext _sessionContext;
-
- public SessionAuthProvider(ISessionContext sessionContext)
- {
- _sessionContext = sessionContext;
- }
-
- public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
- {
- return true;
- }
-
- public override bool IsAuthorized(IAuthSession session, IAuthTokens tokens, Authenticate request = null)
- {
- return true;
- }
-
- protected override void SaveUserAuth(IServiceBase authService, IAuthSession session, IAuthRepository authRepo, IAuthTokens tokens)
- {
- }
-
- public override object Authenticate(IServiceBase authService, IAuthSession session, Authenticate request)
- {
- return base.Authenticate(authService, session, request);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs b/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs
deleted file mode 100644
index 8a7c14eb66..0000000000
--- a/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using MediaBrowser.Common;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Logging;
-using ServiceStack.Logging;
-
-namespace MediaBrowser.Server.Implementations.HttpServer
-{
- /// <summary>
- /// Class ServerFactory
- /// </summary>
- public static class ServerFactory
- {
- /// <summary>
- /// Creates the server.
- /// </summary>
- /// <returns>IHttpServer.</returns>
- public static IHttpServer CreateServer(IApplicationHost applicationHost,
- ILogManager logManager,
- IServerConfigurationManager config,
- INetworkManager _networkmanager,
- IMemoryStreamProvider streamProvider,
- string serverName,
- string defaultRedirectpath)
- {
- LogManager.LogFactory = new ServerLogFactory(logManager);
-
- return new HttpListenerHost(applicationHost, logManager, config, serverName, defaultRedirectpath, _networkmanager, streamProvider);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/HttpServer/ServerLogFactory.cs b/MediaBrowser.Server.Implementations/HttpServer/ServerLogFactory.cs
deleted file mode 100644
index 40af3f3b05..0000000000
--- a/MediaBrowser.Server.Implementations/HttpServer/ServerLogFactory.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using System;
-using MediaBrowser.Model.Logging;
-using ServiceStack.Logging;
-
-namespace MediaBrowser.Server.Implementations.HttpServer
-{
- /// <summary>
- /// Class ServerLogFactory
- /// </summary>
- public class ServerLogFactory : ILogFactory
- {
- /// <summary>
- /// The _log manager
- /// </summary>
- private readonly ILogManager _logManager;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ServerLogFactory"/> class.
- /// </summary>
- /// <param name="logManager">The log manager.</param>
- public ServerLogFactory(ILogManager logManager)
- {
- _logManager = logManager;
- }
-
- /// <summary>
- /// Gets the logger.
- /// </summary>
- /// <param name="typeName">Name of the type.</param>
- /// <returns>ILog.</returns>
- public ILog GetLogger(string typeName)
- {
- return new ServerLogger(_logManager.GetLogger(typeName));
- }
-
- /// <summary>
- /// Gets the logger.
- /// </summary>
- /// <param name="type">The type.</param>
- /// <returns>ILog.</returns>
- public ILog GetLogger(Type type)
- {
- return GetLogger(type.Name);
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/HttpServer/ServerLogger.cs b/MediaBrowser.Server.Implementations/HttpServer/ServerLogger.cs
deleted file mode 100644
index bf79247841..0000000000
--- a/MediaBrowser.Server.Implementations/HttpServer/ServerLogger.cs
+++ /dev/null
@@ -1,194 +0,0 @@
-using MediaBrowser.Model.Logging;
-using ServiceStack.Logging;
-using System;
-
-namespace MediaBrowser.Server.Implementations.HttpServer
-{
- /// <summary>
- /// Class ServerLogger
- /// </summary>
- public class ServerLogger : ILog
- {
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ServerLogger"/> class.
- /// </summary>
- /// <param name="logger">The logger.</param>
- public ServerLogger(ILogger logger)
- {
- _logger = logger;
- }
-
- /// <summary>
- /// Logs a Debug message and exception.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <param name="exception">The exception.</param>
- public void Debug(object message, Exception exception)
- {
- _logger.ErrorException(GetMesssage(message), exception);
- }
-
- /// <summary>
- /// Logs a Debug message.
- /// </summary>
- /// <param name="message">The message.</param>
- public void Debug(object message)
- {
- // Way too verbose. Can always make this configurable if needed again.
- //_logger.Debug(GetMesssage(message));
- }
-
- /// <summary>
- /// Logs a Debug format message.
- /// </summary>
- /// <param name="format">The format.</param>
- /// <param name="args">The args.</param>
- public void DebugFormat(string format, params object[] args)
- {
- // Way too verbose. Can always make this configurable if needed again.
- //_logger.Debug(format, args);
- }
-
- /// <summary>
- /// Logs a Error message and exception.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <param name="exception">The exception.</param>
- public void Error(object message, Exception exception)
- {
- _logger.ErrorException(GetMesssage(message), exception);
- }
-
- /// <summary>
- /// Logs a Error message.
- /// </summary>
- /// <param name="message">The message.</param>
- public void Error(object message)
- {
- _logger.Error(GetMesssage(message));
- }
-
- /// <summary>
- /// Logs a Error format message.
- /// </summary>
- /// <param name="format">The format.</param>
- /// <param name="args">The args.</param>
- public void ErrorFormat(string format, params object[] args)
- {
- _logger.Error(format, args);
- }
-
- /// <summary>
- /// Logs a Fatal message and exception.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <param name="exception">The exception.</param>
- public void Fatal(object message, Exception exception)
- {
- _logger.FatalException(GetMesssage(message), exception);
- }
-
- /// <summary>
- /// Logs a Fatal message.
- /// </summary>
- /// <param name="message">The message.</param>
- public void Fatal(object message)
- {
- _logger.Fatal(GetMesssage(message));
- }
-
- /// <summary>
- /// Logs a Error format message.
- /// </summary>
- /// <param name="format">The format.</param>
- /// <param name="args">The args.</param>
- public void FatalFormat(string format, params object[] args)
- {
- _logger.Fatal(format, args);
- }
-
- /// <summary>
- /// Logs an Info message and exception.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <param name="exception">The exception.</param>
- public void Info(object message, Exception exception)
- {
- _logger.ErrorException(GetMesssage(message), exception);
- }
-
- /// <summary>
- /// Logs an Info message and exception.
- /// </summary>
- /// <param name="message">The message.</param>
- public void Info(object message)
- {
- _logger.Info(GetMesssage(message));
- }
-
- /// <summary>
- /// Logs an Info format message.
- /// </summary>
- /// <param name="format">The format.</param>
- /// <param name="args">The args.</param>
- public void InfoFormat(string format, params object[] args)
- {
- _logger.Info(format, args);
- }
-
- /// <summary>
- /// Gets or sets a value indicating whether this instance is debug enabled.
- /// </summary>
- /// <value><c>true</c> if this instance is debug enabled; otherwise, <c>false</c>.</value>
- public bool IsDebugEnabled
- {
- get { return true; }
- }
-
- /// <summary>
- /// Logs a Warning message and exception.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <param name="exception">The exception.</param>
- public void Warn(object message, Exception exception)
- {
- _logger.ErrorException(GetMesssage(message), exception);
- }
-
- /// <summary>
- /// Logs a Warning message.
- /// </summary>
- /// <param name="message">The message.</param>
- public void Warn(object message)
- {
- // Hide StringMapTypeDeserializer messages
- // _logger.Warn(GetMesssage(message));
- }
-
- /// <summary>
- /// Logs a Warning format message.
- /// </summary>
- /// <param name="format">The format.</param>
- /// <param name="args">The args.</param>
- public void WarnFormat(string format, params object[] args)
- {
- // Hide StringMapTypeDeserializer messages
- // _logger.Warn(format, args);
- }
-
- /// <summary>
- /// Gets the messsage.
- /// </summary>
- /// <param name="o">The o.</param>
- /// <returns>System.String.</returns>
- private string GetMesssage(object o)
- {
- return o == null ? string.Empty : o.ToString();
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/Extensions.cs b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/Extensions.cs
deleted file mode 100644
index 154313fb90..0000000000
--- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/Extensions.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using MediaBrowser.Model.Logging;
-using SocketHttpListener.Net;
-using System;
-
-namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
-{
- public static class Extensions
- {
- public static string GetOperationName(this HttpListenerRequest request)
- {
- return request.Url.Segments[request.Url.Segments.Length - 1];
- }
-
- public static void CloseOutputStream(this HttpListenerResponse response, ILogger logger)
- {
- try
- {
- response.OutputStream.Flush();
- response.OutputStream.Close();
- response.Close();
- }
- catch (Exception ex)
- {
- logger.ErrorException("Error in HttpListenerResponseWrapper: " + ex.Message, ex);
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs
deleted file mode 100644
index a58645ec54..0000000000
--- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs
+++ /dev/null
@@ -1,151 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Net;
-using MediaBrowser.Model.Logging;
-using ServiceStack;
-using ServiceStack.Host;
-using ServiceStack.Web;
-using HttpListenerResponse = SocketHttpListener.Net.HttpListenerResponse;
-
-namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
-{
- public class WebSocketSharpResponse : IHttpResponse
- {
- private readonly ILogger _logger;
- private readonly HttpListenerResponse response;
-
- public WebSocketSharpResponse(ILogger logger, HttpListenerResponse response, IRequest request)
- {
- _logger = logger;
- this.response = response;
- Items = new Dictionary<string, object>();
- Request = request;
- }
-
- public IRequest Request { get; private set; }
- public bool UseBufferedStream { get; set; }
- public Dictionary<string, object> Items { get; private set; }
- public object OriginalResponse
- {
- get { return response; }
- }
-
- public int StatusCode
- {
- get { return this.response.StatusCode; }
- set { this.response.StatusCode = value; }
- }
-
- public string StatusDescription
- {
- get { return this.response.StatusDescription; }
- set { this.response.StatusDescription = value; }
- }
-
- public string ContentType
- {
- get { return response.ContentType; }
- set { response.ContentType = value; }
- }
-
- public ICookies Cookies { get; set; }
-
- public void AddHeader(string name, string value)
- {
- if (string.Equals(name, "Content-Type", StringComparison.OrdinalIgnoreCase))
- {
- ContentType = value;
- return;
- }
-
- response.AddHeader(name, value);
- }
-
- public string GetHeader(string name)
- {
- return response.Headers[name];
- }
-
- public void Redirect(string url)
- {
- response.Redirect(url);
- }
-
- public Stream OutputStream
- {
- get { return response.OutputStream; }
- }
-
- public object Dto { get; set; }
-
- public void Write(string text)
- {
- var bOutput = System.Text.Encoding.UTF8.GetBytes(text);
- response.ContentLength64 = bOutput.Length;
-
- var outputStream = response.OutputStream;
- outputStream.Write(bOutput, 0, bOutput.Length);
- Close();
- }
-
- public void Close()
- {
- if (!this.IsClosed)
- {
- this.IsClosed = true;
-
- try
- {
- this.response.CloseOutputStream(_logger);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error closing HttpListener output stream", ex);
- }
- }
- }
-
- public void End()
- {
- Close();
- }
-
- public void Flush()
- {
- response.OutputStream.Flush();
- }
-
- public bool IsClosed
- {
- get;
- private set;
- }
-
- public void SetContentLength(long contentLength)
- {
- //you can happily set the Content-Length header in Asp.Net
- //but HttpListener will complain if you do - you have to set ContentLength64 on the response.
- //workaround: HttpListener throws "The parameter is incorrect" exceptions when we try to set the Content-Length header
- response.ContentLength64 = contentLength;
- }
-
- public void SetCookie(Cookie cookie)
- {
- var cookieStr = cookie.AsHeaderValue();
- response.Headers.Add(HttpHeaders.SetCookie, cookieStr);
- }
-
- public bool SendChunked
- {
- get { return response.SendChunked; }
- set { response.SendChunked = value; }
- }
-
- public bool KeepAlive { get; set; }
-
- public void ClearCookies()
- {
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
deleted file mode 100644
index 454abddddb..0000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ /dev/null
@@ -1,167 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text.RegularExpressions;
-using System.Threading;
-using System.Threading.Tasks;
-using CommonIO;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Logging;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
-{
- public class M3uParser
- {
- private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
- private readonly IHttpClient _httpClient;
- private readonly IServerApplicationHost _appHost;
-
- public M3uParser(ILogger logger, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost)
- {
- _logger = logger;
- _fileSystem = fileSystem;
- _httpClient = httpClient;
- _appHost = appHost;
- }
-
- public async Task<List<M3UChannel>> Parse(string url, string channelIdPrefix, string tunerHostId, CancellationToken cancellationToken)
- {
- var urlHash = url.GetMD5().ToString("N");
-
- // Read the file and display it line by line.
- using (var reader = new StreamReader(await GetListingsStream(url, cancellationToken).ConfigureAwait(false)))
- {
- return GetChannels(reader, urlHash, channelIdPrefix, tunerHostId);
- }
- }
-
- public Task<Stream> GetListingsStream(string url, CancellationToken cancellationToken)
- {
- if (url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
- {
- return _httpClient.Get(new HttpRequestOptions
- {
- Url = url,
- CancellationToken = cancellationToken,
- // Some data providers will require a user agent
- UserAgent = _appHost.FriendlyName + "/" + _appHost.ApplicationVersion
- });
- }
- return Task.FromResult(_fileSystem.OpenRead(url));
- }
-
- private List<M3UChannel> GetChannels(StreamReader reader, string urlHash, string channelIdPrefix, string tunerHostId)
- {
- var channels = new List<M3UChannel>();
- string line;
- string extInf = "";
- while ((line = reader.ReadLine()) != null)
- {
- line = line.Trim();
- if (string.IsNullOrWhiteSpace(line))
- {
- continue;
- }
-
- if (line.StartsWith("#EXTM3U", StringComparison.OrdinalIgnoreCase))
- {
- continue;
- }
-
- if (line.StartsWith("#EXTINF:", StringComparison.OrdinalIgnoreCase))
- {
- extInf = line.Substring(8).Trim();
- _logger.Info("Found m3u channel: {0}", extInf);
- }
- else if (!string.IsNullOrWhiteSpace(extInf) && !line.StartsWith("#", StringComparison.OrdinalIgnoreCase))
- {
- var channel = GetChannelnfo(extInf, tunerHostId, line);
- channel.Id = channelIdPrefix + urlHash + line.GetMD5().ToString("N");
- channel.Path = line;
- channels.Add(channel);
- extInf = "";
- }
- }
- return channels;
- }
- private M3UChannel GetChannelnfo(string extInf, string tunerHostId, string mediaUrl)
- {
- var titleIndex = extInf.LastIndexOf(',');
- var channel = new M3UChannel();
- channel.TunerHostId = tunerHostId;
-
- channel.Number = extInf.Trim().Split(' ')[0] ?? "0";
- channel.Name = extInf.Substring(titleIndex + 1);
-
- //Check for channel number with the format from SatIp
- int number;
- var numberIndex = channel.Name.IndexOf('.');
- if (numberIndex > 0)
- {
- if (int.TryParse(channel.Name.Substring(0, numberIndex), out number))
- {
- channel.Number = number.ToString();
- channel.Name = channel.Name.Substring(numberIndex + 1);
- }
- }
-
- if (string.Equals(channel.Number, "-1", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(mediaUrl))
- {
- channel.Number = Path.GetFileNameWithoutExtension(mediaUrl.Split('/').Last());
- }
-
- if (string.Equals(channel.Number, "-1", StringComparison.OrdinalIgnoreCase))
- {
- channel.Number = "0";
- }
-
- channel.ImageUrl = FindProperty("tvg-logo", extInf);
-
- var name = FindProperty("tvg-name", extInf);
- if (string.IsNullOrWhiteSpace(name))
- {
- name = FindProperty("tvg-id", extInf);
- }
-
- channel.Name = name;
-
- var numberString = FindProperty("tvg-id", extInf);
- if (string.IsNullOrWhiteSpace(numberString))
- {
- numberString = FindProperty("channel-id", extInf);
- }
-
- if (!string.IsNullOrWhiteSpace(numberString))
- {
- channel.Number = numberString;
- }
-
- return channel;
-
- }
- private string FindProperty(string property, string properties)
- {
- var reg = new Regex(@"([a-z0-9\-_]+)=\""([^""]+)\""", RegexOptions.IgnoreCase);
- var matches = reg.Matches(properties);
- foreach (Match match in matches)
- {
- if (match.Groups[1].Value == property)
- {
- return match.Groups[2].Value;
- }
- }
- return null;
- }
- }
-
-
- public class M3UChannel : ChannelInfo
- {
- public string Path { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Logging/PatternsLogger.cs b/MediaBrowser.Server.Implementations/Logging/PatternsLogger.cs
deleted file mode 100644
index 00b6cc5a8b..0000000000
--- a/MediaBrowser.Server.Implementations/Logging/PatternsLogger.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using Patterns.Logging;
-using System;
-
-namespace MediaBrowser.Server.Implementations.Logging
-{
- public class PatternsLogger : ILogger
- {
- private readonly Model.Logging.ILogger _logger;
-
- public PatternsLogger()
- : this(new Model.Logging.NullLogger())
- {
- }
-
- public PatternsLogger(Model.Logging.ILogger logger)
- {
- _logger = logger;
- }
-
- public void Debug(string message, params object[] paramList)
- {
- _logger.Debug(message, paramList);
- }
-
- public void Error(string message, params object[] paramList)
- {
- _logger.Error(message, paramList);
- }
-
- public void ErrorException(string message, Exception exception, params object[] paramList)
- {
- _logger.ErrorException(message, exception, paramList);
- }
-
- public void Fatal(string message, params object[] paramList)
- {
- _logger.Fatal(message, paramList);
- }
-
- public void FatalException(string message, Exception exception, params object[] paramList)
- {
- _logger.FatalException(message, exception, paramList);
- }
-
- public void Info(string message, params object[] paramList)
- {
- _logger.Info(message, paramList);
- }
-
- public void Warn(string message, params object[] paramList)
- {
- _logger.Warn(message, paramList);
- }
-
- public void Log(LogSeverity severity, string message, params object[] paramList)
- {
- }
-
- public void LogMultiline(string message, LogSeverity severity, System.Text.StringBuilder additionalContent)
- {
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
index f01a107df5..3bfbff6e67 100644
--- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
+++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
@@ -11,10 +11,9 @@
<AssemblyName>MediaBrowser.Server.Implementations</AssemblyName>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
- <TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
- <ReleaseVersion>
- </ReleaseVersion>
- <TargetFrameworkProfile />
+ <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>
@@ -42,343 +41,14 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
- <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
- </Reference>
- <Reference Include="Emby.XmlTv, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
- <HintPath>..\packages\Emby.XmlTv.1.0.0.56\lib\net45\Emby.XmlTv.dll</HintPath>
- <Private>True</Private>
- </Reference>
- <Reference Include="INIFileParser, Version=2.3.0.0, Culture=neutral, PublicKeyToken=79af7b307b65cf3c, processorArchitecture=MSIL">
- <HintPath>..\packages\ini-parser.2.3.0\lib\net20\INIFileParser.dll</HintPath>
- <Private>True</Private>
- </Reference>
- <Reference Include="Interfaces.IO">
- <HintPath>..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll</HintPath>
- </Reference>
- <Reference Include="MediaBrowser.Naming, Version=1.0.6059.24054, Culture=neutral, processorArchitecture=MSIL">
- <HintPath>..\packages\MediaBrowser.Naming.1.0.0.55\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll</HintPath>
- <Private>True</Private>
- </Reference>
- <Reference Include="MoreLinq">
- <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
- </Reference>
- <Reference Include="Patterns.Logging">
- <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
- </Reference>
- <Reference Include="ServiceStack.Api.Swagger">
- <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Api.Swagger.dll</HintPath>
- </Reference>
- <Reference Include="SimpleInjector, Version=3.2.2.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
- <HintPath>..\packages\SimpleInjector.3.2.2\lib\net45\SimpleInjector.dll</HintPath>
- <Private>True</Private>
- </Reference>
- <Reference Include="SocketHttpListener, Version=1.0.6109.26162, Culture=neutral, processorArchitecture=MSIL">
- <HintPath>..\packages\SocketHttpListener.1.0.0.40\lib\net45\SocketHttpListener.dll</HintPath>
- <Private>True</Private>
- </Reference>
- <Reference Include="System" />
- <Reference Include="System.Core" />
- <Reference Include="Microsoft.CSharp" />
- <Reference Include="System.Data" />
- <Reference Include="System.Net" />
- <Reference Include="System.Runtime.Serialization" />
- <Reference Include="System.Security" />
- <Reference Include="System.Web" />
- <Reference Include="System.Xml" />
- <Reference Include="ServiceStack">
- <HintPath>..\ThirdParty\ServiceStack\ServiceStack.dll</HintPath>
- </Reference>
- <Reference Include="ServiceStack.Client">
- <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Client.dll</HintPath>
- </Reference>
- <Reference Include="ServiceStack.Common">
- <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Common.dll</HintPath>
- </Reference>
- <Reference Include="ServiceStack.Interfaces">
- <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath>
- </Reference>
- <Reference Include="ServiceStack.Text">
- <HintPath>..\ThirdParty\ServiceStack.Text\ServiceStack.Text.dll</HintPath>
- </Reference>
- <Reference Include="System.Xml.Linq" />
- <Reference Include="UniversalDetector">
- <HintPath>..\ThirdParty\UniversalDetector\UniversalDetector.dll</HintPath>
- </Reference>
- </ItemGroup>
- <ItemGroup>
<Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link>
</Compile>
- <Compile Include="Activity\ActivityManager.cs" />
- <Compile Include="Activity\ActivityRepository.cs" />
- <Compile Include="Branding\BrandingConfigurationFactory.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\CollectionManager.cs" />
- <Compile Include="Collections\CollectionsDynamicFolder.cs" />
- <Compile Include="Collections\CollectionImageProvider.cs" />
- <Compile Include="Configuration\ServerConfigurationManager.cs" />
- <Compile Include="Connect\ConnectData.cs" />
- <Compile Include="Connect\ConnectManager.cs" />
- <Compile Include="Connect\Responses.cs" />
- <Compile Include="Connect\Validator.cs" />
- <Compile Include="Devices\DeviceManager.cs" />
- <Compile Include="Devices\DeviceRepository.cs" />
<Compile Include="Devices\CameraUploadsFolder.cs" />
- <Compile Include="Dto\DtoService.cs" />
- <Compile Include="EntryPoints\ActivityLogEntryPoint.cs" />
- <Compile Include="EntryPoints\AutomaticRestartEntryPoint.cs" />
- <Compile Include="EntryPoints\ExternalPortForwarding.cs" />
- <Compile Include="EntryPoints\LibraryChangedNotifier.cs" />
- <Compile Include="EntryPoints\LoadRegistrations.cs" />
- <Compile Include="EntryPoints\Notifications\Notifications.cs" />
- <Compile Include="EntryPoints\Notifications\WebSocketNotifier.cs" />
- <Compile Include="EntryPoints\RecordingNotifier.cs" />
- <Compile Include="EntryPoints\RefreshUsersMetadata.cs" />
- <Compile Include="EntryPoints\UsageEntryPoint.cs" />
- <Compile Include="Connect\ConnectEntryPoint.cs" />
- <Compile Include="EntryPoints\UsageReporter.cs" />
- <Compile Include="FileOrganization\EpisodeFileOrganizer.cs" />
- <Compile Include="FileOrganization\Extensions.cs" />
- <Compile Include="FileOrganization\FileOrganizationNotifier.cs" />
- <Compile Include="FileOrganization\FileOrganizationService.cs" />
- <Compile Include="FileOrganization\NameUtils.cs" />
- <Compile Include="FileOrganization\TvFolderOrganizer.cs" />
- <Compile Include="EntryPoints\UdpServerEntryPoint.cs" />
- <Compile Include="EntryPoints\ServerEventNotifier.cs" />
- <Compile Include="EntryPoints\UserDataChangeNotifier.cs" />
- <Compile Include="FileOrganization\OrganizerScheduledTask.cs" />
- <Compile Include="HttpServer\AsyncStreamWriter.cs" />
- <Compile Include="HttpServer\IHttpListener.cs" />
- <Compile Include="HttpServer\Security\AuthorizationContext.cs" />
- <Compile Include="HttpServer\ContainerAdapter.cs" />
- <Compile Include="HttpServer\GetSwaggerResource.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\Security\AuthService.cs" />
- <Compile Include="HttpServer\Security\SessionAuthProvider.cs" />
- <Compile Include="HttpServer\ServerFactory.cs" />
- <Compile Include="HttpServer\ServerLogFactory.cs" />
- <Compile Include="HttpServer\ServerLogger.cs" />
- <Compile Include="HttpServer\Security\SessionContext.cs" />
- <Compile Include="HttpServer\SocketSharp\HttpUtility.cs" />
- <Compile Include="HttpServer\SocketSharp\SharpWebSocket.cs" />
- <Compile Include="HttpServer\StreamWriter.cs" />
- <Compile Include="HttpServer\SwaggerService.cs" />
- <Compile Include="HttpServer\SocketSharp\Extensions.cs" />
- <Compile Include="HttpServer\SocketSharp\RequestMono.cs" />
- <Compile Include="HttpServer\SocketSharp\WebSocketSharpListener.cs" />
- <Compile Include="HttpServer\SocketSharp\WebSocketSharpRequest.cs" />
- <Compile Include="HttpServer\SocketSharp\WebSocketSharpResponse.cs" />
- <Compile Include="Intros\DefaultIntroProvider.cs" />
- <Compile Include="IO\FileRefresher.cs" />
- <Compile Include="IO\LibraryMonitor.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\Resolvers\SpecialFolderResolver.cs" />
- <Compile Include="Library\Resolvers\BaseVideoResolver.cs" />
- <Compile Include="Library\Resolvers\PhotoAlbumResolver.cs" />
- <Compile Include="Library\Resolvers\PhotoResolver.cs" />
- <Compile Include="Library\Resolvers\PlaylistResolver.cs" />
- <Compile Include="Library\SearchEngine.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\ItemResolver.cs" />
- <Compile Include="Library\Resolvers\FolderResolver.cs" />
- <Compile Include="Library\Resolvers\Movies\BoxSetResolver.cs" />
- <Compile Include="Library\Resolvers\Movies\MovieResolver.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\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="Library\Validators\YearsPostScanTask.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\TunerHosts\BaseTunerHost.cs" />
- <Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunHost.cs" />
- <Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunDiscovery.cs" />
- <Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunLiveStream.cs" />
- <Compile Include="LiveTv\TunerHosts\M3uParser.cs" />
- <Compile Include="LiveTv\TunerHosts\M3UTunerHost.cs" />
- <Compile Include="LiveTv\ProgramImageProvider.cs" />
- <Compile Include="LiveTv\RecordingImageProvider.cs" />
- <Compile Include="LiveTv\RefreshChannelsScheduledTask.cs" />
- <Compile Include="LiveTv\TunerHosts\MulticastStream.cs" />
- <Compile Include="LiveTv\TunerHosts\QueueStream.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\ChannelScan.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\ReportBlock.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpAppPacket.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpByePacket.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpListener.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpPacket.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpReceiverReportPacket.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpSenderReportPacket.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpSourceDescriptionPacket.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\SourceDescriptionBlock.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\SourceDescriptionItem.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtp\RtpListener.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtp\RtpPacket.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspMethod.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspRequest.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspResponse.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspSession.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspStatusCode.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\SatIpHost.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\SatIpDiscovery.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\TransmissionMode.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Utils.cs" />
- <Compile Include="Localization\LocalizationManager.cs" />
- <Compile Include="Logging\PatternsLogger.cs" />
- <Compile Include="MediaEncoder\EncodingManager.cs" />
- <Compile Include="Notifications\IConfigurableNotificationService.cs" />
- <Compile Include="Persistence\BaseSqliteRepository.cs" />
- <Compile Include="Persistence\CleanDatabaseScheduledTask.cs" />
- <Compile Include="Persistence\DataExtensions.cs" />
- <Compile Include="Persistence\IDbConnector.cs" />
- <Compile Include="Persistence\MediaStreamColumns.cs" />
- <Compile Include="Social\SharingManager.cs" />
- <Compile Include="Social\SharingRepository.cs" />
- <Compile Include="Sorting\StartDateComparer.cs" />
- <Compile Include="Sync\SyncHelper.cs" />
- <Compile Include="Sync\SyncJobOptions.cs" />
- <Compile Include="Sync\SyncNotificationEntryPoint.cs" />
- <Compile Include="UserViews\CollectionFolderImageProvider.cs" />
- <Compile Include="UserViews\DynamicImageProvider.cs" />
- <Compile Include="News\NewsEntryPoint.cs" />
- <Compile Include="News\NewsService.cs" />
- <Compile Include="Notifications\CoreNotificationTypes.cs" />
- <Compile Include="Notifications\InternalNotificationService.cs" />
- <Compile Include="Notifications\NotificationConfigurationFactory.cs" />
- <Compile Include="Notifications\NotificationManager.cs" />
- <Compile Include="Persistence\SqliteFileOrganizationRepository.cs" />
- <Compile Include="Notifications\SqliteNotificationsRepository.cs" />
- <Compile Include="Persistence\TypeMapper.cs" />
- <Compile Include="Photos\BaseDynamicImageProvider.cs" />
<Compile Include="Playlists\ManualPlaylistsFolder.cs" />
- <Compile Include="Photos\PhotoAlbumImageProvider.cs" />
- <Compile Include="Playlists\PlaylistImageProvider.cs" />
- <Compile Include="Playlists\PlaylistManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="ScheduledTasks\PeopleValidationTask.cs" />
- <Compile Include="ScheduledTasks\ChapterImagesTask.cs" />
- <Compile Include="ScheduledTasks\PluginUpdateTask.cs" />
- <Compile Include="ScheduledTasks\RefreshIntrosTask.cs" />
- <Compile Include="ScheduledTasks\RefreshMediaLibraryTask.cs" />
- <Compile Include="ScheduledTasks\SystemUpdateTask.cs" />
- <Compile Include="Security\AuthenticationRepository.cs" />
- <Compile Include="Security\EncryptionManager.cs" />
- <Compile Include="ServerApplicationPaths.cs" />
- <Compile Include="ServerManager\ServerManager.cs" />
- <Compile Include="ServerManager\WebSocketConnection.cs" />
- <Compile Include="Session\HttpSessionController.cs" />
- <Compile Include="Session\SessionManager.cs">
- <SubType>Code</SubType>
- </Compile>
- <Compile Include="Session\SessionWebSocketListener.cs" />
- <Compile Include="Session\WebSocketController.cs" />
- <Compile Include="Sorting\AiredEpisodeOrderComparer.cs" />
- <Compile Include="Sorting\AirTimeComparer.cs" />
- <Compile Include="Sorting\AlbumArtistComparer.cs" />
- <Compile Include="Sorting\AlbumComparer.cs" />
- <Compile Include="Sorting\AlphanumComparator.cs" />
- <Compile Include="Sorting\ArtistComparer.cs" />
- <Compile Include="Sorting\BudgetComparer.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\MetascoreComparer.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\RevenueComparer.cs" />
- <Compile Include="Sorting\RuntimeComparer.cs" />
- <Compile Include="Sorting\SeriesSortNameComparer.cs" />
- <Compile Include="Sorting\SortNameComparer.cs" />
- <Compile Include="Persistence\SqliteDisplayPreferencesRepository.cs" />
- <Compile Include="Persistence\SqliteItemRepository.cs" />
- <Compile Include="Persistence\SqliteUserDataRepository.cs" />
- <Compile Include="Persistence\SqliteUserRepository.cs" />
- <Compile Include="Sorting\StudioComparer.cs" />
- <Compile Include="Sorting\VideoBitRateComparer.cs" />
- <Compile Include="Sync\AppSyncProvider.cs" />
- <Compile Include="Sync\CloudSyncProfile.cs" />
- <Compile Include="Sync\IHasSyncQuality.cs" />
- <Compile Include="Sync\MediaSync.cs" />
- <Compile Include="Sync\MultiProviderSync.cs" />
- <Compile Include="Sync\ServerSyncScheduledTask.cs" />
- <Compile Include="Sync\SyncedMediaSourceProvider.cs" />
- <Compile Include="Sync\SyncRegistrationInfo.cs" />
- <Compile Include="Sync\SyncConfig.cs" />
- <Compile Include="Sync\SyncJobProcessor.cs" />
- <Compile Include="Sync\SyncManager.cs" />
- <Compile Include="Sync\SyncRepository.cs" />
- <Compile Include="Sync\SyncConvertScheduledTask.cs" />
- <Compile Include="Sync\TargetDataProvider.cs" />
- <Compile Include="TV\TVSeriesManager.cs" />
- <Compile Include="Udp\UdpMessageReceivedEventArgs.cs" />
- <Compile Include="Udp\UdpServer.cs" />
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\MediaBrowser.Common.Implementations\MediaBrowser.Common.Implementations.csproj">
- <Project>{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}</Project>
- <Name>MediaBrowser.Common.Implementations</Name>
- </ProjectReference>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
<Project>{9142EEFA-7570-41E1-BFCC-468BB571AF2F}</Project>
<Name>MediaBrowser.Common</Name>
@@ -391,395 +61,12 @@
<Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project>
<Name>MediaBrowser.Model</Name>
</ProjectReference>
- <ProjectReference Include="..\Mono.Nat\Mono.Nat.csproj">
- <Project>{d7453b88-2266-4805-b39b-2b5a2a33e1ba}</Project>
- <Name>Mono.Nat</Name>
- </ProjectReference>
</ItemGroup>
<ItemGroup>
- <EmbeddedResource Include="Localization\Ratings\us.txt" />
- <EmbeddedResource Include="Localization\Ratings\au.txt" />
- <EmbeddedResource Include="Localization\Ratings\gb.txt" />
- <EmbeddedResource Include="Localization\Ratings\nl.txt" />
- <EmbeddedResource Include="Localization\Ratings\br.txt" />
- <EmbeddedResource Include="Localization\Ratings\dk.txt" />
- <EmbeddedResource Include="Localization\Ratings\de.txt" />
- <EmbeddedResource Include="Localization\Ratings\mx.txt" />
- <EmbeddedResource Include="Localization\Ratings\co.txt" />
- <EmbeddedResource Include="Localization\Ratings\fr.txt" />
- <EmbeddedResource Include="Localization\Ratings\ie.txt" />
- <EmbeddedResource Include="Localization\Ratings\jp.txt" />
- <EmbeddedResource Include="Localization\Ratings\kz.txt" />
- <EmbeddedResource Include="Localization\Ratings\nz.txt" />
- <EmbeddedResource Include="Localization\Ratings\ru.txt" />
- </ItemGroup>
- <ItemGroup>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\lib\backbone-min.js">
- <Link>swagger-ui\lib\backbone-min.js</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\lib\handlebars-2.0.0.js">
- <Link>swagger-ui\lib\handlebars-2.0.0.js</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\lib\highlight.7.3.pack.js">
- <Link>swagger-ui\lib\highlight.7.3.pack.js</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\lib\jquery-1.8.0.min.js">
- <Link>swagger-ui\lib\jquery-1.8.0.min.js</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\lib\jquery.ba-bbq.min.js">
- <Link>swagger-ui\lib\jquery.ba-bbq.min.js</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\lib\jquery.slideto.min.js">
- <Link>swagger-ui\lib\jquery.slideto.min.js</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\lib\jquery.wiggle.min.js">
- <Link>swagger-ui\lib\jquery.wiggle.min.js</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\lib\marked.js">
- <Link>swagger-ui\lib\marked.js</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\lib\shred.bundle.js">
- <Link>swagger-ui\lib\shred.bundle.js</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\lib\swagger-client.js">
- <Link>swagger-ui\lib\swagger-client.js</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\lib\swagger-oauth.js">
- <Link>swagger-ui\lib\swagger-oauth.js</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\lib\underscore-min.js">
- <Link>swagger-ui\lib\underscore-min.js</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\o2c.html">
- <Link>swagger-ui\o2c.html</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\patch.js">
- <Link>swagger-ui\patch.js</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\swagger-ui.js">
- <Link>swagger-ui\swagger-ui.js</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\swagger-ui.min.js">
- <Link>swagger-ui\swagger-ui.min.js</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <EmbeddedResource Include="Localization\countries.json" />
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\fonts\droid-sans-v6-latin-700.eot">
- <Link>swagger-ui\fonts\droid-sans-v6-latin-700.eot</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\fonts\droid-sans-v6-latin-700.ttf">
- <Link>swagger-ui\fonts\droid-sans-v6-latin-700.ttf</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\fonts\droid-sans-v6-latin-700.woff">
- <Link>swagger-ui\fonts\droid-sans-v6-latin-700.woff</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\fonts\droid-sans-v6-latin-700.woff2">
- <Link>swagger-ui\fonts\droid-sans-v6-latin-700.woff2</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\fonts\droid-sans-v6-latin-regular.eot">
- <Link>swagger-ui\fonts\droid-sans-v6-latin-regular.eot</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\fonts\droid-sans-v6-latin-regular.ttf">
- <Link>swagger-ui\fonts\droid-sans-v6-latin-regular.ttf</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\fonts\droid-sans-v6-latin-regular.woff">
- <Link>swagger-ui\fonts\droid-sans-v6-latin-regular.woff</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\fonts\droid-sans-v6-latin-regular.woff2">
- <Link>swagger-ui\fonts\droid-sans-v6-latin-regular.woff2</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<None Include="app.config" />
- <EmbeddedResource Include="Localization\Core\core.json" />
- <EmbeddedResource Include="Localization\Core\ar.json" />
- <EmbeddedResource Include="Localization\Core\bg-BG.json" />
- <EmbeddedResource Include="Localization\Core\ca.json" />
- <EmbeddedResource Include="Localization\Core\cs.json" />
- <EmbeddedResource Include="Localization\Core\da.json" />
- <EmbeddedResource Include="Localization\Core\de.json" />
- <EmbeddedResource Include="Localization\Core\el.json" />
- <EmbeddedResource Include="Localization\Core\en-GB.json" />
- <EmbeddedResource Include="Localization\Core\en-US.json" />
- <EmbeddedResource Include="Localization\Core\es-AR.json" />
- <EmbeddedResource Include="Localization\Core\es-MX.json" />
- <EmbeddedResource Include="Localization\Core\es.json" />
- <EmbeddedResource Include="Localization\Core\fi.json" />
- <EmbeddedResource Include="Localization\Core\fr.json" />
- <EmbeddedResource Include="Localization\Core\gsw.json" />
- <EmbeddedResource Include="Localization\Core\he.json" />
- <EmbeddedResource Include="Localization\Core\hr.json" />
- <EmbeddedResource Include="Localization\Core\it.json" />
- <EmbeddedResource Include="Localization\Core\kk.json" />
- <EmbeddedResource Include="Localization\Core\ko.json" />
- <EmbeddedResource Include="Localization\Core\ms.json" />
- <EmbeddedResource Include="Localization\Core\nb.json" />
- <EmbeddedResource Include="Localization\Core\nl.json" />
- <EmbeddedResource Include="Localization\Core\pl.json" />
- <EmbeddedResource Include="Localization\Core\pt-BR.json" />
- <EmbeddedResource Include="Localization\Core\pt-PT.json" />
- <EmbeddedResource Include="Localization\Core\ro.json" />
- <EmbeddedResource Include="Localization\Core\ru.json" />
- <EmbeddedResource Include="Localization\Core\sl-SI.json" />
- <EmbeddedResource Include="Localization\Core\sv.json" />
- <EmbeddedResource Include="Localization\Core\tr.json" />
- <EmbeddedResource Include="Localization\Core\uk.json" />
- <EmbeddedResource Include="Localization\Core\vi.json" />
- <EmbeddedResource Include="Localization\Core\zh-CN.json" />
- <EmbeddedResource Include="Localization\Core\zh-TW.json" />
- <EmbeddedResource Include="Localization\Core\zh-HK.json" />
- <EmbeddedResource Include="Localization\Core\hu.json" />
- <EmbeddedResource Include="Localization\Core\id.json" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0030.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0049.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0070.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0090.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0100.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0130.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0160.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0170.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0192.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0200.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0215.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0235.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0255.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0260.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0282.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0305.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0308.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0310.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0315.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0330.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0360.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0380.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0390.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0400.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0420.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0435.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0450.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0460.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0475.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0480.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0490.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0505.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0510.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0520.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0525.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0530.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0549.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0560.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0570.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0600.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0620.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0642.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0650.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0660.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0685.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0705.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0721.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0740.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0750.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0765.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0785.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0830.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0851.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0865.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0875.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0880.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0900.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0915.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0922.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0935.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0950.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0965.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1005.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1030.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1055.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1082.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1100.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1105.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1130.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1155.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1160.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1180.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1195.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1222.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1240.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1250.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1280.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1320.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1340.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1380.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1400.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1440.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1500.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1520.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1540.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1560.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1590.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1600 OPTUS D1 FTA %28160.0E%29.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1600.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1620.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1640.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1660.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1690.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1720.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1800.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1830.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2210.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2230.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2250.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2270.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2290.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2310.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2330.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2350.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2370.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2390.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2410.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2432.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2451.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2470.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2489.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2500.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2527.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2550.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2570.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2590.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2608.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2630.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2650.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2669.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2690.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2710.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2728.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2730.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2750.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2760.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2770.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2780.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2812.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2820.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2830.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2850.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2873.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2880.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2881.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2882.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2900.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2930.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2950.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2970.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2985.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2990.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3020.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3045.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3070.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3100.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3125.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3150.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3169.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3195.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3225.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3255.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3285.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3300.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3325.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3355.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3380.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3400.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3420.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3450.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3460.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3475.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3490.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3520.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3527.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3550.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3560.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3592.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3594.ini" />
- <EmbeddedResource Include="Localization\Core\fr-CA.json" />
- <None Include="packages.config" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Localization\Ratings\ca.txt" />
- </ItemGroup>
- <ItemGroup>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\css\reset.css">
- <Link>swagger-ui\css\reset.css</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\css\screen.css">
- <Link>swagger-ui\css\screen.css</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\css\typography.css">
- <Link>swagger-ui\css\typography.css</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\fonts\droid-sans-v6-latin-700.svg">
- <Link>swagger-ui\fonts\droid-sans-v6-latin-700.svg</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\fonts\droid-sans-v6-latin-regular.svg">
- <Link>swagger-ui\fonts\droid-sans-v6-latin-regular.svg</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\images\explorer_icons.png">
- <Link>swagger-ui\images\explorer_icons.png</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\images\logo_small.png">
- <Link>swagger-ui\images\logo_small.png</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\images\pet_store_api.png">
- <Link>swagger-ui\images\pet_store_api.png</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\images\throbber.gif">
- <Link>swagger-ui\images\throbber.gif</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\images\wordnik_api.png">
- <Link>swagger-ui\images\wordnik_api.png</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\index.html">
- <Link>swagger-ui\index.html</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\ThirdParty\ServiceStack\swagger-ui\lib\shred\content.js">
- <Link>swagger-ui\lib\shred\content.js</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <EmbeddedResource Include="Localization\iso6392.txt" />
- <EmbeddedResource Include="Localization\Ratings\be.txt" />
</ItemGroup>
<ItemGroup />
- <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <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">
diff --git a/MediaBrowser.Server.Implementations/News/NewsEntryPoint.cs b/MediaBrowser.Server.Implementations/News/NewsEntryPoint.cs
deleted file mode 100644
index c15af6f701..0000000000
--- a/MediaBrowser.Server.Implementations/News/NewsEntryPoint.cs
+++ /dev/null
@@ -1,168 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Notifications;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.News;
-using MediaBrowser.Model.Notifications;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using System.Xml;
-using CommonIO;
-using MediaBrowser.Common.Threading;
-
-namespace MediaBrowser.Server.Implementations.News
-{
- public class NewsEntryPoint : IServerEntryPoint
- {
- private PeriodicTimer _timer;
- private readonly IHttpClient _httpClient;
- private readonly IApplicationPaths _appPaths;
- private readonly IFileSystem _fileSystem;
- private readonly ILogger _logger;
- private readonly IJsonSerializer _json;
-
- private readonly INotificationManager _notifications;
- private readonly IUserManager _userManager;
-
- private readonly TimeSpan _frequency = TimeSpan.FromHours(24);
-
- public NewsEntryPoint(IHttpClient httpClient, IApplicationPaths appPaths, IFileSystem fileSystem, ILogger logger, IJsonSerializer json, INotificationManager notifications, IUserManager userManager)
- {
- _httpClient = httpClient;
- _appPaths = appPaths;
- _fileSystem = fileSystem;
- _logger = logger;
- _json = json;
- _notifications = notifications;
- _userManager = userManager;
- }
-
- public void Run()
- {
- _timer = new PeriodicTimer(OnTimerFired, null, TimeSpan.FromMilliseconds(500), _frequency);
- }
-
- /// <summary>
- /// Called when [timer fired].
- /// </summary>
- /// <param name="state">The state.</param>
- private async void OnTimerFired(object state)
- {
- var path = Path.Combine(_appPaths.CachePath, "news.json");
-
- try
- {
- await DownloadNews(path).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error downloading news", ex);
- }
- }
-
- private async Task DownloadNews(string path)
- {
- DateTime? lastUpdate = null;
-
- if (_fileSystem.FileExists(path))
- {
- lastUpdate = _fileSystem.GetLastWriteTimeUtc(path);
- }
-
- var requestOptions = new HttpRequestOptions
- {
- Url = "http://emby.media/community/index.php?/blog/rss/1-media-browser-developers-blog",
- Progress = new Progress<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
- };
-
- using (var stream = await _httpClient.Get(requestOptions).ConfigureAwait(false))
- {
- var doc = new XmlDocument();
- doc.Load(stream);
-
- var news = ParseRssItems(doc).ToList();
-
- _json.SerializeToFile(news, path);
-
- await CreateNotifications(news, lastUpdate, CancellationToken.None).ConfigureAwait(false);
- }
- }
-
- private Task CreateNotifications(List<NewsItem> items, DateTime? lastUpdate, CancellationToken cancellationToken)
- {
- if (lastUpdate.HasValue)
- {
- items = items.Where(i => i.Date.ToUniversalTime() >= lastUpdate.Value)
- .ToList();
- }
-
- var tasks = items.Select(i => _notifications.SendNotification(new NotificationRequest
- {
- Date = i.Date,
- Name = i.Title,
- Description = i.Description,
- Url = i.Link,
- UserIds = _userManager.Users.Select(u => u.Id.ToString("N")).ToList()
-
- }, cancellationToken));
-
- return Task.WhenAll(tasks);
- }
-
- private IEnumerable<NewsItem> ParseRssItems(XmlDocument xmlDoc)
- {
- var nodes = xmlDoc.SelectNodes("rss/channel/item");
-
- if (nodes != null)
- {
- foreach (XmlNode node in nodes)
- {
- var newsItem = new NewsItem();
-
- newsItem.Title = ParseDocElements(node, "title");
-
- newsItem.DescriptionHtml = ParseDocElements(node, "description");
- newsItem.Description = newsItem.DescriptionHtml.StripHtml();
-
- newsItem.Link = ParseDocElements(node, "link");
-
- var date = ParseDocElements(node, "pubDate");
- DateTime parsedDate;
-
- if (DateTime.TryParse(date, out parsedDate))
- {
- newsItem.Date = parsedDate;
- }
-
- yield return newsItem;
- }
- }
- }
-
- private string ParseDocElements(XmlNode parent, string xPath)
- {
- var node = parent.SelectSingleNode(xPath);
-
- return node != null ? node.InnerText : string.Empty;
- }
-
- public void Dispose()
- {
- if (_timer != null)
- {
- _timer.Dispose();
- _timer = null;
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Notifications/SqliteNotificationsRepository.cs b/MediaBrowser.Server.Implementations/Notifications/SqliteNotificationsRepository.cs
deleted file mode 100644
index f30ba3e542..0000000000
--- a/MediaBrowser.Server.Implementations/Notifications/SqliteNotificationsRepository.cs
+++ /dev/null
@@ -1,470 +0,0 @@
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Notifications;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Notifications;
-using MediaBrowser.Server.Implementations.Persistence;
-using System;
-using System.Collections.Generic;
-using System.Data;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Notifications
-{
- public class SqliteNotificationsRepository : BaseSqliteRepository, INotificationsRepository
- {
- public SqliteNotificationsRepository(ILogManager logManager, IServerApplicationPaths appPaths, IDbConnector dbConnector) : base(logManager, dbConnector)
- {
- DbFilePath = Path.Combine(appPaths.DataPath, "notifications.db");
- }
-
- public event EventHandler<NotificationUpdateEventArgs> NotificationAdded;
- public event EventHandler<NotificationReadEventArgs> NotificationsMarkedRead;
- ////public event EventHandler<NotificationUpdateEventArgs> NotificationUpdated;
-
- public async Task Initialize()
- {
- using (var connection = await CreateConnection().ConfigureAwait(false))
- {
- 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, Url TEXT, Level TEXT NOT NULL, IsRead BOOLEAN NOT NULL, Category TEXT NOT NULL, RelatedId TEXT, 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, Logger);
- }
- }
-
- /// <summary>
- /// Gets the notifications.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <returns>NotificationResult.</returns>
- public NotificationResult GetNotifications(NotificationQuery query)
- {
- var result = new NotificationResult();
-
- using (var connection = CreateConnection(true).Result)
- {
- using (var cmd = connection.CreateCommand())
- {
- var clauses = new List<string>();
-
- if (query.IsRead.HasValue)
- {
- clauses.Add("IsRead=@IsRead");
- cmd.Parameters.Add(cmd, "@IsRead", DbType.Boolean).Value = query.IsRead.Value;
- }
-
- clauses.Add("UserId=@UserId");
- cmd.Parameters.Add(cmd, "@UserId", DbType.Guid).Value = new Guid(query.UserId);
-
- var whereClause = " where " + string.Join(" And ", clauses.ToArray());
-
- cmd.CommandText = string.Format("select count(Id) from Notifications{0};select Id,UserId,Date,Name,Description,Url,Level,IsRead,Category,RelatedId from Notifications{0} order by IsRead asc, Date desc", whereClause);
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
- {
- if (reader.Read())
- {
- result.TotalRecordCount = reader.GetInt32(0);
- }
-
- if (reader.NextResult())
- {
- var notifications = GetNotifications(reader);
-
- if (query.StartIndex.HasValue)
- {
- notifications = notifications.Skip(query.StartIndex.Value);
- }
-
- if (query.Limit.HasValue)
- {
- notifications = notifications.Take(query.Limit.Value);
- }
-
- result.Notifications = notifications.ToArray();
- }
- }
-
- return result;
- }
- }
- }
-
- public NotificationsSummary GetNotificationsSummary(string userId)
- {
- var result = new NotificationsSummary();
-
- using (var connection = CreateConnection(true).Result)
- {
- using (var cmd = connection.CreateCommand())
- {
- cmd.CommandText = "select Level from Notifications where UserId=@UserId and IsRead=@IsRead";
-
- cmd.Parameters.Add(cmd, "@UserId", DbType.Guid).Value = new Guid(userId);
- cmd.Parameters.Add(cmd, "@IsRead", DbType.Boolean).Value = false;
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
- {
- var levels = new List<NotificationLevel>();
-
- while (reader.Read())
- {
- levels.Add(GetLevel(reader, 0));
- }
-
- result.UnreadCount = levels.Count;
-
- if (levels.Count > 0)
- {
- result.MaxUnreadNotificationLevel = levels.Max();
- }
- }
-
- return result;
- }
- }
- }
-
- /// <summary>
- /// Gets the notifications.
- /// </summary>
- /// <param name="reader">The reader.</param>
- /// <returns>IEnumerable{Notification}.</returns>
- private IEnumerable<Notification> GetNotifications(IDataReader reader)
- {
- var list = new List<Notification>();
-
- while (reader.Read())
- {
- list.Add(GetNotification(reader));
- }
-
- return list;
- }
-
- private Notification GetNotification(IDataReader reader)
- {
- var notification = new Notification
- {
- Id = reader.GetGuid(0).ToString("N"),
- UserId = reader.GetGuid(1).ToString("N"),
- Date = reader.GetDateTime(2).ToUniversalTime(),
- Name = reader.GetString(3)
- };
-
- if (!reader.IsDBNull(4))
- {
- notification.Description = reader.GetString(4);
- }
-
- if (!reader.IsDBNull(5))
- {
- notification.Url = reader.GetString(5);
- }
-
- notification.Level = GetLevel(reader, 6);
- notification.IsRead = reader.GetBoolean(7);
-
- 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(IDataReader reader, int index)
- {
- NotificationLevel level;
-
- var val = reader.GetString(index);
-
- 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();
-
- using (var connection = await CreateConnection().ConfigureAwait(false))
- {
- using (var replaceNotificationCommand = connection.CreateCommand())
- {
- replaceNotificationCommand.CommandText = "replace into Notifications (Id, UserId, Date, Name, Description, Url, Level, IsRead, Category, RelatedId) values (@Id, @UserId, @Date, @Name, @Description, @Url, @Level, @IsRead, @Category, @RelatedId)";
-
- replaceNotificationCommand.Parameters.Add(replaceNotificationCommand, "@Id");
- replaceNotificationCommand.Parameters.Add(replaceNotificationCommand, "@UserId");
- replaceNotificationCommand.Parameters.Add(replaceNotificationCommand, "@Date");
- replaceNotificationCommand.Parameters.Add(replaceNotificationCommand, "@Name");
- replaceNotificationCommand.Parameters.Add(replaceNotificationCommand, "@Description");
- replaceNotificationCommand.Parameters.Add(replaceNotificationCommand, "@Url");
- replaceNotificationCommand.Parameters.Add(replaceNotificationCommand, "@Level");
- replaceNotificationCommand.Parameters.Add(replaceNotificationCommand, "@IsRead");
- replaceNotificationCommand.Parameters.Add(replaceNotificationCommand, "@Category");
- replaceNotificationCommand.Parameters.Add(replaceNotificationCommand, "@RelatedId");
-
- IDbTransaction transaction = null;
-
- try
- {
- transaction = connection.BeginTransaction();
-
- replaceNotificationCommand.GetParameter(0).Value = new Guid(notification.Id);
- replaceNotificationCommand.GetParameter(1).Value = new Guid(notification.UserId);
- replaceNotificationCommand.GetParameter(2).Value = notification.Date.ToUniversalTime();
- replaceNotificationCommand.GetParameter(3).Value = notification.Name;
- replaceNotificationCommand.GetParameter(4).Value = notification.Description;
- replaceNotificationCommand.GetParameter(5).Value = notification.Url;
- replaceNotificationCommand.GetParameter(6).Value = notification.Level.ToString();
- replaceNotificationCommand.GetParameter(7).Value = notification.IsRead;
- replaceNotificationCommand.GetParameter(8).Value = string.Empty;
- replaceNotificationCommand.GetParameter(9).Value = string.Empty;
-
- replaceNotificationCommand.Transaction = transaction;
-
- replaceNotificationCommand.ExecuteNonQuery();
-
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to save notification:", e);
-
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
- }
- }
- }
- }
- }
-
- /// <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();
-
- await MarkReadInternal(idArray, userId, isRead, cancellationToken).ConfigureAwait(false);
-
- if (NotificationsMarkedRead != null)
- {
- try
- {
- NotificationsMarkedRead(this, new NotificationReadEventArgs
- {
- IdList = list.ToArray(),
- 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 (var connection = await CreateConnection().ConfigureAwait(false))
- {
- using (var markAllReadCommand = connection.CreateCommand())
- {
- markAllReadCommand.CommandText = "update Notifications set IsRead=@IsRead where UserId=@UserId";
-
- markAllReadCommand.Parameters.Add(markAllReadCommand, "@UserId");
- markAllReadCommand.Parameters.Add(markAllReadCommand, "@IsRead");
-
- IDbTransaction transaction = null;
-
- try
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- transaction = connection.BeginTransaction();
-
- markAllReadCommand.GetParameter(0).Value = new Guid(userId);
- markAllReadCommand.GetParameter(1).Value = isRead;
-
- markAllReadCommand.ExecuteNonQuery();
-
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to save notification:", e);
-
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
- }
- }
- }
- }
- }
-
- private async Task MarkReadInternal(IEnumerable<Guid> notificationIdList, string userId, bool isRead, CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- using (var connection = await CreateConnection().ConfigureAwait(false))
- {
- using (var markReadCommand = connection.CreateCommand())
- {
- markReadCommand.CommandText = "update Notifications set IsRead=@IsRead where Id=@Id and UserId=@UserId";
-
- markReadCommand.Parameters.Add(markReadCommand, "@UserId");
- markReadCommand.Parameters.Add(markReadCommand, "@IsRead");
- markReadCommand.Parameters.Add(markReadCommand, "@Id");
-
- IDbTransaction transaction = null;
-
- try
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- transaction = connection.BeginTransaction();
-
- markReadCommand.GetParameter(0).Value = new Guid(userId);
- markReadCommand.GetParameter(1).Value = isRead;
-
- foreach (var id in notificationIdList)
- {
- markReadCommand.GetParameter(2).Value = id;
-
- markReadCommand.Transaction = transaction;
-
- markReadCommand.ExecuteNonQuery();
- }
-
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to save notification:", e);
-
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
- }
- }
- }
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Persistence/BaseSqliteRepository.cs b/MediaBrowser.Server.Implementations/Persistence/BaseSqliteRepository.cs
deleted file mode 100644
index 233ab56fed..0000000000
--- a/MediaBrowser.Server.Implementations/Persistence/BaseSqliteRepository.cs
+++ /dev/null
@@ -1,114 +0,0 @@
-using MediaBrowser.Model.Logging;
-using System;
-using System.Data;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Persistence
-{
- public abstract class BaseSqliteRepository : IDisposable
- {
- protected SemaphoreSlim WriteLock = new SemaphoreSlim(1, 1);
- protected readonly IDbConnector DbConnector;
- protected ILogger Logger;
-
- protected string DbFilePath { get; set; }
-
- protected BaseSqliteRepository(ILogManager logManager, IDbConnector dbConnector)
- {
- DbConnector = dbConnector;
- Logger = logManager.GetLogger(GetType().Name);
- }
-
- protected virtual bool EnableConnectionPooling
- {
- get { return true; }
- }
-
- protected virtual async Task<IDbConnection> CreateConnection(bool isReadOnly = false)
- {
- var connection = await DbConnector.Connect(DbFilePath, false, true).ConfigureAwait(false);
-
- connection.RunQueries(new[]
- {
- "pragma temp_store = memory"
-
- }, Logger);
-
- return connection;
- }
-
- private bool _disposed;
- protected void CheckDisposed()
- {
- if (_disposed)
- {
- throw new ObjectDisposedException(GetType().Name + " has been disposed and cannot be accessed.");
- }
- }
-
- public void Dispose()
- {
- _disposed = true;
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- protected async Task Vacuum(IDbConnection connection)
- {
- CheckDisposed();
-
- await WriteLock.WaitAsync().ConfigureAwait(false);
-
- try
- {
- using (var cmd = connection.CreateCommand())
- {
- cmd.CommandText = "vacuum";
- cmd.ExecuteNonQuery();
- }
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to vacuum:", e);
-
- throw;
- }
- finally
- {
- WriteLock.Release();
- }
- }
-
- private readonly object _disposeLock = new object();
-
- /// <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)
- {
- try
- {
- lock (_disposeLock)
- {
- WriteLock.Wait();
-
- CloseConnection();
- }
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error disposing database", ex);
- }
- }
- }
-
- protected virtual void CloseConnection()
- {
-
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Persistence/DataExtensions.cs b/MediaBrowser.Server.Implementations/Persistence/DataExtensions.cs
deleted file mode 100644
index 028465354c..0000000000
--- a/MediaBrowser.Server.Implementations/Persistence/DataExtensions.cs
+++ /dev/null
@@ -1,181 +0,0 @@
-using System.Text;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Data;
-using System.IO;
-using MediaBrowser.Common.IO;
-
-namespace MediaBrowser.Server.Implementations.Persistence
-{
- static class DataExtensions
- {
- /// <summary>
- /// Determines whether the specified conn is open.
- /// </summary>
- /// <param name="conn">The conn.</param>
- /// <returns><c>true</c> if the specified conn is open; otherwise, <c>false</c>.</returns>
- public static bool IsOpen(this IDbConnection conn)
- {
- return conn.State == ConnectionState.Open;
- }
-
- public static IDataParameter GetParameter(this IDbCommand cmd, int index)
- {
- return (IDataParameter)cmd.Parameters[index];
- }
-
- public static IDataParameter Add(this IDataParameterCollection paramCollection, IDbCommand cmd, string name, DbType type)
- {
- var param = cmd.CreateParameter();
-
- param.ParameterName = name;
- param.DbType = type;
-
- paramCollection.Add(param);
-
- return param;
- }
-
- public static IDataParameter Add(this IDataParameterCollection paramCollection, IDbCommand cmd, string name)
- {
- var param = cmd.CreateParameter();
-
- param.ParameterName = name;
-
- paramCollection.Add(param);
-
- return param;
- }
-
-
- /// <summary>
- /// Gets a stream from a DataReader at a given ordinal
- /// </summary>
- /// <returns>Stream.</returns>
- /// <exception cref="System.ArgumentNullException">reader</exception>
- public static Stream GetMemoryStream(this IDataReader reader, int ordinal, IMemoryStreamProvider streamProvider)
- {
- if (reader == null)
- {
- throw new ArgumentNullException("reader");
- }
-
- var memoryStream = streamProvider.CreateNew();
- var num = 0L;
- var array = new byte[4096];
- long bytes;
- do
- {
- bytes = reader.GetBytes(ordinal, num, array, 0, array.Length);
- memoryStream.Write(array, 0, (int)bytes);
- num += bytes;
- }
- while (bytes > 0L);
- memoryStream.Position = 0;
- return memoryStream;
- }
-
- /// <summary>
- /// Runs the queries.
- /// </summary>
- /// <param name="connection">The connection.</param>
- /// <param name="queries">The queries.</param>
- /// <param name="logger">The logger.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
- /// <exception cref="System.ArgumentNullException">queries</exception>
- public static void RunQueries(this IDbConnection connection, string[] queries, ILogger logger)
- {
- if (queries == null)
- {
- throw new ArgumentNullException("queries");
- }
-
- using (var tran = connection.BeginTransaction())
- {
- try
- {
- using (var cmd = connection.CreateCommand())
- {
- foreach (var query in queries)
- {
- cmd.Transaction = tran;
- cmd.CommandText = query;
- cmd.ExecuteNonQuery();
- }
- }
-
- tran.Commit();
- }
- catch (Exception e)
- {
- logger.ErrorException("Error running queries", e);
- tran.Rollback();
- throw;
- }
- }
- }
-
- public static void Attach(IDbConnection db, string path, string alias)
- {
- using (var cmd = db.CreateCommand())
- {
- cmd.CommandText = string.Format("attach @dbPath as {0};", alias);
- cmd.Parameters.Add(cmd, "@dbPath", DbType.String);
- cmd.GetParameter(0).Value = path;
-
- cmd.ExecuteNonQuery();
- }
- }
-
- /// <summary>
- /// Serializes to bytes.
- /// </summary>
- /// <returns>System.Byte[][].</returns>
- /// <exception cref="System.ArgumentNullException">obj</exception>
- public static byte[] SerializeToBytes(this IJsonSerializer json, object obj, IMemoryStreamProvider streamProvider)
- {
- if (obj == null)
- {
- throw new ArgumentNullException("obj");
- }
-
- using (var stream = streamProvider.CreateNew())
- {
- json.SerializeToStream(obj, stream);
- return stream.ToArray();
- }
- }
-
- public static void AddColumn(this IDbConnection connection, ILogger logger, string table, string columnName, string type)
- {
- using (var cmd = connection.CreateCommand())
- {
- cmd.CommandText = "PRAGMA table_info(" + table + ")";
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
- {
- while (reader.Read())
- {
- if (!reader.IsDBNull(1))
- {
- var name = reader.GetString(1);
-
- if (string.Equals(name, columnName, StringComparison.OrdinalIgnoreCase))
- {
- return;
- }
- }
- }
- }
- }
-
- var builder = new StringBuilder();
-
- builder.AppendLine("alter table " + table);
- builder.AppendLine("add column " + columnName + " " + type);
-
- connection.RunQueries(new[] { builder.ToString() }, logger);
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Persistence/IDbConnector.cs b/MediaBrowser.Server.Implementations/Persistence/IDbConnector.cs
deleted file mode 100644
index 596cf8407a..0000000000
--- a/MediaBrowser.Server.Implementations/Persistence/IDbConnector.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System.Data;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Persistence
-{
- public interface IDbConnector
- {
- Task<IDbConnection> Connect(string dbPath, bool isReadOnly, bool enablePooling = false, int? cacheSize = null);
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs b/MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs
deleted file mode 100644
index 1d9be2e0d6..0000000000
--- a/MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs
+++ /dev/null
@@ -1,408 +0,0 @@
-using System;
-using System.Data;
-using System.Text;
-using MediaBrowser.Model.Logging;
-
-namespace MediaBrowser.Server.Implementations.Persistence
-{
- public class MediaStreamColumns
- {
- private readonly IDbConnection _connection;
- private readonly ILogger _logger;
-
- public MediaStreamColumns(IDbConnection connection, ILogger logger)
- {
- _connection = connection;
- _logger = logger;
- }
-
- public void AddColumns()
- {
- AddPixelFormatColumnCommand();
- AddBitDepthCommand();
- AddIsAnamorphicColumn();
- AddKeyFramesColumn();
- AddRefFramesCommand();
- AddCodecTagColumn();
- AddCommentColumn();
- AddNalColumn();
- AddIsAvcColumn();
- AddTitleColumn();
- AddTimeBaseColumn();
- AddCodecTimeBaseColumn();
- }
-
- private void AddIsAvcColumn()
- {
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "PRAGMA table_info(mediastreams)";
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
- {
- while (reader.Read())
- {
- if (!reader.IsDBNull(1))
- {
- var name = reader.GetString(1);
-
- if (string.Equals(name, "IsAvc", StringComparison.OrdinalIgnoreCase))
- {
- return;
- }
- }
- }
- }
- }
-
- var builder = new StringBuilder();
-
- builder.AppendLine("alter table mediastreams");
- builder.AppendLine("add column IsAvc BIT NULL");
-
- _connection.RunQueries(new[] { builder.ToString() }, _logger);
- }
-
- private void AddTimeBaseColumn()
- {
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "PRAGMA table_info(mediastreams)";
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
- {
- while (reader.Read())
- {
- if (!reader.IsDBNull(1))
- {
- var name = reader.GetString(1);
-
- if (string.Equals(name, "TimeBase", StringComparison.OrdinalIgnoreCase))
- {
- return;
- }
- }
- }
- }
- }
-
- var builder = new StringBuilder();
-
- builder.AppendLine("alter table mediastreams");
- builder.AppendLine("add column TimeBase TEXT");
-
- _connection.RunQueries(new[] { builder.ToString() }, _logger);
- }
-
- private void AddCodecTimeBaseColumn()
- {
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "PRAGMA table_info(mediastreams)";
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
- {
- while (reader.Read())
- {
- if (!reader.IsDBNull(1))
- {
- var name = reader.GetString(1);
-
- if (string.Equals(name, "CodecTimeBase", StringComparison.OrdinalIgnoreCase))
- {
- return;
- }
- }
- }
- }
- }
-
- var builder = new StringBuilder();
-
- builder.AppendLine("alter table mediastreams");
- builder.AppendLine("add column CodecTimeBase TEXT");
-
- _connection.RunQueries(new[] { builder.ToString() }, _logger);
- }
-
- private void AddTitleColumn()
- {
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "PRAGMA table_info(mediastreams)";
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
- {
- while (reader.Read())
- {
- if (!reader.IsDBNull(1))
- {
- var name = reader.GetString(1);
-
- if (string.Equals(name, "Title", StringComparison.OrdinalIgnoreCase))
- {
- return;
- }
- }
- }
- }
- }
-
- var builder = new StringBuilder();
-
- builder.AppendLine("alter table mediastreams");
- builder.AppendLine("add column Title TEXT");
-
- _connection.RunQueries(new[] { builder.ToString() }, _logger);
- }
-
- private void AddNalColumn()
- {
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "PRAGMA table_info(mediastreams)";
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
- {
- while (reader.Read())
- {
- if (!reader.IsDBNull(1))
- {
- var name = reader.GetString(1);
-
- if (string.Equals(name, "NalLengthSize", StringComparison.OrdinalIgnoreCase))
- {
- return;
- }
- }
- }
- }
- }
-
- var builder = new StringBuilder();
-
- builder.AppendLine("alter table mediastreams");
- builder.AppendLine("add column NalLengthSize TEXT");
-
- _connection.RunQueries(new[] { builder.ToString() }, _logger);
- }
-
- private void AddCommentColumn()
- {
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "PRAGMA table_info(mediastreams)";
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
- {
- while (reader.Read())
- {
- if (!reader.IsDBNull(1))
- {
- var name = reader.GetString(1);
-
- if (string.Equals(name, "Comment", StringComparison.OrdinalIgnoreCase))
- {
- return;
- }
- }
- }
- }
- }
-
- var builder = new StringBuilder();
-
- builder.AppendLine("alter table mediastreams");
- builder.AppendLine("add column Comment TEXT");
-
- _connection.RunQueries(new[] { builder.ToString() }, _logger);
- }
-
- private void AddCodecTagColumn()
- {
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "PRAGMA table_info(mediastreams)";
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
- {
- while (reader.Read())
- {
- if (!reader.IsDBNull(1))
- {
- var name = reader.GetString(1);
-
- if (string.Equals(name, "CodecTag", StringComparison.OrdinalIgnoreCase))
- {
- return;
- }
- }
- }
- }
- }
-
- var builder = new StringBuilder();
-
- builder.AppendLine("alter table mediastreams");
- builder.AppendLine("add column CodecTag TEXT");
-
- _connection.RunQueries(new[] { builder.ToString() }, _logger);
- }
-
- private void AddPixelFormatColumnCommand()
- {
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "PRAGMA table_info(mediastreams)";
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
- {
- while (reader.Read())
- {
- if (!reader.IsDBNull(1))
- {
- var name = reader.GetString(1);
-
- if (string.Equals(name, "PixelFormat", StringComparison.OrdinalIgnoreCase))
- {
- return;
- }
- }
- }
- }
- }
-
- var builder = new StringBuilder();
-
- builder.AppendLine("alter table mediastreams");
- builder.AppendLine("add column PixelFormat TEXT");
-
- _connection.RunQueries(new[] { builder.ToString() }, _logger);
- }
-
- private void AddBitDepthCommand()
- {
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "PRAGMA table_info(mediastreams)";
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
- {
- while (reader.Read())
- {
- if (!reader.IsDBNull(1))
- {
- var name = reader.GetString(1);
-
- if (string.Equals(name, "BitDepth", StringComparison.OrdinalIgnoreCase))
- {
- return;
- }
- }
- }
- }
- }
-
- var builder = new StringBuilder();
-
- builder.AppendLine("alter table mediastreams");
- builder.AppendLine("add column BitDepth INT NULL");
-
- _connection.RunQueries(new[] { builder.ToString() }, _logger);
- }
-
- private void AddRefFramesCommand()
- {
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "PRAGMA table_info(mediastreams)";
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
- {
- while (reader.Read())
- {
- if (!reader.IsDBNull(1))
- {
- var name = reader.GetString(1);
-
- if (string.Equals(name, "RefFrames", StringComparison.OrdinalIgnoreCase))
- {
- return;
- }
- }
- }
- }
- }
-
- var builder = new StringBuilder();
-
- builder.AppendLine("alter table mediastreams");
- builder.AppendLine("add column RefFrames INT NULL");
-
- _connection.RunQueries(new[] { builder.ToString() }, _logger);
- }
-
- private void AddKeyFramesColumn()
- {
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "PRAGMA table_info(mediastreams)";
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
- {
- while (reader.Read())
- {
- if (!reader.IsDBNull(1))
- {
- var name = reader.GetString(1);
-
- if (string.Equals(name, "KeyFrames", StringComparison.OrdinalIgnoreCase))
- {
- return;
- }
- }
- }
- }
- }
-
- var builder = new StringBuilder();
-
- builder.AppendLine("alter table mediastreams");
- builder.AppendLine("add column KeyFrames TEXT NULL");
-
- _connection.RunQueries(new[] { builder.ToString() }, _logger);
- }
-
- private void AddIsAnamorphicColumn()
- {
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "PRAGMA table_info(mediastreams)";
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
- {
- while (reader.Read())
- {
- if (!reader.IsDBNull(1))
- {
- var name = reader.GetString(1);
-
- if (string.Equals(name, "IsAnamorphic", StringComparison.OrdinalIgnoreCase))
- {
- return;
- }
- }
- }
- }
- }
-
- var builder = new StringBuilder();
-
- builder.AppendLine("alter table mediastreams");
- builder.AppendLine("add column IsAnamorphic BIT NULL");
-
- _connection.RunQueries(new[] { builder.ToString() }, _logger);
- }
-
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs
deleted file mode 100644
index 1726a77a6b..0000000000
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs
+++ /dev/null
@@ -1,312 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Collections.Generic;
-using System.Data;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-
-namespace MediaBrowser.Server.Implementations.Persistence
-{
- /// <summary>
- /// Class SQLiteDisplayPreferencesRepository
- /// </summary>
- public class SqliteDisplayPreferencesRepository : BaseSqliteRepository, IDisplayPreferencesRepository
- {
- private readonly IMemoryStreamProvider _memoryStreamProvider;
-
- public SqliteDisplayPreferencesRepository(ILogManager logManager, IJsonSerializer jsonSerializer, IApplicationPaths appPaths, IDbConnector dbConnector, IMemoryStreamProvider memoryStreamProvider)
- : base(logManager, dbConnector)
- {
- _jsonSerializer = jsonSerializer;
- _memoryStreamProvider = memoryStreamProvider;
- DbFilePath = Path.Combine(appPaths.DataPath, "displaypreferences.db");
- }
-
- /// <summary>
- /// Gets the name of the repository
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get
- {
- return "SQLite";
- }
- }
-
- /// <summary>
- /// The _json serializer
- /// </summary>
- private readonly IJsonSerializer _jsonSerializer;
-
- /// <summary>
- /// Opens the connection to the database
- /// </summary>
- /// <returns>Task.</returns>
- public async Task Initialize()
- {
- using (var connection = await CreateConnection().ConfigureAwait(false))
- {
- string[] queries = {
-
- "create table if not exists userdisplaypreferences (id GUID, userId GUID, client text, data BLOB)",
- "create unique index if not exists userdisplaypreferencesindex on userdisplaypreferences (id, userId, client)"
- };
-
- connection.RunQueries(queries, Logger);
- }
- }
-
- /// <summary>
- /// Save the display preferences associated with an item in the repo
- /// </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>
- /// <exception cref="System.ArgumentNullException">item</exception>
- public async Task SaveDisplayPreferences(DisplayPreferences displayPreferences, Guid userId, string client, CancellationToken cancellationToken)
- {
- if (displayPreferences == null)
- {
- throw new ArgumentNullException("displayPreferences");
- }
- if (string.IsNullOrWhiteSpace(displayPreferences.Id))
- {
- throw new ArgumentNullException("displayPreferences.Id");
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- var serialized = _jsonSerializer.SerializeToBytes(displayPreferences, _memoryStreamProvider);
-
- using (var connection = await CreateConnection().ConfigureAwait(false))
- {
- IDbTransaction transaction = null;
-
- try
- {
- transaction = connection.BeginTransaction();
-
- using (var cmd = connection.CreateCommand())
- {
- cmd.CommandText = "replace into userdisplaypreferences (id, userid, client, data) values (@1, @2, @3, @4)";
-
- cmd.Parameters.Add(cmd, "@1", DbType.Guid).Value = new Guid(displayPreferences.Id);
- cmd.Parameters.Add(cmd, "@2", DbType.Guid).Value = userId;
- cmd.Parameters.Add(cmd, "@3", DbType.String).Value = client;
- cmd.Parameters.Add(cmd, "@4", DbType.Binary).Value = serialized;
-
- cmd.Transaction = transaction;
-
- cmd.ExecuteNonQuery();
- }
-
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to save display preferences:", e);
-
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
- }
- }
- }
- }
-
- /// <summary>
- /// Save all display preferences associated with a user in the repo
- /// </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>
- /// <exception cref="System.ArgumentNullException">item</exception>
- public async Task SaveAllDisplayPreferences(IEnumerable<DisplayPreferences> displayPreferences, Guid userId, CancellationToken cancellationToken)
- {
- if (displayPreferences == null)
- {
- throw new ArgumentNullException("displayPreferences");
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- using (var connection = await CreateConnection().ConfigureAwait(false))
- {
- IDbTransaction transaction = null;
-
- try
- {
- transaction = connection.BeginTransaction();
-
- foreach (var displayPreference in displayPreferences)
- {
-
- var serialized = _jsonSerializer.SerializeToBytes(displayPreference, _memoryStreamProvider);
-
- using (var cmd = connection.CreateCommand())
- {
- cmd.CommandText = "replace into userdisplaypreferences (id, userid, client, data) values (@1, @2, @3, @4)";
-
- cmd.Parameters.Add(cmd, "@1", DbType.Guid).Value = new Guid(displayPreference.Id);
- cmd.Parameters.Add(cmd, "@2", DbType.Guid).Value = userId;
- cmd.Parameters.Add(cmd, "@3", DbType.String).Value = displayPreference.Client;
- cmd.Parameters.Add(cmd, "@4", DbType.Binary).Value = serialized;
-
- cmd.Transaction = transaction;
-
- cmd.ExecuteNonQuery();
- }
- }
-
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to save display preferences:", e);
-
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
- }
- }
- }
- }
-
- /// <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>
- /// <exception cref="System.ArgumentNullException">item</exception>
- public DisplayPreferences GetDisplayPreferences(string displayPreferencesId, Guid userId, string client)
- {
- if (string.IsNullOrWhiteSpace(displayPreferencesId))
- {
- throw new ArgumentNullException("displayPreferencesId");
- }
-
- var guidId = displayPreferencesId.GetMD5();
-
- using (var connection = CreateConnection(true).Result)
- {
- using (var cmd = connection.CreateCommand())
- {
- cmd.CommandText = "select data from userdisplaypreferences where id = @id and userId=@userId and client=@client";
-
- cmd.Parameters.Add(cmd, "@id", DbType.Guid).Value = guidId;
- cmd.Parameters.Add(cmd, "@userId", DbType.Guid).Value = userId;
- cmd.Parameters.Add(cmd, "@client", DbType.String).Value = client;
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
- {
- if (reader.Read())
- {
- using (var stream = reader.GetMemoryStream(0, _memoryStreamProvider))
- {
- return _jsonSerializer.DeserializeFromStream<DisplayPreferences>(stream);
- }
- }
- }
-
- return new DisplayPreferences
- {
- Id = guidId.ToString("N")
- };
- }
- }
- }
-
- /// <summary>
- /// Gets all display preferences for the given user.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <returns>Task{DisplayPreferences}.</returns>
- /// <exception cref="System.ArgumentNullException">item</exception>
- public IEnumerable<DisplayPreferences> GetAllDisplayPreferences(Guid userId)
- {
- var list = new List<DisplayPreferences>();
-
- using (var connection = CreateConnection(true).Result)
- {
- using (var cmd = connection.CreateCommand())
- {
- cmd.CommandText = "select data from userdisplaypreferences where userId=@userId";
-
- cmd.Parameters.Add(cmd, "@userId", DbType.Guid).Value = userId;
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
- {
- while (reader.Read())
- {
- using (var stream = reader.GetMemoryStream(0, _memoryStreamProvider))
- {
- list.Add(_jsonSerializer.DeserializeFromStream<DisplayPreferences>(stream));
- }
- }
- }
- }
- }
-
- return list;
- }
-
- public Task SaveDisplayPreferences(DisplayPreferences displayPreferences, string userId, string client, CancellationToken cancellationToken)
- {
- return SaveDisplayPreferences(displayPreferences, new Guid(userId), client, cancellationToken);
- }
-
- public DisplayPreferences GetDisplayPreferences(string displayPreferencesId, string userId, string client)
- {
- return GetDisplayPreferences(displayPreferencesId, new Guid(userId), client);
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs
deleted file mode 100644
index c273d49458..0000000000
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using System;
-using System.Data;
-using System.Data.SQLite;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Logging;
-
-namespace MediaBrowser.Server.Implementations.Persistence
-{
- /// <summary>
- /// Class SQLiteExtensions
- /// </summary>
- public static class SqliteExtensions
- {
- /// <summary>
- /// Connects to db.
- /// </summary>
- public static async Task<IDbConnection> ConnectToDb(string dbPath, bool isReadOnly, bool enablePooling, int? cacheSize, ILogger logger)
- {
- if (string.IsNullOrEmpty(dbPath))
- {
- throw new ArgumentNullException("dbPath");
- }
-
- SQLiteConnection.SetMemoryStatus(false);
-
- var connectionstr = new SQLiteConnectionStringBuilder
- {
- PageSize = 4096,
- CacheSize = cacheSize ?? 2000,
- SyncMode = SynchronizationModes.Normal,
- DataSource = dbPath,
- JournalMode = SQLiteJournalModeEnum.Wal,
-
- // This is causing crashing under linux
- Pooling = enablePooling && Environment.OSVersion.Platform == PlatformID.Win32NT,
- ReadOnly = isReadOnly
- };
-
- var connectionString = connectionstr.ConnectionString;
-
- if (!enablePooling)
- {
- logger.Info("Sqlite {0} opening {1}", SQLiteConnection.SQLiteVersion, connectionString);
- }
-
- var connection = new SQLiteConnection(connectionString);
-
- await connection.OpenAsync().ConfigureAwait(false);
-
- return connection;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteFileOrganizationRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteFileOrganizationRepository.cs
deleted file mode 100644
index 7a5e000905..0000000000
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteFileOrganizationRepository.cs
+++ /dev/null
@@ -1,408 +0,0 @@
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Model.FileOrganization;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Querying;
-using System;
-using System.Collections.Generic;
-using System.Data;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Persistence
-{
- public class SqliteFileOrganizationRepository : BaseSqliteRepository, IFileOrganizationRepository, IDisposable
- {
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
- public SqliteFileOrganizationRepository(ILogManager logManager, IServerApplicationPaths appPaths, IDbConnector connector) : base(logManager, connector)
- {
- DbFilePath = Path.Combine(appPaths.DataPath, "fileorganization.db");
- }
-
- /// <summary>
- /// Opens the connection to the database
- /// </summary>
- /// <returns>Task.</returns>
- public async Task Initialize()
- {
- using (var connection = await CreateConnection().ConfigureAwait(false))
- {
- string[] queries = {
-
- "create table if not exists FileOrganizerResults (ResultId GUID PRIMARY KEY, OriginalPath TEXT, TargetPath TEXT, FileLength INT, OrganizationDate datetime, Status TEXT, OrganizationType TEXT, StatusMessage TEXT, ExtractedName TEXT, ExtractedYear int null, ExtractedSeasonNumber int null, ExtractedEpisodeNumber int null, ExtractedEndingEpisodeNumber, DuplicatePaths TEXT int null)",
- "create index if not exists idx_FileOrganizerResults on FileOrganizerResults(ResultId)"
- };
-
- connection.RunQueries(queries, Logger);
- }
- }
-
- public async Task SaveResult(FileOrganizationResult result, CancellationToken cancellationToken)
- {
- if (result == null)
- {
- throw new ArgumentNullException("result");
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- using (var connection = await CreateConnection().ConfigureAwait(false))
- {
- using (var saveResultCommand = connection.CreateCommand())
- {
- saveResultCommand.CommandText = "replace into FileOrganizerResults (ResultId, OriginalPath, TargetPath, FileLength, OrganizationDate, Status, OrganizationType, StatusMessage, ExtractedName, ExtractedYear, ExtractedSeasonNumber, ExtractedEpisodeNumber, ExtractedEndingEpisodeNumber, DuplicatePaths) values (@ResultId, @OriginalPath, @TargetPath, @FileLength, @OrganizationDate, @Status, @OrganizationType, @StatusMessage, @ExtractedName, @ExtractedYear, @ExtractedSeasonNumber, @ExtractedEpisodeNumber, @ExtractedEndingEpisodeNumber, @DuplicatePaths)";
-
- saveResultCommand.Parameters.Add(saveResultCommand, "@ResultId");
- saveResultCommand.Parameters.Add(saveResultCommand, "@OriginalPath");
- saveResultCommand.Parameters.Add(saveResultCommand, "@TargetPath");
- saveResultCommand.Parameters.Add(saveResultCommand, "@FileLength");
- saveResultCommand.Parameters.Add(saveResultCommand, "@OrganizationDate");
- saveResultCommand.Parameters.Add(saveResultCommand, "@Status");
- saveResultCommand.Parameters.Add(saveResultCommand, "@OrganizationType");
- saveResultCommand.Parameters.Add(saveResultCommand, "@StatusMessage");
- saveResultCommand.Parameters.Add(saveResultCommand, "@ExtractedName");
- saveResultCommand.Parameters.Add(saveResultCommand, "@ExtractedYear");
- saveResultCommand.Parameters.Add(saveResultCommand, "@ExtractedSeasonNumber");
- saveResultCommand.Parameters.Add(saveResultCommand, "@ExtractedEpisodeNumber");
- saveResultCommand.Parameters.Add(saveResultCommand, "@ExtractedEndingEpisodeNumber");
- saveResultCommand.Parameters.Add(saveResultCommand, "@DuplicatePaths");
-
- IDbTransaction transaction = null;
-
- try
- {
- transaction = connection.BeginTransaction();
-
- var index = 0;
-
- saveResultCommand.GetParameter(index++).Value = new Guid(result.Id);
- saveResultCommand.GetParameter(index++).Value = result.OriginalPath;
- saveResultCommand.GetParameter(index++).Value = result.TargetPath;
- saveResultCommand.GetParameter(index++).Value = result.FileSize;
- saveResultCommand.GetParameter(index++).Value = result.Date;
- saveResultCommand.GetParameter(index++).Value = result.Status.ToString();
- saveResultCommand.GetParameter(index++).Value = result.Type.ToString();
- saveResultCommand.GetParameter(index++).Value = result.StatusMessage;
- saveResultCommand.GetParameter(index++).Value = result.ExtractedName;
- saveResultCommand.GetParameter(index++).Value = result.ExtractedYear;
- saveResultCommand.GetParameter(index++).Value = result.ExtractedSeasonNumber;
- saveResultCommand.GetParameter(index++).Value = result.ExtractedEpisodeNumber;
- saveResultCommand.GetParameter(index++).Value = result.ExtractedEndingEpisodeNumber;
- saveResultCommand.GetParameter(index).Value = string.Join("|", result.DuplicatePaths.ToArray());
-
- saveResultCommand.Transaction = transaction;
-
- saveResultCommand.ExecuteNonQuery();
-
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to save FileOrganizationResult:", e);
-
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
- }
- }
- }
- }
- }
-
- public async Task Delete(string id)
- {
- if (string.IsNullOrEmpty(id))
- {
- throw new ArgumentNullException("id");
- }
-
- using (var connection = await CreateConnection().ConfigureAwait(false))
- {
- using (var deleteResultCommand = connection.CreateCommand())
- {
- deleteResultCommand.CommandText = "delete from FileOrganizerResults where ResultId = @ResultId";
-
- deleteResultCommand.Parameters.Add(deleteResultCommand, "@ResultId");
-
- IDbTransaction transaction = null;
-
- try
- {
- transaction = connection.BeginTransaction();
-
- deleteResultCommand.GetParameter(0).Value = new Guid(id);
-
- deleteResultCommand.Transaction = transaction;
-
- deleteResultCommand.ExecuteNonQuery();
-
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to delete FileOrganizationResult:", e);
-
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
- }
- }
- }
- }
- }
-
- public async Task DeleteAll()
- {
- using (var connection = await CreateConnection().ConfigureAwait(false))
- {
- using (var cmd = connection.CreateCommand())
- {
- cmd.CommandText = "delete from FileOrganizerResults";
-
- IDbTransaction transaction = null;
-
- try
- {
- transaction = connection.BeginTransaction();
-
- cmd.Transaction = transaction;
-
- cmd.ExecuteNonQuery();
-
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to delete results", e);
-
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
- }
- }
- }
- }
- }
-
- public QueryResult<FileOrganizationResult> GetResults(FileOrganizationResultQuery query)
- {
- if (query == null)
- {
- throw new ArgumentNullException("query");
- }
-
- using (var connection = CreateConnection(true).Result)
- {
- using (var cmd = connection.CreateCommand())
- {
- cmd.CommandText = "SELECT ResultId, OriginalPath, TargetPath, FileLength, OrganizationDate, Status, OrganizationType, StatusMessage, ExtractedName, ExtractedYear, ExtractedSeasonNumber, ExtractedEpisodeNumber, ExtractedEndingEpisodeNumber, DuplicatePaths from FileOrganizerResults";
-
- if (query.StartIndex.HasValue && query.StartIndex.Value > 0)
- {
- cmd.CommandText += string.Format(" WHERE ResultId NOT IN (SELECT ResultId FROM FileOrganizerResults ORDER BY OrganizationDate desc LIMIT {0})",
- query.StartIndex.Value.ToString(_usCulture));
- }
-
- cmd.CommandText += " ORDER BY OrganizationDate desc";
-
- if (query.Limit.HasValue)
- {
- cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(_usCulture);
- }
-
- cmd.CommandText += "; select count (ResultId) from FileOrganizerResults";
-
- var list = new List<FileOrganizationResult>();
- var count = 0;
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
- {
- while (reader.Read())
- {
- list.Add(GetResult(reader));
- }
-
- if (reader.NextResult() && reader.Read())
- {
- count = reader.GetInt32(0);
- }
- }
-
- return new QueryResult<FileOrganizationResult>()
- {
- Items = list.ToArray(),
- TotalRecordCount = count
- };
- }
- }
- }
-
- public FileOrganizationResult GetResult(string id)
- {
- if (string.IsNullOrEmpty(id))
- {
- throw new ArgumentNullException("id");
- }
-
- using (var connection = CreateConnection(true).Result)
- {
- var guid = new Guid(id);
-
- using (var cmd = connection.CreateCommand())
- {
- cmd.CommandText = "select ResultId, OriginalPath, TargetPath, FileLength, OrganizationDate, Status, OrganizationType, StatusMessage, ExtractedName, ExtractedYear, ExtractedSeasonNumber, ExtractedEpisodeNumber, ExtractedEndingEpisodeNumber, DuplicatePaths from FileOrganizerResults where ResultId=@Id";
-
- cmd.Parameters.Add(cmd, "@Id", DbType.Guid).Value = guid;
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
- {
- if (reader.Read())
- {
- return GetResult(reader);
- }
- }
- }
-
- return null;
- }
- }
-
- public FileOrganizationResult GetResult(IDataReader reader)
- {
- var index = 0;
-
- var result = new FileOrganizationResult
- {
- Id = reader.GetGuid(0).ToString("N")
- };
-
- index++;
- if (!reader.IsDBNull(index))
- {
- result.OriginalPath = reader.GetString(index);
- }
-
- index++;
- if (!reader.IsDBNull(index))
- {
- result.TargetPath = reader.GetString(index);
- }
-
- index++;
- result.FileSize = reader.GetInt64(index);
-
- index++;
- result.Date = reader.GetDateTime(index).ToUniversalTime();
-
- index++;
- result.Status = (FileSortingStatus)Enum.Parse(typeof(FileSortingStatus), reader.GetString(index), true);
-
- index++;
- result.Type = (FileOrganizerType)Enum.Parse(typeof(FileOrganizerType), reader.GetString(index), true);
-
- index++;
- if (!reader.IsDBNull(index))
- {
- result.StatusMessage = reader.GetString(index);
- }
-
- result.OriginalFileName = Path.GetFileName(result.OriginalPath);
-
- index++;
- if (!reader.IsDBNull(index))
- {
- result.ExtractedName = reader.GetString(index);
- }
-
- index++;
- if (!reader.IsDBNull(index))
- {
- result.ExtractedYear = reader.GetInt32(index);
- }
-
- index++;
- if (!reader.IsDBNull(index))
- {
- result.ExtractedSeasonNumber = reader.GetInt32(index);
- }
-
- index++;
- if (!reader.IsDBNull(index))
- {
- result.ExtractedEpisodeNumber = reader.GetInt32(index);
- }
-
- index++;
- if (!reader.IsDBNull(index))
- {
- result.ExtractedEndingEpisodeNumber = reader.GetInt32(index);
- }
-
- index++;
- if (!reader.IsDBNull(index))
- {
- result.DuplicatePaths = reader.GetString(index).Split('|').Where(i => !string.IsNullOrEmpty(i)).ToList();
- }
-
- return result;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs
deleted file mode 100644
index 62d9e76347..0000000000
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs
+++ /dev/null
@@ -1,453 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Generic;
-using System.Data;
-using System.Globalization;
-using System.IO;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Persistence
-{
- public class SqliteUserDataRepository : BaseSqliteRepository, IUserDataRepository
- {
- private IDbConnection _connection;
-
- public SqliteUserDataRepository(ILogManager logManager, IApplicationPaths appPaths, IDbConnector connector) : base(logManager, connector)
- {
- DbFilePath = Path.Combine(appPaths.DataPath, "userdata_v2.db");
- }
-
- protected override bool EnableConnectionPooling
- {
- get { return false; }
- }
-
- /// <summary>
- /// Gets the name of the repository
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get
- {
- return "SQLite";
- }
- }
-
- protected override async Task<IDbConnection> CreateConnection(bool isReadOnly = false)
- {
- var connection = await DbConnector.Connect(DbFilePath, false, false, 10000).ConfigureAwait(false);
-
- connection.RunQueries(new[]
- {
- "pragma temp_store = memory"
-
- }, Logger);
-
- return connection;
- }
-
- /// <summary>
- /// Opens the connection to the database
- /// </summary>
- /// <returns>Task.</returns>
- public async Task Initialize(IDbConnection connection, SemaphoreSlim writeLock)
- {
- WriteLock.Dispose();
- WriteLock = writeLock;
- _connection = connection;
-
- string[] queries = {
-
- "create table if not exists UserDataDb.userdata (key nvarchar, userId GUID, rating float null, played bit, playCount int, isFavorite bit, playbackPositionTicks bigint, lastPlayedDate datetime null)",
-
- "drop index if exists UserDataDb.idx_userdata",
- "drop index if exists UserDataDb.idx_userdata1",
- "drop index if exists UserDataDb.idx_userdata2",
- "drop index if exists UserDataDb.userdataindex1",
-
- "create unique index if not exists UserDataDb.userdataindex on userdata (key, userId)",
- "create index if not exists UserDataDb.userdataindex2 on userdata (key, userId, played)",
- "create index if not exists UserDataDb.userdataindex3 on userdata (key, userId, playbackPositionTicks)",
- "create index if not exists UserDataDb.userdataindex4 on userdata (key, userId, isFavorite)",
-
- //pragmas
- "pragma temp_store = memory",
-
- "pragma shrink_memory"
- };
-
- _connection.RunQueries(queries, Logger);
-
- _connection.AddColumn(Logger, "userdata", "AudioStreamIndex", "int");
- _connection.AddColumn(Logger, "userdata", "SubtitleStreamIndex", "int");
- }
-
- /// <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 Task SaveUserData(Guid userId, string key, UserItemData userData, CancellationToken cancellationToken)
- {
- if (userData == null)
- {
- throw new ArgumentNullException("userData");
- }
- if (userId == Guid.Empty)
- {
- throw new ArgumentNullException("userId");
- }
- if (string.IsNullOrEmpty(key))
- {
- throw new ArgumentNullException("key");
- }
-
- return PersistUserData(userId, key, userData, cancellationToken);
- }
-
- public Task SaveAllUserData(Guid userId, IEnumerable<UserItemData> userData, CancellationToken cancellationToken)
- {
- if (userData == null)
- {
- throw new ArgumentNullException("userData");
- }
- if (userId == Guid.Empty)
- {
- throw new ArgumentNullException("userId");
- }
-
- return PersistAllUserData(userId, userData, cancellationToken);
- }
-
- /// <summary>
- /// Persists 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>
- public async Task PersistUserData(Guid userId, string key, UserItemData userData, CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- await WriteLock.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- IDbTransaction transaction = null;
-
- try
- {
- transaction = _connection.BeginTransaction();
-
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "replace into userdata (key, userId, rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex) values (@key, @userId, @rating,@played,@playCount,@isFavorite,@playbackPositionTicks,@lastPlayedDate,@AudioStreamIndex,@SubtitleStreamIndex)";
-
- cmd.Parameters.Add(cmd, "@key", DbType.String).Value = key;
- cmd.Parameters.Add(cmd, "@userId", DbType.Guid).Value = userId;
- cmd.Parameters.Add(cmd, "@rating", DbType.Double).Value = userData.Rating;
- cmd.Parameters.Add(cmd, "@played", DbType.Boolean).Value = userData.Played;
- cmd.Parameters.Add(cmd, "@playCount", DbType.Int32).Value = userData.PlayCount;
- cmd.Parameters.Add(cmd, "@isFavorite", DbType.Boolean).Value = userData.IsFavorite;
- cmd.Parameters.Add(cmd, "@playbackPositionTicks", DbType.Int64).Value = userData.PlaybackPositionTicks;
- cmd.Parameters.Add(cmd, "@lastPlayedDate", DbType.DateTime).Value = userData.LastPlayedDate;
- cmd.Parameters.Add(cmd, "@AudioStreamIndex", DbType.Int32).Value = userData.AudioStreamIndex;
- cmd.Parameters.Add(cmd, "@SubtitleStreamIndex", DbType.Int32).Value = userData.SubtitleStreamIndex;
-
- cmd.Transaction = transaction;
-
- cmd.ExecuteNonQuery();
- }
-
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to save user data:", e);
-
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
- }
-
- WriteLock.Release();
- }
- }
-
- /// <summary>
- /// Persist all user data for the specified user
- /// </summary>
- /// <param name="userId"></param>
- /// <param name="userData"></param>
- /// <param name="cancellationToken"></param>
- /// <returns></returns>
- private async Task PersistAllUserData(Guid userId, IEnumerable<UserItemData> userData, CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- await WriteLock.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- IDbTransaction transaction = null;
-
- try
- {
- transaction = _connection.BeginTransaction();
-
- foreach (var userItemData in userData)
- {
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "replace into userdata (key, userId, rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex) values (@key, @userId, @rating,@played,@playCount,@isFavorite,@playbackPositionTicks,@lastPlayedDate,@AudioStreamIndex,@SubtitleStreamIndex)";
-
- cmd.Parameters.Add(cmd, "@key", DbType.String).Value = userItemData.Key;
- cmd.Parameters.Add(cmd, "@userId", DbType.Guid).Value = userId;
- cmd.Parameters.Add(cmd, "@rating", DbType.Double).Value = userItemData.Rating;
- cmd.Parameters.Add(cmd, "@played", DbType.Boolean).Value = userItemData.Played;
- cmd.Parameters.Add(cmd, "@playCount", DbType.Int32).Value = userItemData.PlayCount;
- cmd.Parameters.Add(cmd, "@isFavorite", DbType.Boolean).Value = userItemData.IsFavorite;
- cmd.Parameters.Add(cmd, "@playbackPositionTicks", DbType.Int64).Value = userItemData.PlaybackPositionTicks;
- cmd.Parameters.Add(cmd, "@lastPlayedDate", DbType.DateTime).Value = userItemData.LastPlayedDate;
- cmd.Parameters.Add(cmd, "@AudioStreamIndex", DbType.Int32).Value = userItemData.AudioStreamIndex;
- cmd.Parameters.Add(cmd, "@SubtitleStreamIndex", DbType.Int32).Value = userItemData.SubtitleStreamIndex;
-
- cmd.Transaction = transaction;
-
- cmd.ExecuteNonQuery();
- }
-
- cancellationToken.ThrowIfCancellationRequested();
- }
-
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to save user data:", e);
-
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
- }
-
- WriteLock.Release();
- }
- }
-
- /// <summary>
- /// Gets the user data.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <param name="key">The key.</param>
- /// <returns>Task{UserItemData}.</returns>
- /// <exception cref="System.ArgumentNullException">
- /// userId
- /// or
- /// key
- /// </exception>
- public UserItemData GetUserData(Guid userId, string key)
- {
- if (userId == Guid.Empty)
- {
- throw new ArgumentNullException("userId");
- }
- if (string.IsNullOrEmpty(key))
- {
- throw new ArgumentNullException("key");
- }
-
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from userdata where key = @key and userId=@userId";
-
- cmd.Parameters.Add(cmd, "@key", DbType.String).Value = key;
- cmd.Parameters.Add(cmd, "@userId", DbType.Guid).Value = userId;
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
- {
- if (reader.Read())
- {
- return ReadRow(reader);
- }
- }
-
- return null;
- }
- }
-
- public UserItemData GetUserData(Guid userId, List<string> keys)
- {
- if (userId == Guid.Empty)
- {
- throw new ArgumentNullException("userId");
- }
- if (keys == null)
- {
- throw new ArgumentNullException("keys");
- }
-
- using (var cmd = _connection.CreateCommand())
- {
- var index = 0;
- var userdataKeys = new List<string>();
- var builder = new StringBuilder();
- foreach (var key in keys)
- {
- var paramName = "@Key" + index;
- userdataKeys.Add("Key =" + paramName);
- cmd.Parameters.Add(cmd, paramName, DbType.String).Value = key;
- builder.Append(" WHEN Key=" + paramName + " THEN " + index);
- index++;
- break;
- }
-
- var keyText = string.Join(" OR ", userdataKeys.ToArray());
-
- cmd.CommandText = "select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from userdata where userId=@userId AND (" + keyText + ") ";
-
- cmd.CommandText += " ORDER BY (Case " + builder + " Else " + keys.Count.ToString(CultureInfo.InvariantCulture) + " End )";
- cmd.CommandText += " LIMIT 1";
-
- cmd.Parameters.Add(cmd, "@userId", DbType.Guid).Value = userId;
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
- {
- if (reader.Read())
- {
- return ReadRow(reader);
- }
- }
-
- return null;
- }
- }
-
- /// <summary>
- /// Return all user-data associated with the given user
- /// </summary>
- /// <param name="userId"></param>
- /// <returns></returns>
- public IEnumerable<UserItemData> GetAllUserData(Guid userId)
- {
- if (userId == Guid.Empty)
- {
- throw new ArgumentNullException("userId");
- }
-
- using (var cmd = _connection.CreateCommand())
- {
- cmd.CommandText = "select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from userdata where userId=@userId";
-
- cmd.Parameters.Add(cmd, "@userId", DbType.Guid).Value = userId;
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
- {
- while (reader.Read())
- {
- yield return ReadRow(reader);
- }
- }
- }
- }
-
- /// <summary>
- /// Read a row from the specified reader into the provided userData object
- /// </summary>
- /// <param name="reader"></param>
- private UserItemData ReadRow(IDataReader reader)
- {
- var userData = new UserItemData();
-
- userData.Key = reader.GetString(0);
- userData.UserId = reader.GetGuid(1);
-
- if (!reader.IsDBNull(2))
- {
- userData.Rating = reader.GetDouble(2);
- }
-
- userData.Played = reader.GetBoolean(3);
- userData.PlayCount = reader.GetInt32(4);
- userData.IsFavorite = reader.GetBoolean(5);
- userData.PlaybackPositionTicks = reader.GetInt64(6);
-
- if (!reader.IsDBNull(7))
- {
- userData.LastPlayedDate = reader.GetDateTime(7).ToUniversalTime();
- }
-
- if (!reader.IsDBNull(8))
- {
- userData.AudioStreamIndex = reader.GetInt32(8);
- }
-
- if (!reader.IsDBNull(9))
- {
- userData.SubtitleStreamIndex = reader.GetInt32(9);
- }
-
- return userData;
- }
-
- protected override void Dispose(bool dispose)
- {
- // handled by library database
- }
-
- protected override void CloseConnection()
- {
- // handled by library database
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs
deleted file mode 100644
index 31fa78806c..0000000000
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs
+++ /dev/null
@@ -1,237 +0,0 @@
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Collections.Generic;
-using System.Data;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-
-namespace MediaBrowser.Server.Implementations.Persistence
-{
- /// <summary>
- /// Class SQLiteUserRepository
- /// </summary>
- public class SqliteUserRepository : BaseSqliteRepository, IUserRepository
- {
- private readonly IJsonSerializer _jsonSerializer;
- private readonly IMemoryStreamProvider _memoryStreamProvider;
-
- public SqliteUserRepository(ILogManager logManager, IServerApplicationPaths appPaths, IJsonSerializer jsonSerializer, IDbConnector dbConnector, IMemoryStreamProvider memoryStreamProvider) : base(logManager, dbConnector)
- {
- _jsonSerializer = jsonSerializer;
- _memoryStreamProvider = memoryStreamProvider;
-
- DbFilePath = Path.Combine(appPaths.DataPath, "users.db");
- }
-
- /// <summary>
- /// Gets the name of the repository
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get
- {
- return "SQLite";
- }
- }
-
- /// <summary>
- /// Opens the connection to the database
- /// </summary>
- /// <returns>Task.</returns>
- public async Task Initialize()
- {
- using (var connection = await CreateConnection().ConfigureAwait(false))
- {
- string[] queries = {
-
- "create table if not exists users (guid GUID primary key, data BLOB)",
- "create index if not exists idx_users on users(guid)",
- "create table if not exists schema_version (table_name primary key, version)",
-
- "pragma shrink_memory"
- };
-
- connection.RunQueries(queries, Logger);
- }
- }
-
- /// <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 async Task SaveUser(User user, CancellationToken cancellationToken)
- {
- if (user == null)
- {
- throw new ArgumentNullException("user");
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- var serialized = _jsonSerializer.SerializeToBytes(user, _memoryStreamProvider);
-
- cancellationToken.ThrowIfCancellationRequested();
-
- using (var connection = await CreateConnection().ConfigureAwait(false))
- {
- IDbTransaction transaction = null;
-
- try
- {
- transaction = connection.BeginTransaction();
-
- using (var cmd = connection.CreateCommand())
- {
- cmd.CommandText = "replace into users (guid, data) values (@1, @2)";
- cmd.Parameters.Add(cmd, "@1", DbType.Guid).Value = user.Id;
- cmd.Parameters.Add(cmd, "@2", DbType.Binary).Value = serialized;
-
- cmd.Transaction = transaction;
-
- cmd.ExecuteNonQuery();
- }
-
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to save user:", e);
-
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
- }
- }
- }
- }
-
- /// <summary>
- /// Retrieve all users from the database
- /// </summary>
- /// <returns>IEnumerable{User}.</returns>
- public IEnumerable<User> RetrieveAllUsers()
- {
- var list = new List<User>();
-
- using (var connection = CreateConnection(true).Result)
- {
- using (var cmd = connection.CreateCommand())
- {
- cmd.CommandText = "select guid,data from users";
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
- {
- while (reader.Read())
- {
- var id = reader.GetGuid(0);
-
- using (var stream = reader.GetMemoryStream(1, _memoryStreamProvider))
- {
- var user = _jsonSerializer.DeserializeFromStream<User>(stream);
- user.Id = id;
- list.Add(user);
- }
- }
- }
- }
- }
-
- return list;
- }
-
- /// <summary>
- /// Deletes the user.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">user</exception>
- public async Task DeleteUser(User user, CancellationToken cancellationToken)
- {
- if (user == null)
- {
- throw new ArgumentNullException("user");
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- using (var connection = await CreateConnection().ConfigureAwait(false))
- {
- IDbTransaction transaction = null;
-
- try
- {
- transaction = connection.BeginTransaction();
-
- using (var cmd = connection.CreateCommand())
- {
- cmd.CommandText = "delete from users where guid=@guid";
-
- cmd.Parameters.Add(cmd, "@guid", DbType.Guid).Value = user.Id;
-
- cmd.Transaction = transaction;
-
- cmd.ExecuteNonQuery();
- }
-
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to delete user:", e);
-
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
- }
- }
- }
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs b/MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
index 63dfe20b2f..07773d846b 100644
--- a/MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
+++ b/MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
@@ -1,12 +1,13 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Playlists;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
-using CommonIO;
-using MediaBrowser.Model.Querying;
using System.Threading.Tasks;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Playlists;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Server.Implementations.Playlists
{
@@ -27,6 +28,7 @@ namespace MediaBrowser.Server.Implementations.Playlists
return base.GetEligibleChildrenForRecursiveChildren(user).OfType<Playlist>();
}
+ [IgnoreDataMember]
public override bool IsHidden
{
get
@@ -35,9 +37,10 @@ namespace MediaBrowser.Server.Implementations.Playlists
}
}
+ [IgnoreDataMember]
public override string CollectionType
{
- get { return Model.Entities.CollectionType.Playlists; }
+ get { return MediaBrowser.Model.Entities.CollectionType.Playlists; }
}
protected override Task<QueryResult<BaseItem>> GetItemsInternal(InternalItemsQuery query)
@@ -46,29 +49,5 @@ namespace MediaBrowser.Server.Implementations.Playlists
return base.GetItemsInternal(query);
}
}
-
- 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/MediaBrowser.Server.Implementations/Security/AuthenticationRepository.cs b/MediaBrowser.Server.Implementations/Security/AuthenticationRepository.cs
deleted file mode 100644
index 74a552dccc..0000000000
--- a/MediaBrowser.Server.Implementations/Security/AuthenticationRepository.cs
+++ /dev/null
@@ -1,317 +0,0 @@
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Security;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Server.Implementations.Persistence;
-using System;
-using System.Collections.Generic;
-using System.Data;
-using System.Globalization;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Security
-{
- public class AuthenticationRepository : BaseSqliteRepository, IAuthenticationRepository
- {
- private readonly IServerApplicationPaths _appPaths;
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
- public AuthenticationRepository(ILogManager logManager, IServerApplicationPaths appPaths, IDbConnector connector)
- : base(logManager, connector)
- {
- _appPaths = appPaths;
- DbFilePath = Path.Combine(appPaths.DataPath, "authentication.db");
- }
-
- public async Task Initialize()
- {
- using (var connection = await CreateConnection().ConfigureAwait(false))
- {
- string[] queries = {
-
- "create table if not exists AccessTokens (Id GUID PRIMARY KEY, AccessToken TEXT NOT NULL, DeviceId TEXT, AppName TEXT, AppVersion TEXT, DeviceName TEXT, UserId TEXT, IsActive BIT, DateCreated DATETIME NOT NULL, DateRevoked DATETIME)",
- "create index if not exists idx_AccessTokens on AccessTokens(Id)"
- };
-
- connection.RunQueries(queries, Logger);
-
- connection.AddColumn(Logger, "AccessTokens", "AppVersion", "TEXT");
- }
- }
-
- public Task Create(AuthenticationInfo info, CancellationToken cancellationToken)
- {
- info.Id = Guid.NewGuid().ToString("N");
-
- return Update(info, cancellationToken);
- }
-
- public async Task Update(AuthenticationInfo info, CancellationToken cancellationToken)
- {
- if (info == null)
- {
- throw new ArgumentNullException("info");
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- using (var connection = await CreateConnection().ConfigureAwait(false))
- {
- using (var saveInfoCommand = connection.CreateCommand())
- {
- saveInfoCommand.CommandText = "replace into AccessTokens (Id, AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, IsActive, DateCreated, DateRevoked) values (@Id, @AccessToken, @DeviceId, @AppName, @AppVersion, @DeviceName, @UserId, @IsActive, @DateCreated, @DateRevoked)";
-
- saveInfoCommand.Parameters.Add(saveInfoCommand, "@Id");
- saveInfoCommand.Parameters.Add(saveInfoCommand, "@AccessToken");
- saveInfoCommand.Parameters.Add(saveInfoCommand, "@DeviceId");
- saveInfoCommand.Parameters.Add(saveInfoCommand, "@AppName");
- saveInfoCommand.Parameters.Add(saveInfoCommand, "@AppVersion");
- saveInfoCommand.Parameters.Add(saveInfoCommand, "@DeviceName");
- saveInfoCommand.Parameters.Add(saveInfoCommand, "@UserId");
- saveInfoCommand.Parameters.Add(saveInfoCommand, "@IsActive");
- saveInfoCommand.Parameters.Add(saveInfoCommand, "@DateCreated");
- saveInfoCommand.Parameters.Add(saveInfoCommand, "@DateRevoked");
-
- IDbTransaction transaction = null;
-
- try
- {
- transaction = connection.BeginTransaction();
-
- var index = 0;
-
- saveInfoCommand.GetParameter(index++).Value = new Guid(info.Id);
- saveInfoCommand.GetParameter(index++).Value = info.AccessToken;
- saveInfoCommand.GetParameter(index++).Value = info.DeviceId;
- saveInfoCommand.GetParameter(index++).Value = info.AppName;
- saveInfoCommand.GetParameter(index++).Value = info.AppVersion;
- saveInfoCommand.GetParameter(index++).Value = info.DeviceName;
- saveInfoCommand.GetParameter(index++).Value = info.UserId;
- saveInfoCommand.GetParameter(index++).Value = info.IsActive;
- saveInfoCommand.GetParameter(index++).Value = info.DateCreated;
- saveInfoCommand.GetParameter(index++).Value = info.DateRevoked;
-
- saveInfoCommand.Transaction = transaction;
-
- saveInfoCommand.ExecuteNonQuery();
-
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to save record:", e);
-
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
- }
- }
- }
- }
- }
-
- private const string BaseSelectText = "select Id, AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, IsActive, DateCreated, DateRevoked from AccessTokens";
-
- public QueryResult<AuthenticationInfo> Get(AuthenticationInfoQuery query)
- {
- if (query == null)
- {
- throw new ArgumentNullException("query");
- }
-
- using (var connection = CreateConnection(true).Result)
- {
- using (var cmd = connection.CreateCommand())
- {
- cmd.CommandText = BaseSelectText;
-
- var whereClauses = new List<string>();
-
- var startIndex = query.StartIndex ?? 0;
-
- if (!string.IsNullOrWhiteSpace(query.AccessToken))
- {
- whereClauses.Add("AccessToken=@AccessToken");
- cmd.Parameters.Add(cmd, "@AccessToken", DbType.String).Value = query.AccessToken;
- }
-
- if (!string.IsNullOrWhiteSpace(query.UserId))
- {
- whereClauses.Add("UserId=@UserId");
- cmd.Parameters.Add(cmd, "@UserId", DbType.String).Value = query.UserId;
- }
-
- if (!string.IsNullOrWhiteSpace(query.DeviceId))
- {
- whereClauses.Add("DeviceId=@DeviceId");
- cmd.Parameters.Add(cmd, "@DeviceId", DbType.String).Value = query.DeviceId;
- }
-
- if (query.IsActive.HasValue)
- {
- whereClauses.Add("IsActive=@IsActive");
- cmd.Parameters.Add(cmd, "@IsActive", DbType.Boolean).Value = query.IsActive.Value;
- }
-
- if (query.HasUser.HasValue)
- {
- if (query.HasUser.Value)
- {
- whereClauses.Add("UserId not null");
- }
- else
- {
- whereClauses.Add("UserId is null");
- }
- }
-
- var whereTextWithoutPaging = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
-
- if (startIndex > 0)
- {
- var pagingWhereText = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
-
- whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM AccessTokens {0} ORDER BY DateCreated LIMIT {1})",
- pagingWhereText,
- startIndex.ToString(_usCulture)));
- }
-
- var whereText = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
-
- cmd.CommandText += whereText;
-
- cmd.CommandText += " ORDER BY DateCreated";
-
- if (query.Limit.HasValue)
- {
- cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(_usCulture);
- }
-
- cmd.CommandText += "; select count (Id) from AccessTokens" + whereTextWithoutPaging;
-
- var list = new List<AuthenticationInfo>();
- var count = 0;
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
- {
- while (reader.Read())
- {
- list.Add(Get(reader));
- }
-
- if (reader.NextResult() && reader.Read())
- {
- count = reader.GetInt32(0);
- }
- }
-
- return new QueryResult<AuthenticationInfo>()
- {
- Items = list.ToArray(),
- TotalRecordCount = count
- };
- }
- }
- }
-
- public AuthenticationInfo Get(string id)
- {
- if (string.IsNullOrEmpty(id))
- {
- throw new ArgumentNullException("id");
- }
-
- using (var connection = CreateConnection(true).Result)
- {
- var guid = new Guid(id);
-
- using (var cmd = connection.CreateCommand())
- {
- cmd.CommandText = BaseSelectText + " where Id=@Id";
-
- cmd.Parameters.Add(cmd, "@Id", DbType.Guid).Value = guid;
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
- {
- if (reader.Read())
- {
- return Get(reader);
- }
- }
- }
-
- return null;
- }
- }
-
- private AuthenticationInfo Get(IDataReader reader)
- {
- var info = new AuthenticationInfo
- {
- Id = reader.GetGuid(0).ToString("N"),
- AccessToken = reader.GetString(1)
- };
-
- if (!reader.IsDBNull(2))
- {
- info.DeviceId = reader.GetString(2);
- }
-
- if (!reader.IsDBNull(3))
- {
- info.AppName = reader.GetString(3);
- }
-
- if (!reader.IsDBNull(4))
- {
- info.AppVersion = reader.GetString(4);
- }
-
- if (!reader.IsDBNull(5))
- {
- info.DeviceName = reader.GetString(5);
- }
-
- if (!reader.IsDBNull(6))
- {
- info.UserId = reader.GetString(6);
- }
-
- info.IsActive = reader.GetBoolean(7);
- info.DateCreated = reader.GetDateTime(8).ToUniversalTime();
-
- if (!reader.IsDBNull(9))
- {
- info.DateRevoked = reader.GetDateTime(9).ToUniversalTime();
- }
-
- return info;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Social/SharingRepository.cs b/MediaBrowser.Server.Implementations/Social/SharingRepository.cs
deleted file mode 100644
index c4243c1a76..0000000000
--- a/MediaBrowser.Server.Implementations/Social/SharingRepository.cs
+++ /dev/null
@@ -1,158 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Social;
-using MediaBrowser.Server.Implementations.Persistence;
-using System;
-using System.Data;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Social
-{
- public class SharingRepository : BaseSqliteRepository
- {
- public SharingRepository(ILogManager logManager, IApplicationPaths appPaths, IDbConnector dbConnector)
- : base(logManager, dbConnector)
- {
- DbFilePath = Path.Combine(appPaths.DataPath, "shares.db");
- }
-
- /// <summary>
- /// Opens the connection to the database
- /// </summary>
- /// <returns>Task.</returns>
- public async Task Initialize()
- {
- using (var connection = await CreateConnection().ConfigureAwait(false))
- {
- string[] queries = {
-
- "create table if not exists Shares (Id GUID, ItemId TEXT, UserId TEXT, ExpirationDate DateTime, PRIMARY KEY (Id))",
- "create index if not exists idx_Shares on Shares(Id)",
-
- "pragma shrink_memory"
- };
-
- connection.RunQueries(queries, Logger);
- }
- }
-
- public async Task CreateShare(SocialShareInfo info)
- {
- if (info == null)
- {
- throw new ArgumentNullException("info");
- }
- if (string.IsNullOrWhiteSpace(info.Id))
- {
- throw new ArgumentNullException("info.Id");
- }
-
- var cancellationToken = CancellationToken.None;
-
- cancellationToken.ThrowIfCancellationRequested();
-
- using (var connection = await CreateConnection().ConfigureAwait(false))
- {
- using (var saveShareCommand = connection.CreateCommand())
- {
- saveShareCommand.CommandText = "replace into Shares (Id, ItemId, UserId, ExpirationDate) values (@Id, @ItemId, @UserId, @ExpirationDate)";
-
- saveShareCommand.Parameters.Add(saveShareCommand, "@Id");
- saveShareCommand.Parameters.Add(saveShareCommand, "@ItemId");
- saveShareCommand.Parameters.Add(saveShareCommand, "@UserId");
- saveShareCommand.Parameters.Add(saveShareCommand, "@ExpirationDate");
-
- IDbTransaction transaction = null;
-
- try
- {
- transaction = connection.BeginTransaction();
-
- saveShareCommand.GetParameter(0).Value = new Guid(info.Id);
- saveShareCommand.GetParameter(1).Value = info.ItemId;
- saveShareCommand.GetParameter(2).Value = info.UserId;
- saveShareCommand.GetParameter(3).Value = info.ExpirationDate;
-
- saveShareCommand.Transaction = transaction;
-
- saveShareCommand.ExecuteNonQuery();
-
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to save share:", e);
-
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
- }
- }
- }
- }
- }
-
- public SocialShareInfo GetShareInfo(string id)
- {
- if (string.IsNullOrWhiteSpace(id))
- {
- throw new ArgumentNullException("id");
- }
-
- using (var connection = CreateConnection(true).Result)
- {
- var cmd = connection.CreateCommand();
- cmd.CommandText = "select Id, ItemId, UserId, ExpirationDate from Shares where id = @id";
-
- cmd.Parameters.Add(cmd, "@id", DbType.Guid).Value = new Guid(id);
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
- {
- if (reader.Read())
- {
- return GetSocialShareInfo(reader);
- }
- }
-
- return null;
- }
- }
-
- private SocialShareInfo GetSocialShareInfo(IDataReader reader)
- {
- var info = new SocialShareInfo();
-
- info.Id = reader.GetGuid(0).ToString("N");
- info.ItemId = reader.GetString(1);
- info.UserId = reader.GetString(2);
- info.ExpirationDate = reader.GetDateTime(3).ToUniversalTime();
-
- return info;
- }
-
- public async Task DeleteShare(string id)
- {
-
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/VideoBitRateComparer.cs b/MediaBrowser.Server.Implementations/Sorting/VideoBitRateComparer.cs
deleted file mode 100644
index cbf6ebac66..0000000000
--- a/MediaBrowser.Server.Implementations/Sorting/VideoBitRateComparer.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- class VideoBitRateComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return GetValue(x).CompareTo(GetValue(y));
- }
-
- private int GetValue(BaseItem item)
- {
- var video = item as Video;
-
- if (video != null)
- {
- return video.VideoBitRate ?? 0;
- }
-
- return 0;
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.VideoBitRate; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncRepository.cs b/MediaBrowser.Server.Implementations/Sync/SyncRepository.cs
deleted file mode 100644
index 64ed00ded1..0000000000
--- a/MediaBrowser.Server.Implementations/Sync/SyncRepository.cs
+++ /dev/null
@@ -1,976 +0,0 @@
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Sync;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Sync;
-using MediaBrowser.Server.Implementations.Persistence;
-using System;
-using System.Collections.Generic;
-using System.Data;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Sync
-{
- public class SyncRepository : BaseSqliteRepository, ISyncRepository
- {
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
- private readonly IJsonSerializer _json;
-
- public SyncRepository(ILogManager logManager, IJsonSerializer json, IServerApplicationPaths appPaths, IDbConnector connector)
- : base(logManager, connector)
- {
- _json = json;
- DbFilePath = Path.Combine(appPaths.DataPath, "sync14.db");
- }
-
- private class SyncSummary
- {
- public Dictionary<string, int> Items { get; set; }
-
- public SyncSummary()
- {
- Items = new Dictionary<string, int>();
- }
- }
-
-
- public async Task Initialize()
- {
- using (var connection = await CreateConnection().ConfigureAwait(false))
- {
- string[] queries = {
-
- "create table if not exists SyncJobs (Id GUID PRIMARY KEY, TargetId TEXT NOT NULL, Name TEXT NOT NULL, Profile TEXT, Quality TEXT, Bitrate INT, Status TEXT NOT NULL, Progress FLOAT, UserId TEXT NOT NULL, ItemIds TEXT NOT NULL, Category TEXT, ParentId TEXT, UnwatchedOnly BIT, ItemLimit INT, SyncNewContent BIT, DateCreated DateTime, DateLastModified DateTime, ItemCount int)",
-
- "create table if not exists SyncJobItems (Id GUID PRIMARY KEY, ItemId TEXT, ItemName TEXT, MediaSourceId TEXT, JobId TEXT, TemporaryPath TEXT, OutputPath TEXT, Status TEXT, TargetId TEXT, DateCreated DateTime, Progress FLOAT, AdditionalFiles TEXT, MediaSource TEXT, IsMarkedForRemoval BIT, JobItemIndex INT, ItemDateModifiedTicks BIGINT)",
-
- "drop index if exists idx_SyncJobItems2",
- "drop index if exists idx_SyncJobItems3",
- "drop index if exists idx_SyncJobs1",
- "drop index if exists idx_SyncJobs",
- "drop index if exists idx_SyncJobItems1",
- "create index if not exists idx_SyncJobItems4 on SyncJobItems(TargetId,ItemId,Status,Progress,DateCreated)",
- "create index if not exists idx_SyncJobItems5 on SyncJobItems(TargetId,Status,ItemId,Progress)",
-
- "create index if not exists idx_SyncJobs2 on SyncJobs(TargetId,Status,ItemIds,Progress)",
-
- "pragma shrink_memory"
- };
-
- connection.RunQueries(queries, Logger);
-
- connection.AddColumn(Logger, "SyncJobs", "Profile", "TEXT");
- connection.AddColumn(Logger, "SyncJobs", "Bitrate", "INT");
- connection.AddColumn(Logger, "SyncJobItems", "ItemDateModifiedTicks", "BIGINT");
- }
- }
-
- private const string BaseJobSelectText = "select Id, TargetId, Name, Profile, Quality, Bitrate, Status, Progress, UserId, ItemIds, Category, ParentId, UnwatchedOnly, ItemLimit, SyncNewContent, DateCreated, DateLastModified, ItemCount from SyncJobs";
- private const string BaseJobItemSelectText = "select Id, ItemId, ItemName, MediaSourceId, JobId, TemporaryPath, OutputPath, Status, TargetId, DateCreated, Progress, AdditionalFiles, MediaSource, IsMarkedForRemoval, JobItemIndex, ItemDateModifiedTicks from SyncJobItems";
-
- public SyncJob GetJob(string id)
- {
- if (string.IsNullOrEmpty(id))
- {
- throw new ArgumentNullException("id");
- }
-
- CheckDisposed();
-
- var guid = new Guid(id);
-
- if (guid == Guid.Empty)
- {
- throw new ArgumentNullException("id");
- }
-
- using (var connection = CreateConnection(true).Result)
- {
- using (var cmd = connection.CreateCommand())
- {
- cmd.CommandText = BaseJobSelectText + " where Id=@Id";
-
- cmd.Parameters.Add(cmd, "@Id", DbType.Guid).Value = guid;
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
- {
- if (reader.Read())
- {
- return GetJob(reader);
- }
- }
- }
-
- return null;
- }
- }
-
- private SyncJob GetJob(IDataReader reader)
- {
- var info = new SyncJob
- {
- Id = reader.GetGuid(0).ToString("N"),
- TargetId = reader.GetString(1),
- Name = reader.GetString(2)
- };
-
- if (!reader.IsDBNull(3))
- {
- info.Profile = reader.GetString(3);
- }
-
- if (!reader.IsDBNull(4))
- {
- info.Quality = reader.GetString(4);
- }
-
- if (!reader.IsDBNull(5))
- {
- info.Bitrate = reader.GetInt32(5);
- }
-
- if (!reader.IsDBNull(6))
- {
- info.Status = (SyncJobStatus)Enum.Parse(typeof(SyncJobStatus), reader.GetString(6), true);
- }
-
- if (!reader.IsDBNull(7))
- {
- info.Progress = reader.GetDouble(7);
- }
-
- if (!reader.IsDBNull(8))
- {
- info.UserId = reader.GetString(8);
- }
-
- if (!reader.IsDBNull(9))
- {
- info.RequestedItemIds = reader.GetString(9).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
- }
-
- if (!reader.IsDBNull(10))
- {
- info.Category = (SyncCategory)Enum.Parse(typeof(SyncCategory), reader.GetString(10), true);
- }
-
- if (!reader.IsDBNull(11))
- {
- info.ParentId = reader.GetString(11);
- }
-
- if (!reader.IsDBNull(12))
- {
- info.UnwatchedOnly = reader.GetBoolean(12);
- }
-
- if (!reader.IsDBNull(13))
- {
- info.ItemLimit = reader.GetInt32(13);
- }
-
- info.SyncNewContent = reader.GetBoolean(14);
-
- info.DateCreated = reader.GetDateTime(15).ToUniversalTime();
- info.DateLastModified = reader.GetDateTime(16).ToUniversalTime();
- info.ItemCount = reader.GetInt32(17);
-
- return info;
- }
-
- public Task Create(SyncJob job)
- {
- return InsertOrUpdate(job, true);
- }
-
- public Task Update(SyncJob job)
- {
- return InsertOrUpdate(job, false);
- }
-
- private async Task InsertOrUpdate(SyncJob job, bool insert)
- {
- if (job == null)
- {
- throw new ArgumentNullException("job");
- }
-
- CheckDisposed();
-
- using (var connection = await CreateConnection().ConfigureAwait(false))
- {
- using (var cmd = connection.CreateCommand())
- {
- if (insert)
- {
- cmd.CommandText = "insert into SyncJobs (Id, TargetId, Name, Profile, Quality, Bitrate, Status, Progress, UserId, ItemIds, Category, ParentId, UnwatchedOnly, ItemLimit, SyncNewContent, DateCreated, DateLastModified, ItemCount) values (@Id, @TargetId, @Name, @Profile, @Quality, @Bitrate, @Status, @Progress, @UserId, @ItemIds, @Category, @ParentId, @UnwatchedOnly, @ItemLimit, @SyncNewContent, @DateCreated, @DateLastModified, @ItemCount)";
-
- cmd.Parameters.Add(cmd, "@Id");
- cmd.Parameters.Add(cmd, "@TargetId");
- cmd.Parameters.Add(cmd, "@Name");
- cmd.Parameters.Add(cmd, "@Profile");
- cmd.Parameters.Add(cmd, "@Quality");
- cmd.Parameters.Add(cmd, "@Bitrate");
- cmd.Parameters.Add(cmd, "@Status");
- cmd.Parameters.Add(cmd, "@Progress");
- cmd.Parameters.Add(cmd, "@UserId");
- cmd.Parameters.Add(cmd, "@ItemIds");
- cmd.Parameters.Add(cmd, "@Category");
- cmd.Parameters.Add(cmd, "@ParentId");
- cmd.Parameters.Add(cmd, "@UnwatchedOnly");
- cmd.Parameters.Add(cmd, "@ItemLimit");
- cmd.Parameters.Add(cmd, "@SyncNewContent");
- cmd.Parameters.Add(cmd, "@DateCreated");
- cmd.Parameters.Add(cmd, "@DateLastModified");
- cmd.Parameters.Add(cmd, "@ItemCount");
- }
- else
- {
- cmd.CommandText = "update SyncJobs set TargetId=@TargetId,Name=@Name,Profile=@Profile,Quality=@Quality,Bitrate=@Bitrate,Status=@Status,Progress=@Progress,UserId=@UserId,ItemIds=@ItemIds,Category=@Category,ParentId=@ParentId,UnwatchedOnly=@UnwatchedOnly,ItemLimit=@ItemLimit,SyncNewContent=@SyncNewContent,DateCreated=@DateCreated,DateLastModified=@DateLastModified,ItemCount=@ItemCount where Id=@Id";
-
- cmd.Parameters.Add(cmd, "@Id");
- cmd.Parameters.Add(cmd, "@TargetId");
- cmd.Parameters.Add(cmd, "@Name");
- cmd.Parameters.Add(cmd, "@Profile");
- cmd.Parameters.Add(cmd, "@Quality");
- cmd.Parameters.Add(cmd, "@Bitrate");
- cmd.Parameters.Add(cmd, "@Status");
- cmd.Parameters.Add(cmd, "@Progress");
- cmd.Parameters.Add(cmd, "@UserId");
- cmd.Parameters.Add(cmd, "@ItemIds");
- cmd.Parameters.Add(cmd, "@Category");
- cmd.Parameters.Add(cmd, "@ParentId");
- cmd.Parameters.Add(cmd, "@UnwatchedOnly");
- cmd.Parameters.Add(cmd, "@ItemLimit");
- cmd.Parameters.Add(cmd, "@SyncNewContent");
- cmd.Parameters.Add(cmd, "@DateCreated");
- cmd.Parameters.Add(cmd, "@DateLastModified");
- cmd.Parameters.Add(cmd, "@ItemCount");
- }
-
- IDbTransaction transaction = null;
-
- try
- {
- transaction = connection.BeginTransaction();
-
- var index = 0;
-
- cmd.GetParameter(index++).Value = new Guid(job.Id);
- cmd.GetParameter(index++).Value = job.TargetId;
- cmd.GetParameter(index++).Value = job.Name;
- cmd.GetParameter(index++).Value = job.Profile;
- cmd.GetParameter(index++).Value = job.Quality;
- cmd.GetParameter(index++).Value = job.Bitrate;
- cmd.GetParameter(index++).Value = job.Status.ToString();
- cmd.GetParameter(index++).Value = job.Progress;
- cmd.GetParameter(index++).Value = job.UserId;
- cmd.GetParameter(index++).Value = string.Join(",", job.RequestedItemIds.ToArray());
- cmd.GetParameter(index++).Value = job.Category;
- cmd.GetParameter(index++).Value = job.ParentId;
- cmd.GetParameter(index++).Value = job.UnwatchedOnly;
- cmd.GetParameter(index++).Value = job.ItemLimit;
- cmd.GetParameter(index++).Value = job.SyncNewContent;
- cmd.GetParameter(index++).Value = job.DateCreated;
- cmd.GetParameter(index++).Value = job.DateLastModified;
- cmd.GetParameter(index++).Value = job.ItemCount;
-
- cmd.Transaction = transaction;
-
- cmd.ExecuteNonQuery();
-
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to save record:", e);
-
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
- }
- }
- }
- }
- }
-
- public async Task DeleteJob(string id)
- {
- if (string.IsNullOrWhiteSpace(id))
- {
- throw new ArgumentNullException("id");
- }
-
- CheckDisposed();
-
- using (var connection = await CreateConnection().ConfigureAwait(false))
- {
- using (var deleteJobCommand = connection.CreateCommand())
- {
- using (var deleteJobItemsCommand = connection.CreateCommand())
- {
- IDbTransaction transaction = null;
-
- try
- {
- // _deleteJobCommand
- deleteJobCommand.CommandText = "delete from SyncJobs where Id=@Id";
- deleteJobCommand.Parameters.Add(deleteJobCommand, "@Id");
-
- transaction = connection.BeginTransaction();
-
- deleteJobCommand.GetParameter(0).Value = new Guid(id);
- deleteJobCommand.Transaction = transaction;
- deleteJobCommand.ExecuteNonQuery();
-
- // _deleteJobItemsCommand
- deleteJobItemsCommand.CommandText = "delete from SyncJobItems where JobId=@JobId";
- deleteJobItemsCommand.Parameters.Add(deleteJobItemsCommand, "@JobId");
-
- deleteJobItemsCommand.GetParameter(0).Value = id;
- deleteJobItemsCommand.Transaction = transaction;
- deleteJobItemsCommand.ExecuteNonQuery();
-
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to save record:", e);
-
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
- }
- }
- }
- }
- }
- }
-
- public QueryResult<SyncJob> GetJobs(SyncJobQuery query)
- {
- if (query == null)
- {
- throw new ArgumentNullException("query");
- }
-
- CheckDisposed();
-
- using (var connection = CreateConnection(true).Result)
- {
- using (var cmd = connection.CreateCommand())
- {
- cmd.CommandText = BaseJobSelectText;
-
- var whereClauses = new List<string>();
-
- if (query.Statuses.Length > 0)
- {
- var statuses = string.Join(",", query.Statuses.Select(i => "'" + i.ToString() + "'").ToArray());
-
- whereClauses.Add(string.Format("Status in ({0})", statuses));
- }
- if (!string.IsNullOrWhiteSpace(query.TargetId))
- {
- whereClauses.Add("TargetId=@TargetId");
- cmd.Parameters.Add(cmd, "@TargetId", DbType.String).Value = query.TargetId;
- }
- if (!string.IsNullOrWhiteSpace(query.ExcludeTargetIds))
- {
- var excludeIds = (query.ExcludeTargetIds ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
- if (excludeIds.Length == 1)
- {
- whereClauses.Add("TargetId<>@ExcludeTargetId");
- cmd.Parameters.Add(cmd, "@ExcludeTargetId", DbType.String).Value = excludeIds[0];
- }
- else if (excludeIds.Length > 1)
- {
- whereClauses.Add("TargetId<>@ExcludeTargetId");
- cmd.Parameters.Add(cmd, "@ExcludeTargetId", DbType.String).Value = excludeIds[0];
- }
- }
- if (!string.IsNullOrWhiteSpace(query.UserId))
- {
- whereClauses.Add("UserId=@UserId");
- cmd.Parameters.Add(cmd, "@UserId", DbType.String).Value = query.UserId;
- }
- if (query.SyncNewContent.HasValue)
- {
- whereClauses.Add("SyncNewContent=@SyncNewContent");
- cmd.Parameters.Add(cmd, "@SyncNewContent", DbType.Boolean).Value = query.SyncNewContent.Value;
- }
-
- cmd.CommandText += " mainTable";
-
- var whereTextWithoutPaging = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
-
- var startIndex = query.StartIndex ?? 0;
- if (startIndex > 0)
- {
- whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM SyncJobs ORDER BY (Select Max(DateLastModified) from SyncJobs where TargetId=mainTable.TargetId) DESC, DateLastModified DESC LIMIT {0})",
- startIndex.ToString(_usCulture)));
- }
-
- if (whereClauses.Count > 0)
- {
- cmd.CommandText += " where " + string.Join(" AND ", whereClauses.ToArray());
- }
-
- cmd.CommandText += " ORDER BY (Select Max(DateLastModified) from SyncJobs where TargetId=mainTable.TargetId) DESC, DateLastModified DESC";
-
- if (query.Limit.HasValue)
- {
- cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(_usCulture);
- }
-
- cmd.CommandText += "; select count (Id) from SyncJobs" + whereTextWithoutPaging;
-
- var list = new List<SyncJob>();
- var count = 0;
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
- {
- while (reader.Read())
- {
- list.Add(GetJob(reader));
- }
-
- if (reader.NextResult() && reader.Read())
- {
- count = reader.GetInt32(0);
- }
- }
-
- return new QueryResult<SyncJob>()
- {
- Items = list.ToArray(),
- TotalRecordCount = count
- };
- }
- }
- }
-
- public SyncJobItem GetJobItem(string id)
- {
- if (string.IsNullOrEmpty(id))
- {
- throw new ArgumentNullException("id");
- }
-
- CheckDisposed();
-
- var guid = new Guid(id);
-
- using (var connection = CreateConnection(true).Result)
- {
- using (var cmd = connection.CreateCommand())
- {
- cmd.CommandText = BaseJobItemSelectText + " where Id=@Id";
-
- cmd.Parameters.Add(cmd, "@Id", DbType.Guid).Value = guid;
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
- {
- if (reader.Read())
- {
- return GetJobItem(reader);
- }
- }
- }
-
- return null;
- }
- }
-
- private QueryResult<T> GetJobItemReader<T>(SyncJobItemQuery query, string baseSelectText, Func<IDataReader, T> itemFactory)
- {
- if (query == null)
- {
- throw new ArgumentNullException("query");
- }
-
- using (var connection = CreateConnection(true).Result)
- {
- using (var cmd = connection.CreateCommand())
- {
- cmd.CommandText = baseSelectText;
-
- var whereClauses = new List<string>();
-
- if (!string.IsNullOrWhiteSpace(query.JobId))
- {
- whereClauses.Add("JobId=@JobId");
- cmd.Parameters.Add(cmd, "@JobId", DbType.String).Value = query.JobId;
- }
- if (!string.IsNullOrWhiteSpace(query.ItemId))
- {
- whereClauses.Add("ItemId=@ItemId");
- cmd.Parameters.Add(cmd, "@ItemId", DbType.String).Value = query.ItemId;
- }
- if (!string.IsNullOrWhiteSpace(query.TargetId))
- {
- whereClauses.Add("TargetId=@TargetId");
- cmd.Parameters.Add(cmd, "@TargetId", DbType.String).Value = query.TargetId;
- }
-
- if (query.Statuses.Length > 0)
- {
- var statuses = string.Join(",", query.Statuses.Select(i => "'" + i.ToString() + "'").ToArray());
-
- whereClauses.Add(string.Format("Status in ({0})", statuses));
- }
-
- var whereTextWithoutPaging = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
-
- var startIndex = query.StartIndex ?? 0;
- if (startIndex > 0)
- {
- whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM SyncJobItems ORDER BY JobItemIndex, DateCreated LIMIT {0})",
- startIndex.ToString(_usCulture)));
- }
-
- if (whereClauses.Count > 0)
- {
- cmd.CommandText += " where " + string.Join(" AND ", whereClauses.ToArray());
- }
-
- cmd.CommandText += " ORDER BY JobItemIndex, DateCreated";
-
- if (query.Limit.HasValue)
- {
- cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(_usCulture);
- }
-
- cmd.CommandText += "; select count (Id) from SyncJobItems" + whereTextWithoutPaging;
-
- var list = new List<T>();
- var count = 0;
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
- {
- while (reader.Read())
- {
- list.Add(itemFactory(reader));
- }
-
- if (reader.NextResult() && reader.Read())
- {
- count = reader.GetInt32(0);
- }
- }
-
- return new QueryResult<T>()
- {
- Items = list.ToArray(),
- TotalRecordCount = count
- };
- }
- }
- }
-
- public Dictionary<string, SyncedItemProgress> GetSyncedItemProgresses(SyncJobItemQuery query)
- {
- var result = new Dictionary<string, SyncedItemProgress>();
-
- var now = DateTime.UtcNow;
-
- using (var connection = CreateConnection(true).Result)
- {
- using (var cmd = connection.CreateCommand())
- {
- cmd.CommandText = "select ItemId,Status,Progress from SyncJobItems";
-
- var whereClauses = new List<string>();
-
- if (!string.IsNullOrWhiteSpace(query.TargetId))
- {
- whereClauses.Add("TargetId=@TargetId");
- cmd.Parameters.Add(cmd, "@TargetId", DbType.String).Value = query.TargetId;
- }
-
- if (query.Statuses.Length > 0)
- {
- var statuses = string.Join(",", query.Statuses.Select(i => "'" + i.ToString() + "'").ToArray());
-
- whereClauses.Add(string.Format("Status in ({0})", statuses));
- }
-
- if (whereClauses.Count > 0)
- {
- cmd.CommandText += " where " + string.Join(" AND ", whereClauses.ToArray());
- }
-
- cmd.CommandText += ";" + cmd.CommandText
- .Replace("select ItemId,Status,Progress from SyncJobItems", "select ItemIds,Status,Progress from SyncJobs")
- .Replace("'Synced'", "'Completed','CompletedWithError'");
-
- //Logger.Debug(cmd.CommandText);
-
- using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
- {
- LogQueryTime("GetSyncedItemProgresses", cmd, now);
-
- while (reader.Read())
- {
- AddStatusResult(reader, result, false);
- }
-
- if (reader.NextResult())
- {
- while (reader.Read())
- {
- AddStatusResult(reader, result, true);
- }
- }
- }
- }
- }
-
- return result;
- }
-
- private void LogQueryTime(string methodName, IDbCommand cmd, DateTime startDate)
- {
- var elapsed = (DateTime.UtcNow - startDate).TotalMilliseconds;
-
- var slowThreshold = 1000;
-
-#if DEBUG
- slowThreshold = 50;
-#endif
-
- if (elapsed >= slowThreshold)
- {
- Logger.Debug("{2} query time (slow): {0}ms. Query: {1}",
- Convert.ToInt32(elapsed),
- cmd.CommandText,
- methodName);
- }
- else
- {
- //Logger.Debug("{2} query time: {0}ms. Query: {1}",
- // Convert.ToInt32(elapsed),
- // cmd.CommandText,
- // methodName);
- }
- }
-
- private void AddStatusResult(IDataReader reader, Dictionary<string, SyncedItemProgress> result, bool multipleIds)
- {
- if (reader.IsDBNull(0))
- {
- return;
- }
-
- var itemIds = new List<string>();
-
- var ids = reader.GetString(0);
-
- if (multipleIds)
- {
- itemIds = ids.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
- }
- else
- {
- itemIds.Add(ids);
- }
-
- if (!reader.IsDBNull(1))
- {
- SyncJobItemStatus status;
- var statusString = reader.GetString(1);
- if (string.Equals(statusString, "Completed", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(statusString, "CompletedWithError", StringComparison.OrdinalIgnoreCase))
- {
- status = SyncJobItemStatus.Synced;
- }
- else
- {
- status = (SyncJobItemStatus)Enum.Parse(typeof(SyncJobItemStatus), statusString, true);
- }
-
- if (status == SyncJobItemStatus.Synced)
- {
- foreach (var itemId in itemIds)
- {
- result[itemId] = new SyncedItemProgress
- {
- Status = SyncJobItemStatus.Synced
- };
- }
- }
- else
- {
- double progress = reader.IsDBNull(2) ? 0.0 : reader.GetDouble(2);
-
- foreach (var itemId in itemIds)
- {
- SyncedItemProgress currentStatus;
- if (!result.TryGetValue(itemId, out currentStatus) || (currentStatus.Status != SyncJobItemStatus.Synced && progress >= currentStatus.Progress))
- {
- result[itemId] = new SyncedItemProgress
- {
- Status = status,
- Progress = progress
- };
- }
- }
- }
- }
- }
-
- public QueryResult<SyncJobItem> GetJobItems(SyncJobItemQuery query)
- {
- return GetJobItemReader(query, BaseJobItemSelectText, GetJobItem);
- }
-
- public Task Create(SyncJobItem jobItem)
- {
- return InsertOrUpdate(jobItem, true);
- }
-
- public Task Update(SyncJobItem jobItem)
- {
- return InsertOrUpdate(jobItem, false);
- }
-
- private async Task InsertOrUpdate(SyncJobItem jobItem, bool insert)
- {
- if (jobItem == null)
- {
- throw new ArgumentNullException("jobItem");
- }
-
- CheckDisposed();
-
- using (var connection = await CreateConnection().ConfigureAwait(false))
- {
- using (var cmd = connection.CreateCommand())
- {
- if (insert)
- {
- cmd.CommandText = "insert into SyncJobItems (Id, ItemId, ItemName, MediaSourceId, JobId, TemporaryPath, OutputPath, Status, TargetId, DateCreated, Progress, AdditionalFiles, MediaSource, IsMarkedForRemoval, JobItemIndex, ItemDateModifiedTicks) values (@Id, @ItemId, @ItemName, @MediaSourceId, @JobId, @TemporaryPath, @OutputPath, @Status, @TargetId, @DateCreated, @Progress, @AdditionalFiles, @MediaSource, @IsMarkedForRemoval, @JobItemIndex, @ItemDateModifiedTicks)";
-
- cmd.Parameters.Add(cmd, "@Id");
- cmd.Parameters.Add(cmd, "@ItemId");
- cmd.Parameters.Add(cmd, "@ItemName");
- cmd.Parameters.Add(cmd, "@MediaSourceId");
- cmd.Parameters.Add(cmd, "@JobId");
- cmd.Parameters.Add(cmd, "@TemporaryPath");
- cmd.Parameters.Add(cmd, "@OutputPath");
- cmd.Parameters.Add(cmd, "@Status");
- cmd.Parameters.Add(cmd, "@TargetId");
- cmd.Parameters.Add(cmd, "@DateCreated");
- cmd.Parameters.Add(cmd, "@Progress");
- cmd.Parameters.Add(cmd, "@AdditionalFiles");
- cmd.Parameters.Add(cmd, "@MediaSource");
- cmd.Parameters.Add(cmd, "@IsMarkedForRemoval");
- cmd.Parameters.Add(cmd, "@JobItemIndex");
- cmd.Parameters.Add(cmd, "@ItemDateModifiedTicks");
- }
- else
- {
- // cmd
- cmd.CommandText = "update SyncJobItems set ItemId=@ItemId,ItemName=@ItemName,MediaSourceId=@MediaSourceId,JobId=@JobId,TemporaryPath=@TemporaryPath,OutputPath=@OutputPath,Status=@Status,TargetId=@TargetId,DateCreated=@DateCreated,Progress=@Progress,AdditionalFiles=@AdditionalFiles,MediaSource=@MediaSource,IsMarkedForRemoval=@IsMarkedForRemoval,JobItemIndex=@JobItemIndex,ItemDateModifiedTicks=@ItemDateModifiedTicks where Id=@Id";
-
- cmd.Parameters.Add(cmd, "@Id");
- cmd.Parameters.Add(cmd, "@ItemId");
- cmd.Parameters.Add(cmd, "@ItemName");
- cmd.Parameters.Add(cmd, "@MediaSourceId");
- cmd.Parameters.Add(cmd, "@JobId");
- cmd.Parameters.Add(cmd, "@TemporaryPath");
- cmd.Parameters.Add(cmd, "@OutputPath");
- cmd.Parameters.Add(cmd, "@Status");
- cmd.Parameters.Add(cmd, "@TargetId");
- cmd.Parameters.Add(cmd, "@DateCreated");
- cmd.Parameters.Add(cmd, "@Progress");
- cmd.Parameters.Add(cmd, "@AdditionalFiles");
- cmd.Parameters.Add(cmd, "@MediaSource");
- cmd.Parameters.Add(cmd, "@IsMarkedForRemoval");
- cmd.Parameters.Add(cmd, "@JobItemIndex");
- cmd.Parameters.Add(cmd, "@ItemDateModifiedTicks");
- }
-
- IDbTransaction transaction = null;
-
- try
- {
- transaction = connection.BeginTransaction();
-
- var index = 0;
-
- cmd.GetParameter(index++).Value = new Guid(jobItem.Id);
- cmd.GetParameter(index++).Value = jobItem.ItemId;
- cmd.GetParameter(index++).Value = jobItem.ItemName;
- cmd.GetParameter(index++).Value = jobItem.MediaSourceId;
- cmd.GetParameter(index++).Value = jobItem.JobId;
- cmd.GetParameter(index++).Value = jobItem.TemporaryPath;
- cmd.GetParameter(index++).Value = jobItem.OutputPath;
- cmd.GetParameter(index++).Value = jobItem.Status.ToString();
- cmd.GetParameter(index++).Value = jobItem.TargetId;
- cmd.GetParameter(index++).Value = jobItem.DateCreated;
- cmd.GetParameter(index++).Value = jobItem.Progress;
- cmd.GetParameter(index++).Value = _json.SerializeToString(jobItem.AdditionalFiles);
- cmd.GetParameter(index++).Value = jobItem.MediaSource == null ? null : _json.SerializeToString(jobItem.MediaSource);
- cmd.GetParameter(index++).Value = jobItem.IsMarkedForRemoval;
- cmd.GetParameter(index++).Value = jobItem.JobItemIndex;
- cmd.GetParameter(index++).Value = jobItem.ItemDateModifiedTicks;
-
- cmd.Transaction = transaction;
-
- cmd.ExecuteNonQuery();
-
- transaction.Commit();
- }
- catch (OperationCanceledException)
- {
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- catch (Exception e)
- {
- Logger.ErrorException("Failed to save record:", e);
-
- if (transaction != null)
- {
- transaction.Rollback();
- }
-
- throw;
- }
- finally
- {
- if (transaction != null)
- {
- transaction.Dispose();
- }
- }
- }
- }
- }
-
- private SyncJobItem GetJobItem(IDataReader reader)
- {
- var info = new SyncJobItem
- {
- Id = reader.GetGuid(0).ToString("N"),
- ItemId = reader.GetString(1)
- };
-
- if (!reader.IsDBNull(2))
- {
- info.ItemName = reader.GetString(2);
- }
-
- if (!reader.IsDBNull(3))
- {
- info.MediaSourceId = reader.GetString(3);
- }
-
- info.JobId = reader.GetString(4);
-
- if (!reader.IsDBNull(5))
- {
- info.TemporaryPath = reader.GetString(5);
- }
- if (!reader.IsDBNull(6))
- {
- info.OutputPath = reader.GetString(6);
- }
-
- if (!reader.IsDBNull(7))
- {
- info.Status = (SyncJobItemStatus)Enum.Parse(typeof(SyncJobItemStatus), reader.GetString(7), true);
- }
-
- info.TargetId = reader.GetString(8);
-
- info.DateCreated = reader.GetDateTime(9).ToUniversalTime();
-
- if (!reader.IsDBNull(10))
- {
- info.Progress = reader.GetDouble(10);
- }
-
- if (!reader.IsDBNull(11))
- {
- var json = reader.GetString(11);
-
- if (!string.IsNullOrWhiteSpace(json))
- {
- info.AdditionalFiles = _json.DeserializeFromString<List<ItemFileInfo>>(json);
- }
- }
-
- if (!reader.IsDBNull(12))
- {
- var json = reader.GetString(12);
-
- if (!string.IsNullOrWhiteSpace(json))
- {
- info.MediaSource = _json.DeserializeFromString<MediaSourceInfo>(json);
- }
- }
-
- info.IsMarkedForRemoval = reader.GetBoolean(13);
- info.JobItemIndex = reader.GetInt32(14);
-
- if (!reader.IsDBNull(15))
- {
- info.ItemDateModifiedTicks = reader.GetInt64(15);
- }
-
- return info;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Udp/UdpMessageReceivedEventArgs.cs b/MediaBrowser.Server.Implementations/Udp/UdpMessageReceivedEventArgs.cs
deleted file mode 100644
index 5c83a13007..0000000000
--- a/MediaBrowser.Server.Implementations/Udp/UdpMessageReceivedEventArgs.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-
-namespace MediaBrowser.Server.Implementations.Udp
-{
- /// <summary>
- /// Class UdpMessageReceivedEventArgs
- /// </summary>
- public class UdpMessageReceivedEventArgs : EventArgs
- {
- /// <summary>
- /// Gets or sets the bytes.
- /// </summary>
- /// <value>The bytes.</value>
- public byte[] Bytes { get; set; }
- /// <summary>
- /// Gets or sets the remote end point.
- /// </summary>
- /// <value>The remote end point.</value>
- public string RemoteEndPoint { get; set; }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/app.config b/MediaBrowser.Server.Implementations/app.config
index 77b8b9218c..9d8c1ac93a 100644
--- a/MediaBrowser.Server.Implementations/app.config
+++ b/MediaBrowser.Server.Implementations/app.config
@@ -8,4 +8,4 @@
</dependentAssembly>
</assemblyBinding>
</runtime>
-<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1"/></startup></configuration>
+<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/></startup></configuration>
diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config
deleted file mode 100644
index 043257fc8a..0000000000
--- a/MediaBrowser.Server.Implementations/packages.config
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
- <package id="CommonIO" version="1.0.0.9" targetFramework="net45" />
- <package id="Emby.XmlTv" version="1.0.0.56" targetFramework="net45" />
- <package id="ini-parser" version="2.3.0" targetFramework="net45" />
- <package id="Interfaces.IO" version="1.0.0.5" targetFramework="net45" />
- <package id="MediaBrowser.Naming" version="1.0.0.55" targetFramework="net45" />
- <package id="morelinq" version="1.4.0" targetFramework="net45" />
- <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
- <package id="SimpleInjector" version="3.2.2" targetFramework="net45" />
- <package id="SocketHttpListener" version="1.0.0.40" targetFramework="net45" />
-</packages> \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/project.json b/MediaBrowser.Server.Implementations/project.json
new file mode 100644
index 0000000000..fbbe9eaf32
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/project.json
@@ -0,0 +1,17 @@
+{
+ "frameworks":{
+ "netstandard1.6":{
+ "dependencies":{
+ "NETStandard.Library":"1.6.0",
+ }
+ },
+ ".NETPortable,Version=v4.5,Profile=Profile7":{
+ "buildOptions": {
+ "define": [ ]
+ },
+ "frameworkAssemblies":{
+
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Server.Mac.sln b/MediaBrowser.Server.Mac.sln
index d1e2e4621b..9b04284c30 100644
--- a/MediaBrowser.Server.Mac.sln
+++ b/MediaBrowser.Server.Mac.sln
@@ -1,18 +1,16 @@

Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2012
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Server.Mac", "MediaBrowser.Server.Mac\Emby.Server.Mac.csproj", "{C97B98FA-00D4-4880-88B8-C76017A418AB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "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}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Common.Implementations", "MediaBrowser.Common.Implementations\MediaBrowser.Common.Implementations.csproj", "{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Controller", "MediaBrowser.Controller\MediaBrowser.Controller.csproj", "{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Dlna", "MediaBrowser.Dlna\MediaBrowser.Dlna.csproj", "{734098EB-6DC1-4DD0-A1CA-3140DCD2737C}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.LocalMetadata", "MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj", "{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.MediaEncoding", "MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj", "{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}"
@@ -33,119 +31,61 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenSubtitlesHandler", "Ope
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing", "Emby.Drawing\Emby.Drawing.csproj", "{08FFF49B-F175-4807-A2B5-73B0EBD9F716}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Nat", "Mono.Nat\Mono.Nat.csproj", "{D7453B88-2266-4805-B39B-2B5A2A33E1BA}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "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}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Dlna", "Emby.Dlna\Emby.Dlna.csproj", "{805844AB-E92F-45E6-9D99-4F6D48D129A5}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing.ImageMagick", "Emby.Drawing.ImageMagick\Emby.Drawing.ImageMagick.csproj", "{6CFEE013-6E7C-432B-AC37-CABF0880C69A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing.Net", "Emby.Drawing.Net\Emby.Drawing.Net.csproj", "{C97A239E-A96C-4D64-A844-CCF8CC30AECB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Photos", "Emby.Photos\Emby.Photos.csproj", "{89AB4548-770D-41FD-A891-8DAFF44F452C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Server.Implementations", "Emby.Server.Implementations\Emby.Server.Implementations.csproj", "{E383961B-9356-4D5D-8233-9A1079D03055}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RSSDP", "RSSDP\RSSDP.csproj", "{21002819-C39A-4D3E-BE83-2A276A77FB1F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SocketHttpListener.Portable", "SocketHttpListener.Portable\SocketHttpListener.Portable.csproj", "{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceStack", "ServiceStack\ServiceStack.csproj", "{680A1709-25EB-4D52-A87F-EE03FFD94BAA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
AppStore|Any CPU = AppStore|Any CPU
- Release Mono|Any CPU = Release Mono|Any CPU
+ AppStore|x86 = AppStore|x86
+ Debug|Any CPU = Debug|Any CPU
Debug|x86 = Debug|x86
- Release|x86 = Release|x86
+ Release Mono|Any CPU = Release Mono|Any CPU
Release Mono|x86 = Release Mono|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x86 = Release|x86
+ Signed|Any CPU = Signed|Any CPU
+ Signed|x86 = Signed|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.AppStore|Any CPU.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|x86.ActiveCfg = Debug|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Debug|x86.Build.0 = Debug|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release Mono|Any CPU.ActiveCfg = Debug|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release Mono|Any CPU.Build.0 = Debug|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release Mono|x86.ActiveCfg = Debug|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release Mono|x86.Build.0 = Debug|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|x86.ActiveCfg = Release|Any CPU
- {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release|x86.Build.0 = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.AppStore|Any CPU.Build.0 = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|x86.ActiveCfg = Debug|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|x86.Build.0 = Debug|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release Mono|x86.ActiveCfg = Release Mono|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release Mono|x86.Build.0 = Release Mono|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Any CPU.Build.0 = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|x86.ActiveCfg = Release|Any CPU
- {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|x86.Build.0 = Release|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.AppStore|Any CPU.Build.0 = Release|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Debug|Any CPU.Build.0 = 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 Mono|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release Mono|x86.ActiveCfg = Release Mono|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release Mono|x86.Build.0 = Release Mono|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|x86.ActiveCfg = Release|Any CPU
- {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release|x86.Build.0 = Release|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.AppStore|Any CPU.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|x86.ActiveCfg = Debug|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.Debug|x86.Build.0 = Debug|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.Release Mono|Any CPU.ActiveCfg = Debug|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.Release Mono|Any CPU.Build.0 = Debug|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.Release Mono|x86.ActiveCfg = Debug|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.Release Mono|x86.Build.0 = Debug|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|x86.ActiveCfg = Release|Any CPU
- {23499896-B135-4527-8574-C26E926EA99E}.Release|x86.Build.0 = Release|Any CPU
- {2E781478-814D-4A48-9D80-BFF206441A65}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
- {2E781478-814D-4A48-9D80-BFF206441A65}.AppStore|Any CPU.Build.0 = Release|Any CPU
- {2E781478-814D-4A48-9D80-BFF206441A65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2E781478-814D-4A48-9D80-BFF206441A65}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2E781478-814D-4A48-9D80-BFF206441A65}.Debug|x86.ActiveCfg = Debug|Any CPU
- {2E781478-814D-4A48-9D80-BFF206441A65}.Debug|x86.Build.0 = Debug|Any CPU
- {2E781478-814D-4A48-9D80-BFF206441A65}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
- {2E781478-814D-4A48-9D80-BFF206441A65}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
- {2E781478-814D-4A48-9D80-BFF206441A65}.Release Mono|x86.ActiveCfg = Release Mono|Any CPU
- {2E781478-814D-4A48-9D80-BFF206441A65}.Release Mono|x86.Build.0 = Release Mono|Any CPU
- {2E781478-814D-4A48-9D80-BFF206441A65}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2E781478-814D-4A48-9D80-BFF206441A65}.Release|Any CPU.Build.0 = Release|Any CPU
- {2E781478-814D-4A48-9D80-BFF206441A65}.Release|x86.ActiveCfg = Release|Any CPU
- {2E781478-814D-4A48-9D80-BFF206441A65}.Release|x86.Build.0 = Release|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.AppStore|Any CPU.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|x86.ActiveCfg = Debug|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Debug|x86.Build.0 = Debug|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release Mono|x86.ActiveCfg = Release Mono|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release Mono|x86.Build.0 = Release Mono|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|x86.ActiveCfg = Release|Any CPU
- {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release|x86.Build.0 = Release|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.AppStore|Any CPU.Build.0 = Release|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Debug|x86.ActiveCfg = Debug|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Debug|x86.Build.0 = Debug|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release Mono|x86.ActiveCfg = Release Mono|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release Mono|x86.Build.0 = Release Mono|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|x86.ActiveCfg = Release|Any CPU
- {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release|x86.Build.0 = Release|Any CPU
+ {C97B98FA-00D4-4880-88B8-C76017A418AB}.AppStore|Any CPU.ActiveCfg = AppStore|Any CPU
+ {C97B98FA-00D4-4880-88B8-C76017A418AB}.AppStore|Any CPU.Build.0 = AppStore|Any CPU
+ {C97B98FA-00D4-4880-88B8-C76017A418AB}.AppStore|x86.ActiveCfg = AppStore|Any CPU
+ {C97B98FA-00D4-4880-88B8-C76017A418AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C97B98FA-00D4-4880-88B8-C76017A418AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C97B98FA-00D4-4880-88B8-C76017A418AB}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C97B98FA-00D4-4880-88B8-C76017A418AB}.Debug|x86.Build.0 = Debug|Any CPU
+ {C97B98FA-00D4-4880-88B8-C76017A418AB}.Release Mono|Any CPU.ActiveCfg = Debug|Any CPU
+ {C97B98FA-00D4-4880-88B8-C76017A418AB}.Release Mono|Any CPU.Build.0 = Debug|Any CPU
+ {C97B98FA-00D4-4880-88B8-C76017A418AB}.Release Mono|x86.ActiveCfg = Debug|Any CPU
+ {C97B98FA-00D4-4880-88B8-C76017A418AB}.Release Mono|x86.Build.0 = Debug|Any CPU
+ {C97B98FA-00D4-4880-88B8-C76017A418AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C97B98FA-00D4-4880-88B8-C76017A418AB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C97B98FA-00D4-4880-88B8-C76017A418AB}.Release|x86.ActiveCfg = Release|Any CPU
+ {C97B98FA-00D4-4880-88B8-C76017A418AB}.Release|x86.Build.0 = Release|Any CPU
+ {C97B98FA-00D4-4880-88B8-C76017A418AB}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {C97B98FA-00D4-4880-88B8-C76017A418AB}.Signed|x86.ActiveCfg = Release|Any CPU
{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.AppStore|x86.ActiveCfg = Release Mono|Any CPU
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.AppStore|x86.Build.0 = Release Mono|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|x86.ActiveCfg = Debug|Any CPU
@@ -158,64 +98,14 @@ Global
{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release|Any CPU.Build.0 = 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
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.AppStore|Any CPU.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|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 Mono|Any CPU
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release Mono|x86.ActiveCfg = Release Mono|Any CPU
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release Mono|x86.Build.0 = Release Mono|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|x86.ActiveCfg = Release|Any CPU
- {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release|x86.Build.0 = Release|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.AppStore|Any CPU.Build.0 = Release|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Debug|x86.ActiveCfg = Debug|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Debug|x86.Build.0 = Debug|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release Mono|x86.ActiveCfg = Release Mono|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release Mono|x86.Build.0 = Release Mono|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release|Any CPU.Build.0 = Release|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release|x86.ActiveCfg = Release|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release|x86.Build.0 = Release|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.AppStore|Any CPU.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|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 Mono|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release Mono|x86.ActiveCfg = Release Mono|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release Mono|x86.Build.0 = Release Mono|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|x86.ActiveCfg = Release|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release|x86.Build.0 = Release|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.AppStore|Any CPU.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|x86.ActiveCfg = Debug|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Debug|x86.Build.0 = Debug|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release Mono|Any CPU.ActiveCfg = Debug|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release Mono|Any CPU.Build.0 = Debug|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release Mono|x86.ActiveCfg = Debug|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release Mono|x86.Build.0 = Debug|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|x86.ActiveCfg = Release|Any CPU
- {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release|x86.Build.0 = Release|Any CPU
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|Any CPU.ActiveCfg = Release Mono|Any CPU
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|Any CPU.Build.0 = Release Mono|Any CPU
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|x86.ActiveCfg = Release Mono|Any CPU
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|x86.Build.0 = Release Mono|Any CPU
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.AppStore|x86.ActiveCfg = Release Mono|Any CPU
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.AppStore|x86.Build.0 = Release Mono|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|x86.ActiveCfg = Debug|Any CPU
@@ -228,8 +118,134 @@ Global
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release|Any CPU.Build.0 = 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 Mono|Any CPU
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|Any CPU.Build.0 = Release Mono|Any CPU
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|x86.ActiveCfg = Release Mono|Any CPU
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|x86.Build.0 = Release Mono|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.AppStore|x86.ActiveCfg = Release Mono|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.AppStore|x86.Build.0 = Release Mono|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Debug|Any CPU.Build.0 = 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 Mono|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release Mono|x86.ActiveCfg = Release Mono|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release Mono|x86.Build.0 = Release Mono|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|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 Mono|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Signed|Any CPU.Build.0 = Release Mono|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Signed|x86.ActiveCfg = Release Mono|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Signed|x86.Build.0 = Release Mono|Any CPU
+ {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.AppStore|x86.ActiveCfg = Debug|Any CPU
+ {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.AppStore|x86.Build.0 = Debug|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|x86.ActiveCfg = Debug|Any CPU
+ {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Debug|x86.Build.0 = Debug|Any CPU
+ {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release Mono|Any CPU.ActiveCfg = Debug|Any CPU
+ {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release Mono|Any CPU.Build.0 = Debug|Any CPU
+ {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release Mono|x86.ActiveCfg = Debug|Any CPU
+ {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release Mono|x86.Build.0 = Debug|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|x86.ActiveCfg = Release|Any CPU
+ {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Release|x86.Build.0 = Release|Any CPU
+ {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Signed|Any CPU.ActiveCfg = Debug|Any CPU
+ {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Signed|Any CPU.Build.0 = Debug|Any CPU
+ {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Signed|x86.ActiveCfg = Debug|Any CPU
+ {7EF9F3E0-697D-42F3-A08F-19DEB5F84392}.Signed|x86.Build.0 = Debug|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.AppStore|x86.ActiveCfg = Release Mono|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.AppStore|x86.Build.0 = Release Mono|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|x86.Build.0 = Debug|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release Mono|x86.ActiveCfg = Release Mono|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release Mono|x86.Build.0 = Release Mono|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|x86.ActiveCfg = Release|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|x86.Build.0 = Release|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|Any CPU.ActiveCfg = Release Mono|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|Any CPU.Build.0 = Release Mono|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|x86.ActiveCfg = Release Mono|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|x86.Build.0 = Release Mono|Any CPU
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.AppStore|x86.ActiveCfg = Debug|Any CPU
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.AppStore|x86.Build.0 = Debug|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|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|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|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 = Debug|Any CPU
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Signed|Any CPU.Build.0 = Debug|Any CPU
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Signed|x86.ActiveCfg = Debug|Any CPU
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Signed|x86.Build.0 = Debug|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.AppStore|x86.ActiveCfg = Release Mono|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.AppStore|x86.Build.0 = Release Mono|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|x86.ActiveCfg = Debug|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Debug|x86.Build.0 = Debug|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release Mono|x86.ActiveCfg = Release Mono|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release Mono|x86.Build.0 = Release Mono|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|x86.ActiveCfg = Release|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release|x86.Build.0 = Release|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|Any CPU.ActiveCfg = Release Mono|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|Any CPU.Build.0 = Release Mono|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|x86.ActiveCfg = Release Mono|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|x86.Build.0 = Release Mono|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.AppStore|x86.ActiveCfg = Release Mono|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.AppStore|x86.Build.0 = Release Mono|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Debug|x86.Build.0 = Debug|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Release Mono|x86.ActiveCfg = Release Mono|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Release Mono|x86.Build.0 = Release Mono|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Release|x86.ActiveCfg = Release|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Release|x86.Build.0 = Release|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Signed|Any CPU.ActiveCfg = Release Mono|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Signed|Any CPU.Build.0 = Release Mono|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Signed|x86.ActiveCfg = Release Mono|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Signed|x86.Build.0 = Release Mono|Any CPU
{B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
{B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.AppStore|x86.ActiveCfg = Debug|Any CPU
+ {B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.AppStore|x86.Build.0 = Debug|Any CPU
{B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Debug|x86.ActiveCfg = Debug|Any CPU
@@ -242,48 +258,293 @@ Global
{B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Release|Any CPU.Build.0 = Release|Any CPU
{B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Release|x86.ActiveCfg = Release|Any CPU
{B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Release|x86.Build.0 = Release|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.AppStore|Any CPU.Build.0 = Release|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|x86.ActiveCfg = Debug|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|x86.Build.0 = Debug|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release Mono|x86.ActiveCfg = Release Mono|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release Mono|x86.Build.0 = Release Mono|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|Any CPU.Build.0 = Release|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|x86.ActiveCfg = Release|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|x86.Build.0 = Release|Any CPU
- {C97B98FA-00D4-4880-88B8-C76017A418AB}.AppStore|Any CPU.ActiveCfg = AppStore|Any CPU
- {C97B98FA-00D4-4880-88B8-C76017A418AB}.AppStore|Any CPU.Build.0 = AppStore|Any CPU
- {C97B98FA-00D4-4880-88B8-C76017A418AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C97B98FA-00D4-4880-88B8-C76017A418AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C97B98FA-00D4-4880-88B8-C76017A418AB}.Debug|x86.ActiveCfg = Debug|Any CPU
- {C97B98FA-00D4-4880-88B8-C76017A418AB}.Debug|x86.Build.0 = Debug|Any CPU
- {C97B98FA-00D4-4880-88B8-C76017A418AB}.Release Mono|Any CPU.ActiveCfg = Debug|Any CPU
- {C97B98FA-00D4-4880-88B8-C76017A418AB}.Release Mono|Any CPU.Build.0 = Debug|Any CPU
- {C97B98FA-00D4-4880-88B8-C76017A418AB}.Release Mono|x86.ActiveCfg = Debug|Any CPU
- {C97B98FA-00D4-4880-88B8-C76017A418AB}.Release Mono|x86.Build.0 = Debug|Any CPU
- {C97B98FA-00D4-4880-88B8-C76017A418AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C97B98FA-00D4-4880-88B8-C76017A418AB}.Release|Any CPU.Build.0 = Release|Any CPU
- {C97B98FA-00D4-4880-88B8-C76017A418AB}.Release|x86.ActiveCfg = Release|Any CPU
- {C97B98FA-00D4-4880-88B8-C76017A418AB}.Release|x86.Build.0 = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|Any CPU.Build.0 = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.AppStore|Any CPU.Build.0 = Debug|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|Any CPU.ActiveCfg = Debug|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|Any CPU.Build.0 = Debug|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|x86.ActiveCfg = Debug|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|x86.Build.0 = Debug|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|x86.ActiveCfg = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|x86.Build.0 = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|x86.ActiveCfg = Debug|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|x86.Build.0 = Debug|Any CPU
+ {B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Signed|Any CPU.ActiveCfg = Debug|Any CPU
+ {B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Signed|Any CPU.Build.0 = Debug|Any CPU
+ {B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Signed|x86.ActiveCfg = Debug|Any CPU
+ {B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Signed|x86.Build.0 = Debug|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.AppStore|x86.ActiveCfg = Release Mono|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.AppStore|x86.Build.0 = Release Mono|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|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 Mono|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release Mono|x86.ActiveCfg = Release Mono|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release Mono|x86.Build.0 = Release Mono|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|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 Mono|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|Any CPU.Build.0 = Release Mono|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|x86.ActiveCfg = Release Mono|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|x86.Build.0 = Release Mono|Any CPU
+ {23499896-B135-4527-8574-C26E926EA99E}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {23499896-B135-4527-8574-C26E926EA99E}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {23499896-B135-4527-8574-C26E926EA99E}.AppStore|x86.ActiveCfg = Debug|Any CPU
+ {23499896-B135-4527-8574-C26E926EA99E}.AppStore|x86.Build.0 = Debug|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|x86.ActiveCfg = Debug|Any CPU
+ {23499896-B135-4527-8574-C26E926EA99E}.Debug|x86.Build.0 = Debug|Any CPU
+ {23499896-B135-4527-8574-C26E926EA99E}.Release Mono|Any CPU.ActiveCfg = Debug|Any CPU
+ {23499896-B135-4527-8574-C26E926EA99E}.Release Mono|Any CPU.Build.0 = Debug|Any CPU
+ {23499896-B135-4527-8574-C26E926EA99E}.Release Mono|x86.ActiveCfg = Debug|Any CPU
+ {23499896-B135-4527-8574-C26E926EA99E}.Release Mono|x86.Build.0 = Debug|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|x86.ActiveCfg = Release|Any CPU
+ {23499896-B135-4527-8574-C26E926EA99E}.Release|x86.Build.0 = Release|Any CPU
+ {23499896-B135-4527-8574-C26E926EA99E}.Signed|Any CPU.ActiveCfg = Debug|Any CPU
+ {23499896-B135-4527-8574-C26E926EA99E}.Signed|Any CPU.Build.0 = Debug|Any CPU
+ {23499896-B135-4527-8574-C26E926EA99E}.Signed|x86.ActiveCfg = Debug|Any CPU
+ {23499896-B135-4527-8574-C26E926EA99E}.Signed|x86.Build.0 = Debug|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.AppStore|x86.ActiveCfg = Release Mono|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.AppStore|x86.Build.0 = Release Mono|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Debug|x86.Build.0 = Debug|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release Mono|x86.ActiveCfg = Release Mono|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release Mono|x86.Build.0 = Release Mono|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|x86.ActiveCfg = Release|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Release|x86.Build.0 = Release|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|Any CPU.ActiveCfg = Release Mono|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|Any CPU.Build.0 = Release Mono|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|x86.ActiveCfg = Release Mono|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|x86.Build.0 = Release Mono|Any CPU
+ {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.AppStore|x86.ActiveCfg = Debug|Any CPU
+ {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.AppStore|x86.Build.0 = Debug|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|x86.ActiveCfg = Debug|Any CPU
+ {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Debug|x86.Build.0 = Debug|Any CPU
+ {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release Mono|Any CPU.ActiveCfg = Debug|Any CPU
+ {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release Mono|Any CPU.Build.0 = Debug|Any CPU
+ {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release Mono|x86.ActiveCfg = Debug|Any CPU
+ {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release Mono|x86.Build.0 = Debug|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|x86.ActiveCfg = Release|Any CPU
+ {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Release|x86.Build.0 = Release|Any CPU
+ {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Signed|Any CPU.ActiveCfg = Debug|Any CPU
+ {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Signed|Any CPU.Build.0 = Debug|Any CPU
+ {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Signed|x86.ActiveCfg = Debug|Any CPU
+ {08FFF49B-F175-4807-A2B5-73B0EBD9F716}.Signed|x86.Build.0 = Debug|Any CPU
+ {88AE38DF-19D7-406F-A6A9-09527719A21E}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {88AE38DF-19D7-406F-A6A9-09527719A21E}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {88AE38DF-19D7-406F-A6A9-09527719A21E}.AppStore|x86.ActiveCfg = Release|Any CPU
+ {88AE38DF-19D7-406F-A6A9-09527719A21E}.AppStore|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|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|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|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|x86.ActiveCfg = Release|Any CPU
+ {88AE38DF-19D7-406F-A6A9-09527719A21E}.Signed|x86.Build.0 = Release|Any CPU
+ {713F42B5-878E-499D-A878-E4C652B1D5E8}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {713F42B5-878E-499D-A878-E4C652B1D5E8}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {713F42B5-878E-499D-A878-E4C652B1D5E8}.AppStore|x86.ActiveCfg = Release|Any CPU
+ {713F42B5-878E-499D-A878-E4C652B1D5E8}.AppStore|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|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|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|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|x86.ActiveCfg = Release|Any CPU
+ {713F42B5-878E-499D-A878-E4C652B1D5E8}.Signed|x86.Build.0 = Release|Any CPU
+ {805844AB-E92F-45E6-9D99-4F6D48D129A5}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {805844AB-E92F-45E6-9D99-4F6D48D129A5}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {805844AB-E92F-45E6-9D99-4F6D48D129A5}.AppStore|x86.ActiveCfg = Release|Any CPU
+ {805844AB-E92F-45E6-9D99-4F6D48D129A5}.AppStore|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|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|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|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|x86.ActiveCfg = Release|Any CPU
+ {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Signed|x86.Build.0 = Release|Any CPU
+ {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.AppStore|x86.ActiveCfg = Release|Any CPU
+ {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.AppStore|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|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|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|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|x86.ActiveCfg = Release|Any CPU
+ {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Signed|x86.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.AppStore|x86.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.AppStore|x86.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Debug|x86.Build.0 = Debug|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release Mono|Any CPU.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release Mono|x86.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release Mono|x86.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release|x86.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release|x86.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|Any CPU.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|x86.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|x86.Build.0 = Release|Any CPU
+ {89AB4548-770D-41FD-A891-8DAFF44F452C}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {89AB4548-770D-41FD-A891-8DAFF44F452C}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {89AB4548-770D-41FD-A891-8DAFF44F452C}.AppStore|x86.ActiveCfg = Release|Any CPU
+ {89AB4548-770D-41FD-A891-8DAFF44F452C}.AppStore|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|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|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|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|x86.ActiveCfg = Release|Any CPU
+ {89AB4548-770D-41FD-A891-8DAFF44F452C}.Signed|x86.Build.0 = Release|Any CPU
+ {E383961B-9356-4D5D-8233-9A1079D03055}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {E383961B-9356-4D5D-8233-9A1079D03055}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {E383961B-9356-4D5D-8233-9A1079D03055}.AppStore|x86.ActiveCfg = Release|Any CPU
+ {E383961B-9356-4D5D-8233-9A1079D03055}.AppStore|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|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|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|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|x86.ActiveCfg = Release|Any CPU
+ {E383961B-9356-4D5D-8233-9A1079D03055}.Signed|x86.Build.0 = Release|Any CPU
+ {21002819-C39A-4D3E-BE83-2A276A77FB1F}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {21002819-C39A-4D3E-BE83-2A276A77FB1F}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {21002819-C39A-4D3E-BE83-2A276A77FB1F}.AppStore|x86.ActiveCfg = Release|Any CPU
+ {21002819-C39A-4D3E-BE83-2A276A77FB1F}.AppStore|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|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|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|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|x86.ActiveCfg = Release|Any CPU
+ {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Signed|x86.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.AppStore|Any CPU.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.AppStore|x86.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.AppStore|x86.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|x86.Build.0 = Debug|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release Mono|Any CPU.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release Mono|x86.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release Mono|x86.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release|x86.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release|x86.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|Any CPU.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|x86.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|x86.Build.0 = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.AppStore|Any CPU.ActiveCfg = Signed|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.AppStore|Any CPU.Build.0 = Signed|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.AppStore|x86.ActiveCfg = Signed|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.AppStore|x86.Build.0 = Signed|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|x86.Build.0 = Debug|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Any CPU.Build.0 = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|x86.ActiveCfg = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|x86.Build.0 = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|x86.ActiveCfg = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|x86.Build.0 = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Any CPU.ActiveCfg = Signed|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Any CPU.Build.0 = Signed|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|x86.ActiveCfg = Signed|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|x86.Build.0 = Signed|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
Policies = $0
diff --git a/MediaBrowser.Server.Mac/AppController.cs b/MediaBrowser.Server.Mac/AppController.cs
index 8d690b813a..9e32b53f18 100644
--- a/MediaBrowser.Server.Mac/AppController.cs
+++ b/MediaBrowser.Server.Mac/AppController.cs
@@ -1,8 +1,6 @@
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Model.Logging;
-using MediaBrowser.Server.Startup.Common.Browser;
using System;
using MonoMac.Foundation;
using MonoMac.AppKit;
diff --git a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj
index 6995a5bece..dc61e19a0c 100644
--- a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj
+++ b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj
@@ -87,24 +87,49 @@
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Drawing" />
<Reference Include="XamMac" />
- <Reference Include="Mono.Posix">
+ <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="Patterns.Logging">
- <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
+ <Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
+ <HintPath>..\packages\NLog.4.4.0-betaV15\lib\net45\NLog.dll</HintPath>
+ <Private>True</Private>
</Reference>
- <Reference Include="CommonIO">
- <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
+ <Reference Include="ServiceStack.Text, Version=4.5.4.0, Culture=neutral, processorArchitecture=MSIL">
+ <HintPath>..\packages\ServiceStack.Text.4.5.4\lib\net45\ServiceStack.Text.dll</HintPath>
+ <Private>True</Private>
</Reference>
- <Reference Include="System.Data.SQLite">
- <HintPath>..\ThirdParty\System.Data.SQLite.ManagedOnly\1.0.94.0\System.Data.SQLite.dll</HintPath>
+ <Reference Include="SharpCompress, Version=0.14.0.0, Culture=neutral, processorArchitecture=MSIL">
+ <HintPath>..\packages\SharpCompress.0.14.0\lib\net45\SharpCompress.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="SimpleInjector, Version=3.2.4.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
+ <HintPath>..\packages\SimpleInjector.3.2.4\lib\net45\SimpleInjector.dll</HintPath>
+ <Private>True</Private>
</Reference>
<Reference Include="System.Data" />
+ <Reference Include="Emby.Common.Implementations">
+ <HintPath>..\ThirdParty\emby\Emby.Common.Implementations.dll</HintPath>
+ </Reference>
+ <Reference Include="Emby.Server.Core">
+ <HintPath>..\ThirdParty\emby\Emby.Server.Core.dll</HintPath>
+ </Reference>
+ <Reference Include="Mono.Nat">
+ <HintPath>..\ThirdParty\emby\Mono.Nat.dll</HintPath>
+ </Reference>
+ <Reference Include="TagLib.Portable">
+ <HintPath>..\ThirdParty\taglib\TagLib.Portable.dll</HintPath>
+ </Reference>
+ <Reference Include="SQLitePCLRaw.core">
+ <HintPath>..\packages\SQLitePCLRaw.core.1.1.1\lib\net45\SQLitePCLRaw.core.dll</HintPath>
+ </Reference>
+ <Reference Include="SQLitePCLRaw.provider.sqlite3">
+ <HintPath>..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.1\lib\net45\SQLitePCLRaw.provider.sqlite3.dll</HintPath>
+ </Reference>
</ItemGroup>
<ItemGroup>
<Folder Include="Resources\" />
<Folder Include="Native\" />
- <Folder Include="Security\" />
</ItemGroup>
<ItemGroup>
<Compile Include="AppDelegate.cs" />
@@ -115,69 +140,14 @@
<Compile Include="AppController.designer.cs">
<DependentUpon>AppController.cs</DependentUpon>
</Compile>
- <Compile Include="Native\NativeApp.cs" />
- <Compile Include="Native\BaseMonoApp.cs" />
- <Compile Include="Native\NetworkManager.cs" />
<Compile Include="..\SharedVersion.cs">
<Link>SharedVersion.cs</Link>
</Compile>
<Compile Include="Main.cs" />
<Compile Include="MenuBarIcon.cs" />
- <Compile Include="..\MediaBrowser.Server.Mono\Networking\CertificateGenerator.cs">
- <Link>Native\CertificateGenerator.cs</Link>
- </Compile>
- <Compile Include="Native\DbConnector.cs" />
- <Compile Include="..\MediaBrowser.Server.Implementations\Persistence\SqliteExtensions.cs">
- <Link>Native\SqliteExtensions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Server.Mono\Security\ASN1.cs">
- <Link>Security\ASN1.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Server.Mono\Security\ASN1Convert.cs">
- <Link>Security\ASN1Convert.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Server.Mono\Security\BitConverterLE.cs">
- <Link>Security\BitConverterLE.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Server.Mono\Security\CryptoConvert.cs">
- <Link>Security\CryptoConvert.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Server.Mono\Security\PKCS1.cs">
- <Link>Security\PKCS1.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Server.Mono\Security\PKCS12.cs">
- <Link>Security\PKCS12.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Server.Mono\Security\PKCS7.cs">
- <Link>Security\PKCS7.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Server.Mono\Security\PKCS8.cs">
- <Link>Security\PKCS8.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Server.Mono\Security\X501Name.cs">
- <Link>Security\X501Name.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Server.Mono\Security\X509Builder.cs">
- <Link>Security\X509Builder.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Server.Mono\Security\X509Certificate.cs">
- <Link>Security\X509Certificate.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Server.Mono\Security\X509CertificateBuilder.cs">
- <Link>Security\X509CertificateBuilder.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Server.Mono\Security\X509CertificateCollection.cs">
- <Link>Security\X509CertificateCollection.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Server.Mono\Security\X509Extension.cs">
- <Link>Security\X509Extension.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Server.Mono\Security\X509Extensions.cs">
- <Link>Security\X509Extensions.cs</Link>
- </Compile>
- <Compile Include="..\MediaBrowser.Server.Mono\Security\X520Attributes.cs">
- <Link>Security\X520Attributes.cs</Link>
- </Compile>
+ <Compile Include="MacAppHost.cs" />
+ <Compile Include="Native\MonoFileSystem.cs" />
+ <Compile Include="Native\PowerManagement.cs" />
</ItemGroup>
<ItemGroup>
<InterfaceDefinition Include="MainMenu.xib" />
@@ -207,18 +177,10 @@
<Project>{9142EEFA-7570-41E1-BFCC-468BB571AF2F}</Project>
<Name>MediaBrowser.Common</Name>
</ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Common.Implementations\MediaBrowser.Common.Implementations.csproj">
- <Project>{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}</Project>
- <Name>MediaBrowser.Common.Implementations</Name>
- </ProjectReference>
<ProjectReference Include="..\MediaBrowser.Server.Implementations\MediaBrowser.Server.Implementations.csproj">
<Project>{2E781478-814D-4A48-9D80-BFF206441A65}</Project>
<Name>MediaBrowser.Server.Implementations</Name>
</ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Dlna\MediaBrowser.Dlna.csproj">
- <Project>{734098EB-6DC1-4DD0-A1CA-3140DCD2737C}</Project>
- <Name>MediaBrowser.Dlna</Name>
- </ProjectReference>
<ProjectReference Include="..\MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj">
<Project>{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}</Project>
<Name>MediaBrowser.LocalMetadata</Name>
@@ -247,134 +209,57 @@
<Project>{4FD51AC5-2C16-4308-A993-C3A84F3B4582}</Project>
<Name>MediaBrowser.Api</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="..\Emby.Photos\Emby.Photos.csproj">
+ <Project>{89AB4548-770D-41FD-A891-8DAFF44F452C}</Project>
+ <Name>Emby.Photos</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Emby.Drawing.Net\Emby.Drawing.Net.csproj">
+ <Project>{C97A239E-A96C-4D64-A844-CCF8CC30AECB}</Project>
+ <Name>Emby.Drawing.Net</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\Emby.Drawing.csproj">
+ <Project>{08FFF49B-F175-4807-A2B5-73B0EBD9F716}</Project>
+ <Name>Emby.Drawing</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Emby.Dlna\Emby.Dlna.csproj">
+ <Project>{805844AB-E92F-45E6-9D99-4F6D48D129A5}</Project>
+ <Name>Emby.Dlna</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\DvdLib\DvdLib.csproj">
+ <Project>{713F42B5-878E-499D-A878-E4C652B1D5E8}</Project>
+ <Name>DvdLib</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\BDInfo\BDInfo.csproj">
+ <Project>{88AE38DF-19D7-406F-A6A9-09527719A21E}</Project>
+ <Name>BDInfo</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\SocketHttpListener.Portable\SocketHttpListener.Portable.csproj">
+ <Project>{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}</Project>
+ <Name>SocketHttpListener.Portable</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\ServiceStack\ServiceStack.csproj">
+ <Project>{680A1709-25EB-4D52-A87F-EE03FFD94BAA}</Project>
+ <Name>ServiceStack</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\RSSDP\RSSDP.csproj">
+ <Project>{21002819-C39A-4D3E-BE83-2A276A77FB1F}</Project>
+ <Name>RSSDP</Name>
+ </ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="statusicon.png" />
</ItemGroup>
<ItemGroup>
- <NativeReference Include="..\ThirdParty\SQLite3\osx\libsqlite3.0.dylib">
- <IsCxx>False</IsCxx>
- <Kind>Dynamic</Kind>
- </NativeReference>
- <NativeReference Include="..\ThirdParty\SQLite3\osx\libsqlite3.dylib">
- <IsCxx>False</IsCxx>
- <Kind>Dynamic</Kind>
- </NativeReference>
- </ItemGroup>
- <ItemGroup>
<BundleResource Include="Resources\appicon.icns" />
<BundleResource Include="Resources\MediaBrowser.Server.Mac\Images.xcassets\AppIcon.appiconset\Contents.json" />
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\index.html">
- <Link>Resources\swagger-ui\index.html</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\o2c.html">
- <Link>Resources\swagger-ui\o2c.html</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\patch.js">
- <Link>Resources\swagger-ui\patch.js</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\swagger-ui.js">
- <Link>Resources\swagger-ui\swagger-ui.js</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\swagger-ui.min.js">
- <Link>Resources\swagger-ui\swagger-ui.min.js</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\css\reset.css">
- <Link>Resources\swagger-ui\css\reset.css</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\css\screen.css">
- <Link>Resources\swagger-ui\css\screen.css</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\css\typography.css">
- <Link>Resources\swagger-ui\css\typography.css</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\fonts\droid-sans-v6-latin-700.eot">
- <Link>Resources\swagger-ui\fonts\droid-sans-v6-latin-700.eot</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\fonts\droid-sans-v6-latin-700.svg">
- <Link>Resources\swagger-ui\fonts\droid-sans-v6-latin-700.svg</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\fonts\droid-sans-v6-latin-700.ttf">
- <Link>Resources\swagger-ui\fonts\droid-sans-v6-latin-700.ttf</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\fonts\droid-sans-v6-latin-700.woff">
- <Link>Resources\swagger-ui\fonts\droid-sans-v6-latin-700.woff</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\fonts\droid-sans-v6-latin-700.woff2">
- <Link>Resources\swagger-ui\fonts\droid-sans-v6-latin-700.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\fonts\droid-sans-v6-latin-regular.eot">
- <Link>Resources\swagger-ui\fonts\droid-sans-v6-latin-regular.eot</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\fonts\droid-sans-v6-latin-regular.svg">
- <Link>Resources\swagger-ui\fonts\droid-sans-v6-latin-regular.svg</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\fonts\droid-sans-v6-latin-regular.ttf">
- <Link>Resources\swagger-ui\fonts\droid-sans-v6-latin-regular.ttf</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\fonts\droid-sans-v6-latin-regular.woff">
- <Link>Resources\swagger-ui\fonts\droid-sans-v6-latin-regular.woff</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\fonts\droid-sans-v6-latin-regular.woff2">
- <Link>Resources\swagger-ui\fonts\droid-sans-v6-latin-regular.woff2</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\images\explorer_icons.png">
- <Link>Resources\swagger-ui\images\explorer_icons.png</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\images\logo_small.png">
- <Link>Resources\swagger-ui\images\logo_small.png</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\images\pet_store_api.png">
- <Link>Resources\swagger-ui\images\pet_store_api.png</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\images\throbber.gif">
- <Link>Resources\swagger-ui\images\throbber.gif</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\images\wordnik_api.png">
- <Link>Resources\swagger-ui\images\wordnik_api.png</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\lib\backbone-min.js">
- <Link>Resources\swagger-ui\lib\backbone-min.js</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\lib\handlebars-2.0.0.js">
- <Link>Resources\swagger-ui\lib\handlebars-2.0.0.js</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\lib\highlight.7.3.pack.js">
- <Link>Resources\swagger-ui\lib\highlight.7.3.pack.js</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\lib\jquery-1.8.0.min.js">
- <Link>Resources\swagger-ui\lib\jquery-1.8.0.min.js</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\lib\jquery.ba-bbq.min.js">
- <Link>Resources\swagger-ui\lib\jquery.ba-bbq.min.js</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\lib\jquery.slideto.min.js">
- <Link>Resources\swagger-ui\lib\jquery.slideto.min.js</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\lib\jquery.wiggle.min.js">
- <Link>Resources\swagger-ui\lib\jquery.wiggle.min.js</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\lib\marked.js">
- <Link>Resources\swagger-ui\lib\marked.js</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\lib\shred.bundle.js">
- <Link>Resources\swagger-ui\lib\shred.bundle.js</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\lib\swagger-client.js">
- <Link>Resources\swagger-ui\lib\swagger-client.js</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\lib\swagger-oauth.js">
- <Link>Resources\swagger-ui\lib\swagger-oauth.js</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\lib\underscore-min.js">
- <Link>Resources\swagger-ui\lib\underscore-min.js</Link>
- </BundleResource>
- <BundleResource Include="..\ThirdParty\ServiceStack\swagger-ui\lib\shred\content.js">
- <Link>Resources\swagger-ui\lib\shred\content.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\about.html">
- <Link>Resources\dashboard-ui\about.html</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\addplugin.html">
<Link>Resources\dashboard-ui\addplugin.html</Link>
</BundleResource>
@@ -483,9 +368,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\librarydisplay.html">
<Link>Resources\dashboard-ui\librarydisplay.html</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\librarypathmapping.html">
- <Link>Resources\dashboard-ui\librarypathmapping.html</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\librarysettings.html">
<Link>Resources\dashboard-ui\librarysettings.html</Link>
</BundleResource>
@@ -498,9 +380,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\livetvitems.html">
<Link>Resources\dashboard-ui\livetvitems.html</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\livetvseriestimer.html">
- <Link>Resources\dashboard-ui\livetvseriestimer.html</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\livetvsettings.html">
<Link>Resources\dashboard-ui\livetvsettings.html</Link>
</BundleResource>
@@ -1065,6 +944,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\localassetmanager.js">
<Link>Resources\dashboard-ui\bower_components\emby-apiclient\localassetmanager.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\nullassetmanager.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-apiclient\nullassetmanager.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\serverdiscovery-chrome.js">
<Link>Resources\dashboard-ui\bower_components\emby-apiclient\serverdiscovery-chrome.js</Link>
</BundleResource>
@@ -1080,6 +962,15 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\sync\contentuploader.js">
<Link>Resources\dashboard-ui\bower_components\emby-apiclient\sync\contentuploader.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\sync\filerepository.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-apiclient\sync\filerepository.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\sync\itemrepository.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-apiclient\sync\itemrepository.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\sync\localsync.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-apiclient\sync\localsync.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\sync\mediasync.js">
<Link>Resources\dashboard-ui\bower_components\emby-apiclient\sync\mediasync.js</Link>
</BundleResource>
@@ -1092,6 +983,15 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\sync\serversync.js">
<Link>Resources\dashboard-ui\bower_components\emby-apiclient\sync\serversync.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\sync\transfermanager.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-apiclient\sync\transfermanager.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\sync\useractionrepository.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-apiclient\sync\useractionrepository.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\sync\userrepository.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-apiclient\sync\userrepository.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\.bower.json">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\.bower.json</Link>
</BundleResource>
@@ -1119,6 +1019,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\datetime.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\datetime.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\deletehelper.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\deletehelper.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\dom.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\dom.js</Link>
</BundleResource>
@@ -1137,6 +1040,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\globalize.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\globalize.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\idb.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\idb.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\inputmanager.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\inputmanager.js</Link>
</BundleResource>
@@ -1506,12 +1412,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\fullscreen\fullscreenmanager.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\fullscreen\fullscreenmanager.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\guide\guide-categories.js">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\guide\guide-categories.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\guide\guide-categories.template.html">
- <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\guide\guide-categories.template.html</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\guide\guide-settings.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\guide\guide-settings.js</Link>
</BundleResource>
@@ -1530,6 +1430,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\guide\tvguide.template.html">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\guide\tvguide.template.html</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\imageeditor\imageeditor.css">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\imageeditor\imageeditor.css</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\imageeditor\imageeditor.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\imageeditor\imageeditor.js</Link>
</BundleResource>
@@ -1569,6 +1472,12 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\itemidentifier\itemidentifier.template.html">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\itemidentifier\itemidentifier.template.html</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\lazyloader\lazyloader-intersectionobserver.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\lazyloader\lazyloader-intersectionobserver.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\lazyloader\lazyloader-scroll.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\lazyloader\lazyloader-scroll.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\listview\listview.css">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\listview\listview.css</Link>
</BundleResource>
@@ -1617,6 +1526,15 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\multiselect\multiselect.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\multiselect\multiselect.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\native-promise-only\README.md">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\native-promise-only\README.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\native-promise-only\test_adapter.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\native-promise-only\test_adapter.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\native-promise-only\lib\npo.src.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\native-promise-only\lib\npo.src.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\notifications\badge.png">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\notifications\badge.png</Link>
</BundleResource>
@@ -1671,6 +1589,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\recordingcreator\recordingeditor.template.html">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\recordingcreator\recordingeditor.template.html</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\recordingcreator\recordingfields.css">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\recordingcreator\recordingfields.css</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\recordingcreator\recordingfields.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\recordingcreator\recordingfields.js</Link>
</BundleResource>
@@ -1884,6 +1805,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\sync\sync.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\sync\sync.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\sync\synctoggle.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\sync\synctoggle.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\toast\toast.css">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\toast\toast.css</Link>
</BundleResource>
@@ -2055,6 +1979,12 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\hlsjs\API.md">
<Link>Resources\dashboard-ui\bower_components\hlsjs\API.md</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\hlsjs\CONTRIBUTING.md">
+ <Link>Resources\dashboard-ui\bower_components\hlsjs\CONTRIBUTING.md</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\hlsjs\ISSUE_TEMPLATE.md">
+ <Link>Resources\dashboard-ui\bower_components\hlsjs\ISSUE_TEMPLATE.md</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\hlsjs\LICENSE">
<Link>Resources\dashboard-ui\bower_components\hlsjs\LICENSE</Link>
</BundleResource>
@@ -2502,30 +2432,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\libjass\package.json">
<Link>Resources\dashboard-ui\bower_components\libjass\package.json</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\native-promise-only\.bower.json">
- <Link>Resources\dashboard-ui\bower_components\native-promise-only\.bower.json</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\native-promise-only\.gitignore">
- <Link>Resources\dashboard-ui\bower_components\native-promise-only\.gitignore</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\native-promise-only\.npmignore">
- <Link>Resources\dashboard-ui\bower_components\native-promise-only\.npmignore</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\native-promise-only\README.md">
- <Link>Resources\dashboard-ui\bower_components\native-promise-only\README.md</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\native-promise-only\build.js">
- <Link>Resources\dashboard-ui\bower_components\native-promise-only\build.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\native-promise-only\package.json">
- <Link>Resources\dashboard-ui\bower_components\native-promise-only\package.json</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\native-promise-only\test_adapter.js">
- <Link>Resources\dashboard-ui\bower_components\native-promise-only\test_adapter.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\native-promise-only\lib\npo.src.js">
- <Link>Resources\dashboard-ui\bower_components\native-promise-only\lib\npo.src.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\query-string\.bower.json">
<Link>Resources\dashboard-ui\bower_components\query-string\.bower.json</Link>
</BundleResource>
@@ -3234,9 +3140,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\librarymenu.js">
<Link>Resources\dashboard-ui\scripts\librarymenu.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\librarypathmapping.js">
- <Link>Resources\dashboard-ui\scripts\librarypathmapping.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\livetvchannel.js">
<Link>Resources\dashboard-ui\scripts\livetvchannel.js</Link>
</BundleResource>
@@ -3261,9 +3164,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\livetvschedule.js">
<Link>Resources\dashboard-ui\scripts\livetvschedule.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\livetvseriestimer.js">
- <Link>Resources\dashboard-ui\scripts\livetvseriestimer.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\livetvseriestimers.js">
<Link>Resources\dashboard-ui\scripts\livetvseriestimers.js</Link>
</BundleResource>
@@ -3534,6 +3434,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\da.json">
<Link>Resources\dashboard-ui\strings\da.json</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\de-DE.json">
+ <Link>Resources\dashboard-ui\strings\de-DE.json</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\de.json">
<Link>Resources\dashboard-ui\strings\de.json</Link>
</BundleResource>
@@ -3549,6 +3452,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\es-AR.json">
<Link>Resources\dashboard-ui\strings\es-AR.json</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\es-ES.json">
+ <Link>Resources\dashboard-ui\strings\es-ES.json</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\es-MX.json">
<Link>Resources\dashboard-ui\strings\es-MX.json</Link>
</BundleResource>
@@ -3591,6 +3497,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\ko.json">
<Link>Resources\dashboard-ui\strings\ko.json</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\lt-LT.json">
+ <Link>Resources\dashboard-ui\strings\lt-LT.json</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\ms.json">
<Link>Resources\dashboard-ui\strings\ms.json</Link>
</BundleResource>
@@ -3706,4 +3615,14 @@
<Link>Resources\dashboard-ui\thirdparty\jstree\themes\default\throbber.gif</Link>
</BundleResource>
</ItemGroup>
+ <ItemGroup>
+ <NativeReference Include="..\ThirdParty\SQLite3\osx\libsqlite3.0.dylib">
+ <Kind>Dynamic</Kind>
+ <SmartLink>False</SmartLink>
+ </NativeReference>
+ <NativeReference Include="..\ThirdParty\SQLite3\osx\libsqlite3.dylib">
+ <Kind>Dynamic</Kind>
+ <SmartLink>False</SmartLink>
+ </NativeReference>
+ </ItemGroup>
</Project> \ No newline at end of file
diff --git a/MediaBrowser.Server.Mac/MacAppHost.cs b/MediaBrowser.Server.Mac/MacAppHost.cs
new file mode 100644
index 0000000000..d73a8fbbda
--- /dev/null
+++ b/MediaBrowser.Server.Mac/MacAppHost.cs
@@ -0,0 +1,135 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using Emby.Server.Core;
+using Emby.Server.Implementations;
+using Emby.Server.Implementations.FFMpeg;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.System;
+using Emby.Server.Mac.Native;
+using System.Diagnostics;
+
+namespace MediaBrowser.Server.Mac
+{
+ public class MacAppHost : ApplicationHost
+ {
+ public MacAppHost(ServerApplicationPaths applicationPaths, ILogManager logManager, StartupOptions options, IFileSystem fileSystem, IPowerManagement powerManagement, string releaseAssetFilename, IEnvironmentInfo environmentInfo, MediaBrowser.Controller.Drawing.IImageEncoder imageEncoder, ISystemEvents systemEvents, IMemoryStreamFactory memoryStreamFactory, MediaBrowser.Common.Net.INetworkManager networkManager, Action<string, string> certificateGenerator, Func<string> defaultUsernameFactory) : base(applicationPaths, logManager, options, fileSystem, powerManagement, releaseAssetFilename, environmentInfo, imageEncoder, systemEvents, memoryStreamFactory, networkManager, certificateGenerator, defaultUsernameFactory)
+ {
+ }
+
+ public override bool CanSelfRestart
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ public override bool CanSelfUpdate
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ protected override bool SupportsDualModeSockets
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ protected override FFMpegInstallInfo GetFfmpegInstallInfo()
+ {
+ var info = new FFMpegInstallInfo();
+
+ info.ArchiveType = "7z";
+
+ switch (EnvironmentInfo.SystemArchitecture)
+ {
+ case Architecture.X64:
+ info.Version = "20160124";
+ break;
+ case Architecture.X86:
+ info.Version = "20150110";
+ break;
+ }
+
+ info.DownloadUrls = GetDownloadUrls();
+
+ return info;
+ }
+
+ private string[] GetDownloadUrls()
+ {
+ switch (EnvironmentInfo.SystemArchitecture)
+ {
+ case Architecture.X64:
+ return new[]
+ {
+ "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/osx/ffmpeg-x64-2.8.5.7z"
+ };
+ }
+
+ // No version available
+ return new string[] { };
+ }
+
+ protected override void RestartInternal()
+ {
+ MainClass.Restart();
+ }
+
+ protected override List<Assembly> GetAssembliesWithPartsInternal()
+ {
+ var list = new List<Assembly>();
+
+ list.Add(GetType().Assembly);
+
+ return list;
+ }
+
+ protected override void ShutdownInternal()
+ {
+ MainClass.Shutdown();
+ }
+
+ protected override void AuthorizeServer()
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override void ConfigureAutoRunInternal(bool autorun)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override void EnableLoopbackInternal(string appName)
+ {
+ }
+
+ public override bool SupportsRunningAsService
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public override bool SupportsAutoRunAtStartup
+ {
+ get { return false; }
+ }
+
+ public override bool IsRunningAsService
+ {
+ get
+ {
+ return false;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Mac/Main.cs b/MediaBrowser.Server.Mac/Main.cs
index b48f447078..cda6f459a9 100644
--- a/MediaBrowser.Server.Mac/Main.cs
+++ b/MediaBrowser.Server.Mac/Main.cs
@@ -1,47 +1,57 @@
-using System;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Server.Startup.Common;
+using MediaBrowser.Server.Startup.Common.IO;
+using MediaBrowser.Server.Implementations;
+using System;
using System.Diagnostics;
-using System.Drawing;
+using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Reflection;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Drawing;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Implementations.IO;
-using MediaBrowser.Common.Implementations.Logging;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Server.Implementations;
-using MediaBrowser.Server.Startup.Common;
-using MediaBrowser.Server.Startup.Common.Browser;
-using Microsoft.Win32;
using MonoMac.AppKit;
using MonoMac.Foundation;
using MonoMac.ObjCRuntime;
-using CommonIO;
-using MediaBrowser.Server.Implementations.Logging;
+using Emby.Server.Core;
+using Emby.Server.Implementations;
+using Emby.Common.Implementations.Logging;
+using Emby.Common.Implementations.EnvironmentInfo;
+using Emby.Server.Mac.Native;
+using Emby.Server.Implementations.IO;
+using Emby.Common.Implementations.Networking;
+using Emby.Common.Implementations.Security;
+using Mono.Unix.Native;
+using MediaBrowser.Model.System;
namespace MediaBrowser.Server.Mac
{
class MainClass
{
- internal static ApplicationHost AppHost;
+ internal static MacAppHost AppHost;
private static ILogger _logger;
static void Main (string[] args)
{
- var applicationPath = Assembly.GetEntryAssembly().Location;
+ SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_sqlite3());
- var options = new StartupOptions();
+ var applicationPath = Assembly.GetEntryAssembly().Location;
+
+ var options = new StartupOptions(Environment.GetCommandLineArgs());
// Allow this to be specified on the command line.
var customProgramDataPath = options.GetOption("-programdata");
- var appPaths = CreateApplicationPaths(applicationPath, customProgramDataPath);
+ var appFolderPath = Path.GetDirectoryName(applicationPath);
+
+ var appPaths = CreateApplicationPaths(appFolderPath, customProgramDataPath);
var logManager = new NlogManager(appPaths.LogDirectoryPath, "server");
logManager.ReloadLogger(LogSeverity.Info);
@@ -58,7 +68,7 @@ namespace MediaBrowser.Server.Mac
NSApplication.Main (args);
}
- private static ServerApplicationPaths CreateApplicationPaths(string applicationPath, string programDataPath)
+ private static ServerApplicationPaths CreateApplicationPaths(string appFolderPath, string programDataPath)
{
if (string.IsNullOrEmpty(programDataPath))
{
@@ -71,9 +81,9 @@ namespace MediaBrowser.Server.Mac
}
// Within the mac bundle, go uo two levels then down into Resources folder
- var resourcesPath = Path.Combine(Path.GetDirectoryName(Path.GetDirectoryName (applicationPath)), "Resources");
+ var resourcesPath = Path.Combine(Path.GetDirectoryName(appFolderPath), "Resources");
- return new ServerApplicationPaths(programDataPath, applicationPath, resourcesPath);
+ return new ServerApplicationPaths(programDataPath, appFolderPath, resourcesPath);
}
/// <summary>
@@ -86,17 +96,34 @@ namespace MediaBrowser.Server.Mac
ILogManager logManager,
StartupOptions options)
{
- SystemEvents.SessionEnding += SystemEvents_SessionEnding;
-
// Allow all https requests
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; });
- var fileSystem = new ManagedFileSystem(new PatternsLogger(logManager.GetLogger("FileSystem")), false, true);
+ var fileSystem = new MonoFileSystem(logManager.GetLogger("FileSystem"), false, false);
fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
- var nativeApp = new NativeApp(logManager.GetLogger("App"));
-
- AppHost = new ApplicationHost(appPaths, logManager, options, fileSystem, "Emby.Server.Mac.pkg", nativeApp);
+ var environmentInfo = GetEnvironmentInfo();
+
+ var imageEncoder = ImageEncoderHelper.GetImageEncoder(_logger,
+ logManager,
+ fileSystem,
+ options,
+ () => AppHost.HttpClient,
+ appPaths);
+
+ AppHost = new MacAppHost(appPaths,
+ logManager,
+ options,
+ fileSystem,
+ new PowerManagement(),
+ "Emby.Server.Mac.pkg",
+ environmentInfo,
+ imageEncoder,
+ new Startup.Common.SystemEvents(logManager.GetLogger("SystemEvents")),
+ new MemoryStreamProvider(),
+ new NetworkManager(logManager.GetLogger("NetworkManager")),
+ GenerateCertificate,
+ () => Environment.UserName);
if (options.ContainsOption("-v")) {
Console.WriteLine (AppHost.ApplicationVersion.ToString());
@@ -106,9 +133,95 @@ namespace MediaBrowser.Server.Mac
Console.WriteLine ("appHost.Init");
Task.Run (() => StartServer(CancellationToken.None));
- }
+ }
+
+ private static void GenerateCertificate(string certPath, string certHost)
+ {
+ CertificateGenerator.CreateSelfSignCertificatePfx(certPath, certHost, _logger);
+ }
+
+ private static EnvironmentInfo GetEnvironmentInfo()
+ {
+ var info = new EnvironmentInfo();
+
+ var uname = GetUnixName();
+
+ var sysName = uname.sysname ?? string.Empty;
+
+ if (string.Equals(sysName, "Darwin", StringComparison.OrdinalIgnoreCase))
+ {
+ //info.OperatingSystem = Startup.Common.OperatingSystem.Osx;
+ }
+ else if (string.Equals(sysName, "Linux", StringComparison.OrdinalIgnoreCase))
+ {
+ //info.OperatingSystem = Startup.Common.OperatingSystem.Linux;
+ }
+ else if (string.Equals(sysName, "BSD", StringComparison.OrdinalIgnoreCase))
+ {
+ //info.OperatingSystem = Startup.Common.OperatingSystem.Bsd;
+ //info.IsBsd = true;
+ }
+
+ var archX86 = new Regex("(i|I)[3-6]86");
+
+ if (archX86.IsMatch(uname.machine))
+ {
+ info.CustomArchitecture = Architecture.X86;
+ }
+ else if (string.Equals(uname.machine, "x86_64", StringComparison.OrdinalIgnoreCase))
+ {
+ info.CustomArchitecture = Architecture.X64;
+ }
+ else if (uname.machine.StartsWith("arm", StringComparison.OrdinalIgnoreCase))
+ {
+ info.CustomArchitecture = Architecture.Arm;
+ }
+ else if (System.Environment.Is64BitOperatingSystem)
+ {
+ info.CustomArchitecture = Architecture.X64;
+ }
+ else
+ {
+ info.CustomArchitecture = Architecture.X86;
+ }
+
+ return info;
+ }
+
+ private static Uname _unixName;
+
+ private static Uname GetUnixName()
+ {
+ if (_unixName == null)
+ {
+ var uname = new Uname();
+ try
+ {
+ Utsname utsname;
+ var callResult = Syscall.uname(out utsname);
+ if (callResult == 0)
+ {
+ uname.sysname = utsname.sysname ?? string.Empty;
+ uname.machine = utsname.machine ?? string.Empty;
+ }
+
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error getting unix name", ex);
+ }
+ _unixName = uname;
+ }
+ return _unixName;
+ }
+
+ public class Uname
+ {
+ public string sysname = string.Empty;
+ public string machine = string.Empty;
+ }
- private static async void StartServer(CancellationToken cancellationToken)
+ private static async void StartServer(CancellationToken cancellationToken)
{
var initProgress = new Progress<double>();
@@ -122,19 +235,6 @@ namespace MediaBrowser.Server.Mac
}
}
- /// <summary>
- /// Handles the SessionEnding event of the SystemEvents control.
- /// </summary>
- /// <param name="sender">The source of the event.</param>
- /// <param name="e">The <see cref="SessionEndingEventArgs"/> instance containing the event data.</param>
- static void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
- {
- if (e.Reason == SessionEndReasons.SystemShutdown)
- {
- Shutdown();
- }
- }
-
public static void Shutdown()
{
ShutdownApp();
@@ -202,10 +302,13 @@ namespace MediaBrowser.Server.Mac
class NoCheckCertificatePolicy : ICertificatePolicy
{
- public bool CheckValidationResult (ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem)
+ public bool CheckValidationResult (ServicePoint srvPoint,
+ System.Security.Cryptography.X509Certificates.X509Certificate certificate,
+ WebRequest request,
+ int certificateProblem)
{
return true;
}
- }
+ }
}
diff --git a/MediaBrowser.Server.Mac/MenuBarIcon.cs b/MediaBrowser.Server.Mac/MenuBarIcon.cs
index 865f45057a..1b335ce461 100644
--- a/MediaBrowser.Server.Mac/MenuBarIcon.cs
+++ b/MediaBrowser.Server.Mac/MenuBarIcon.cs
@@ -1,11 +1,10 @@
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Model.Logging;
-using MediaBrowser.Server.Startup.Common.Browser;
using System;
using MonoMac.Foundation;
using MonoMac.AppKit;
+using Emby.Server.Implementations.Browser;
namespace MediaBrowser.Server.Mac
{
diff --git a/MediaBrowser.Server.Mac/Native/BaseMonoApp.cs b/MediaBrowser.Server.Mac/Native/BaseMonoApp.cs
deleted file mode 100644
index f28ff8c0df..0000000000
--- a/MediaBrowser.Server.Mac/Native/BaseMonoApp.cs
+++ /dev/null
@@ -1,272 +0,0 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Server.Startup.Common;
-using Mono.Unix.Native;
-using System;
-using System.Collections.Generic;
-using System.Reflection;
-using System.Text.RegularExpressions;
-using MediaBrowser.Server.Implementations.Persistence;
-using MediaBrowser.Server.Startup.Common.FFMpeg;
-using System.Diagnostics;
-using MediaBrowser.Model.System;
-
-namespace MediaBrowser.Server.Mac
-{
- public abstract class BaseMonoApp : INativeApp
- {
- protected ILogger Logger { get; private set; }
-
- protected BaseMonoApp(ILogger logger)
- {
- Logger = logger;
- }
-
- /// <summary>
- /// Shutdowns this instance.
- /// </summary>
- public abstract void Shutdown();
-
- /// <summary>
- /// Restarts this instance.
- /// </summary>
- public virtual void Restart(StartupOptions options)
- {
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Determines whether this instance [can self restart].
- /// </summary>
- /// <returns><c>true</c> if this instance [can self restart]; otherwise, <c>false</c>.</returns>
- public virtual bool CanSelfRestart
- {
- get
- {
- return false;
- }
- }
-
- public void PreventSystemStandby()
- {
-
- }
-
- public void AllowSystemStandby()
- {
-
- }
-
- public IDbConnector GetDbConnector()
- {
- return new DbConnector(Logger);
- }
-
- public virtual bool SupportsLibraryMonitor
- {
- get
- {
- 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 bool CanSelfUpdate
- {
- get
- {
- return false;
- }
- }
-
- public bool SupportsAutoRunAtStartup
- {
- get { return false; }
- }
-
- public List<Assembly> GetAssembliesWithParts()
- {
- var list = new List<Assembly>();
-
- list.Add(GetType().Assembly);
-
- return list;
- }
-
- public void AuthorizeServer(int udpPort, int httpServerPort, int httpsPort, string applicationPath, string tempDirectory)
- {
- }
-
- private NativeEnvironment _nativeEnvironment;
- public NativeEnvironment Environment
- {
- get { return _nativeEnvironment ?? (_nativeEnvironment = GetEnvironmentInfo()); }
- }
-
- public bool SupportsRunningAsService
- {
- get
- {
- return false;
- }
- }
-
- public bool IsRunningAsService
- {
- get
- {
- return false;
- }
- }
-
- public void ConfigureAutoRun(bool autorun)
- {
- }
-
- public void LaunchUrl(string url)
- {
- var process = new Process
- {
- StartInfo = new ProcessStartInfo
- {
- FileName = url
- },
-
- EnableRaisingEvents = true,
- };
-
- process.Exited += ProcessExited;
-
- process.Start();
- }
-
- /// <summary>
- /// Processes the exited.
- /// </summary>
- /// <param name="sender">The sender.</param>
- /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
- private static void ProcessExited(object sender, EventArgs e)
- {
- ((Process)sender).Dispose();
- }
-
- public FFMpegInstallInfo GetFfmpegInstallInfo()
- {
- return GetInfo(Environment);
- }
-
- public static FFMpegInstallInfo GetInfo(NativeEnvironment environment)
- {
- var info = new FFMpegInstallInfo();
-
- info.ArchiveType = "7z";
-
- switch (environment.SystemArchitecture)
- {
- case Architecture.X64:
- info.Version = "20160124";
- break;
- case Architecture.X86:
- info.Version = "20150110";
- break;
- }
-
- info.DownloadUrls = GetDownloadUrls(environment);
-
- return info;
- }
-
- private static string[] GetDownloadUrls(NativeEnvironment environment)
- {
- switch (environment.SystemArchitecture)
- {
- case Architecture.X64:
- return new[]
- {
- "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/osx/ffmpeg-x64-2.8.5.7z"
- };
- }
-
- // No version available
- return new string[] { };
- }
-
- public INetworkManager CreateNetworkManager(ILogger logger)
- {
- return new NetworkManager(logger);
- }
-
- public void EnableLoopback(string appName)
- {
-
- }
-
- public bool PortsRequireAuthorization(string applicationPath)
- {
- return false;
- }
-
- private NativeEnvironment GetEnvironmentInfo()
- {
- var info = new NativeEnvironment
- {
- OperatingSystem = Startup.Common.OperatingSystem.Linux
- };
-
- var uname = GetUnixName();
-
- var sysName = uname.sysname ?? string.Empty;
-
- info.OperatingSystem = Startup.Common.OperatingSystem.Osx;
-
- var archX86 = new Regex("(i|I)[3-6]86");
-
- if (archX86.IsMatch(uname.machine))
- {
- info.SystemArchitecture = Architecture.X86;
- }
- else if (string.Equals(uname.machine, "x86_64", StringComparison.OrdinalIgnoreCase))
- {
- info.SystemArchitecture = Architecture.X64;
- }
- else if (uname.machine.StartsWith("arm", StringComparison.OrdinalIgnoreCase))
- {
- info.SystemArchitecture = Architecture.Arm;
- }
-
- info.OperatingSystemVersionString = string.IsNullOrWhiteSpace(sysName) ?
- System.Environment.OSVersion.VersionString :
- sysName;
-
- return info;
- }
-
- private Uname _unixName;
- private Uname GetUnixName()
- {
- if (_unixName == null)
- {
- var uname = new Uname();
- Utsname utsname;
- var callResult = Syscall.uname(out utsname);
- if (callResult == 0)
- {
- uname.sysname = utsname.sysname;
- uname.machine = utsname.machine;
- }
-
- _unixName = uname;
- }
- return _unixName;
- }
-
- private class Uname
- {
- public string sysname = string.Empty;
- public string machine = string.Empty;
- }
- }
-}
diff --git a/MediaBrowser.Server.Mac/Native/DbConnector.cs b/MediaBrowser.Server.Mac/Native/DbConnector.cs
deleted file mode 100644
index 4c19210b81..0000000000
--- a/MediaBrowser.Server.Mac/Native/DbConnector.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-using System.Data;
-using System.Data.SQLite;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Server.Implementations.Persistence;
-
-namespace MediaBrowser.Server.Mac
-{
- public class DbConnector : IDbConnector
- {
- private readonly ILogger _logger;
-
- public DbConnector(ILogger logger)
- {
- _logger = logger;
- }
-
- public Task<IDbConnection> Connect(string dbPath, bool isReadOnly, bool enablePooling = false, int? cacheSize = null)
- {
- return SqliteExtensions.ConnectToDb(dbPath, isReadOnly, enablePooling, cacheSize, _logger);
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Mac/Native/MonoFileSystem.cs b/MediaBrowser.Server.Mac/Native/MonoFileSystem.cs
new file mode 100644
index 0000000000..3fb07b8d83
--- /dev/null
+++ b/MediaBrowser.Server.Mac/Native/MonoFileSystem.cs
@@ -0,0 +1,21 @@
+using Emby.Common.Implementations.IO;
+using MediaBrowser.Model.Logging;
+using Mono.Unix.Native;
+
+namespace Emby.Server.Mac.Native
+{
+ public class MonoFileSystem : ManagedFileSystem
+ {
+ public MonoFileSystem(ILogger logger, bool supportsAsyncFileStreams, bool enableManagedInvalidFileNameChars) : base(logger, supportsAsyncFileStreams, enableManagedInvalidFileNameChars, true)
+ {
+ }
+
+ public override void SetExecutable(string path)
+ {
+ // Linux: File permission to 666, and user's execute bit
+ Logger.Info("Syscall.chmod {0} FilePermissions.DEFFILEMODE | FilePermissions.S_IRWXU | FilePermissions.S_IXGRP | FilePermissions.S_IXOTH", path);
+
+ Syscall.chmod(path, FilePermissions.DEFFILEMODE | FilePermissions.S_IRWXU | FilePermissions.S_IXGRP | FilePermissions.S_IXOTH);
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Mac/Native/NativeApp.cs b/MediaBrowser.Server.Mac/Native/NativeApp.cs
deleted file mode 100644
index 59fa92dd18..0000000000
--- a/MediaBrowser.Server.Mac/Native/NativeApp.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using System;
-using MediaBrowser.Server.Startup.Common;
-using MediaBrowser.Model.Logging;
-
-namespace MediaBrowser.Server.Mac
-{
- /// <summary>
- /// Class NativeApp
- /// </summary>
- public class NativeApp : BaseMonoApp
- {
- public NativeApp(ILogger logger)
- : base(logger)
- {
- }
-
- /// <summary>
- /// Shutdowns this instance.
- /// </summary>
- public override void Shutdown()
- {
- MainClass.Shutdown();
- }
-
- /// <summary>
- /// Determines whether this instance [can self restart].
- /// </summary>
- /// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value>
- public override bool CanSelfRestart
- {
- get
- {
- return true;
- }
- }
-
- /// <summary>
- /// Restarts this instance.
- /// </summary>
- public override void Restart(StartupOptions options)
- {
- MainClass.Restart();
- }
- }
-}
-
diff --git a/MediaBrowser.Server.Mac/Native/NetworkManager.cs b/MediaBrowser.Server.Mac/Native/NetworkManager.cs
deleted file mode 100644
index 959ac6774a..0000000000
--- a/MediaBrowser.Server.Mac/Native/NetworkManager.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using MediaBrowser.Common.Implementations.Networking;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
-using System.Collections.Generic;
-using MediaBrowser.Server.Mono.Networking;
-
-namespace MediaBrowser.Server.Mac
-{
- /// <summary>
- /// Class NetUtils
- /// </summary>
- public class NetworkManager : BaseNetworkManager, INetworkManager
- {
- public NetworkManager(ILogger logger)
- : base(logger)
- {
- }
-
- /// <summary>
- /// Gets the network shares.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>IEnumerable{NetworkShare}.</returns>
- public IEnumerable<NetworkShare> GetNetworkShares(string path)
- {
- return new List<NetworkShare> ();
- }
-
- /// <summary>
- /// Gets available devices within the domain
- /// </summary>
- /// <returns>PC's in the Domain</returns>
- public IEnumerable<FileSystemEntryInfo> GetNetworkDevices()
- {
- return new List<FileSystemEntryInfo> ();
- }
-
- /// <summary>
- /// Generates a self signed certificate at the locatation specified by <paramref name="certificatePath"/>.
- /// </summary>
- /// <param name="certificatePath">The path to generate the certificate.</param>
- /// <param name="hostname">The common name for the certificate.</param>
- public void GenerateSelfSignedSslCertificate(string certificatePath, string hostname)
- {
- CertificateGenerator.CreateSelfSignCertificatePfx(certificatePath, hostname, Logger);
- }
- }
-}
diff --git a/MediaBrowser.Server.Mac/Native/PowerManagement.cs b/MediaBrowser.Server.Mac/Native/PowerManagement.cs
new file mode 100644
index 0000000000..f3c97d6582
--- /dev/null
+++ b/MediaBrowser.Server.Mac/Native/PowerManagement.cs
@@ -0,0 +1,15 @@
+using MediaBrowser.Model.System;
+
+namespace Emby.Server.Mac.Native
+{
+ public class PowerManagement : IPowerManagement
+ {
+ public void PreventSystemStandby()
+ {
+ }
+
+ public void AllowSystemStandby()
+ {
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
index e7acb3f50f..24da2fd595 100644
--- a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
+++ b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
@@ -10,7 +10,7 @@
<RootNamespace>MediaBrowser.Server.Mono</RootNamespace>
<AssemblyName>MediaBrowser.Server.Mono</AssemblyName>
<StartupObject>MediaBrowser.Server.Mono.MainClass</StartupObject>
- <TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
+ <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<TargetFrameworkProfile />
</PropertyGroup>
@@ -24,7 +24,7 @@
<WarningLevel>4</WarningLevel>
<PlatformTarget>x86</PlatformTarget>
<Externalconsole>true</Externalconsole>
- <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <AllowUnsafeBlocks>false</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<DebugType>full</DebugType>
@@ -35,7 +35,7 @@
<PlatformTarget>AnyCPU</PlatformTarget>
<Externalconsole>true</Externalconsole>
<Prefer32Bit>false</Prefer32Bit>
- <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <AllowUnsafeBlocks>false</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<Optimize>false</Optimize>
@@ -55,66 +55,99 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release Mono|x86'">
<Prefer32Bit>false</Prefer32Bit>
- <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <AllowUnsafeBlocks>false</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
- <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
+ <Reference Include="Emby.Common.Implementations">
+ <HintPath>..\ThirdParty\emby\Emby.Common.Implementations.dll</HintPath>
+ </Reference>
+ <Reference Include="Emby.Server.Core">
+ <HintPath>..\ThirdParty\emby\Emby.Server.Core.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="Patterns.Logging, Version=1.0.5494.41209, Culture=neutral, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
+ <Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
+ <HintPath>..\packages\NLog.4.4.0-betaV15\lib\net45\NLog.dll</HintPath>
+ <Private>True</Private>
</Reference>
- <Reference Include="System" />
- <Reference Include="ServiceStack.Interfaces">
- <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath>
+ <Reference Include="ServiceStack.Text, Version=4.5.4.0, Culture=neutral, processorArchitecture=MSIL">
+ <HintPath>..\packages\ServiceStack.Text.4.5.4\lib\net45\ServiceStack.Text.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="SharpCompress, Version=0.14.0.0, Culture=neutral, processorArchitecture=MSIL">
+ <HintPath>..\packages\SharpCompress.0.14.0\lib\net45\SharpCompress.dll</HintPath>
+ <Private>True</Private>
</Reference>
+ <Reference Include="SimpleInjector, Version=3.2.4.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
+ <HintPath>..\packages\SimpleInjector.3.2.4\lib\net45\SimpleInjector.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
+ <HintPath>..\packages\SQLitePCLRaw.core.1.1.1\lib\net45\SQLitePCLRaw.core.dll</HintPath>
+ <Private>True</Private>
+ </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.1\lib\net45\SQLitePCLRaw.provider.sqlite3.dll</HintPath>
+ <Private>True</Private>
+ </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.Data.SQLite">
- <HintPath>..\ThirdParty\System.Data.SQLite.ManagedOnly\1.0.94.0\System.Data.SQLite.dll</HintPath>
- </Reference>
+ <Reference Include="System.IO.Compression" />
+ <Reference Include="System.Runtime.Serialization" />
+ <Reference Include="System.ServiceModel" />
+ <Reference Include="System.Transactions" />
+ <Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
- <Compile Include="..\MediaBrowser.Server.Implementations\Persistence\SqliteExtensions.cs">
- <Link>Native\SqliteExtensions.cs</Link>
- </Compile>
<Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link>
</Compile>
- <Compile Include="Native\BaseMonoApp.cs" />
- <Compile Include="Native\DbConnector.cs" />
- <Compile Include="Networking\CertificateGenerator.cs" />
+ <Compile Include="MonoAppHost.cs" />
+ <Compile Include="Native\MonoFileSystem.cs" />
+ <Compile Include="Native\PowerManagement.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="Native\NativeApp.cs" />
- <Compile Include="Networking\NetworkManager.cs" />
- <Compile Include="Security\ASN1.cs" />
- <Compile Include="Security\ASN1Convert.cs" />
- <Compile Include="Security\BitConverterLE.cs" />
- <Compile Include="Security\CryptoConvert.cs" />
- <Compile Include="Security\PKCS1.cs" />
- <Compile Include="Security\PKCS12.cs" />
- <Compile Include="Security\PKCS7.cs" />
- <Compile Include="Security\PKCS8.cs" />
- <Compile Include="Security\X501Name.cs" />
- <Compile Include="Security\X509Builder.cs" />
- <Compile Include="Security\X509Certificate.cs" />
- <Compile Include="Security\X509CertificateBuilder.cs" />
- <Compile Include="Security\X509CertificateCollection.cs" />
- <Compile Include="Security\X509Extension.cs" />
- <Compile Include="Security\X509Extensions.cs" />
- <Compile Include="Security\X520Attributes.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.Net\Emby.Drawing.Net.csproj">
+ <Project>{c97a239e-a96c-4d64-a844-ccf8cc30aecb}</Project>
+ <Name>Emby.Drawing.Net</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.Server.Startup.Common\MediaBrowser.Server.Startup.Common.csproj">
<Project>{b90ab8f2-1bff-4568-a3fd-2a338a435a75}</Project>
<Name>MediaBrowser.Server.Startup.Common</Name>
@@ -131,10 +164,6 @@
<Project>{442B5058-DCAF-4263-BB6A-F21E31120A1B}</Project>
<Name>MediaBrowser.Providers</Name>
</ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Dlna\MediaBrowser.Dlna.csproj">
- <Project>{734098EB-6DC1-4DD0-A1CA-3140DCD2737C}</Project>
- <Name>MediaBrowser.Dlna</Name>
- </ProjectReference>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
<Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project>
<Name>MediaBrowser.Model</Name>
@@ -143,10 +172,6 @@
<Project>{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}</Project>
<Name>MediaBrowser.Controller</Name>
</ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Common.Implementations\MediaBrowser.Common.Implementations.csproj">
- <Project>{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}</Project>
- <Name>MediaBrowser.Common.Implementations</Name>
- </ProjectReference>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
<Project>{9142EEFA-7570-41E1-BFCC-468BB571AF2F}</Project>
<Name>MediaBrowser.Common</Name>
@@ -167,6 +192,22 @@
<Project>{23499896-B135-4527-8574-C26E926EA99E}</Project>
<Name>MediaBrowser.XbmcMetadata</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>
+ <ProjectReference Include="..\ServiceStack\ServiceStack.csproj">
+ <Project>{680a1709-25eb-4d52-a87f-ee03ffd94baa}</Project>
+ <Name>ServiceStack</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\SocketHttpListener.Portable\SocketHttpListener.Portable.csproj">
+ <Project>{4f26d5d8-a7b0-42b3-ba42-7cb7d245934e}</Project>
+ <Name>SocketHttpListener.Portable</Name>
+ </ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="..\ThirdParty\SQLite3\osx\libsqlite3.0.dylib">
@@ -182,6 +223,10 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="packages.config" />
+ <None Include="SQLitePCLRaw.provider.sqlite3.dll.config">
+ <SubType>Designer</SubType>
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
<None Include="System.Data.SQLite.dll.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<SubType>Designer</SubType>
diff --git a/MediaBrowser.Server.Mono/MonoAppHost.cs b/MediaBrowser.Server.Mono/MonoAppHost.cs
new file mode 100644
index 0000000000..8def1ca2bf
--- /dev/null
+++ b/MediaBrowser.Server.Mono/MonoAppHost.cs
@@ -0,0 +1,154 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using Emby.Server.Core;
+using Emby.Server.Implementations;
+using Emby.Server.Implementations.FFMpeg;
+using MediaBrowser.IsoMounter;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.System;
+
+namespace MediaBrowser.Server.Mono
+{
+ public class MonoAppHost : ApplicationHost
+ {
+ public MonoAppHost(ServerApplicationPaths applicationPaths, ILogManager logManager, StartupOptions options, IFileSystem fileSystem, IPowerManagement powerManagement, string releaseAssetFilename, IEnvironmentInfo environmentInfo, MediaBrowser.Controller.Drawing.IImageEncoder imageEncoder, ISystemEvents systemEvents, IMemoryStreamFactory memoryStreamFactory, MediaBrowser.Common.Net.INetworkManager networkManager, Action<string, string> certificateGenerator, Func<string> defaultUsernameFactory) : base(applicationPaths, logManager, options, fileSystem, powerManagement, releaseAssetFilename, environmentInfo, imageEncoder, systemEvents, memoryStreamFactory, networkManager, certificateGenerator, defaultUsernameFactory)
+ {
+ }
+
+ public override bool CanSelfRestart
+ {
+ get
+ {
+ // A restart script must be provided
+ return StartupOptions.ContainsOption("-restartpath");
+ }
+ }
+
+ public override bool CanSelfUpdate
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ protected override FFMpegInstallInfo GetFfmpegInstallInfo()
+ {
+ var info = new FFMpegInstallInfo();
+
+ // Windows builds: http://ffmpeg.zeranoe.com/builds/
+ // Linux builds: http://johnvansickle.com/ffmpeg/
+ // OS X builds: http://ffmpegmac.net/
+ // OS X x64: http://www.evermeet.cx/ffmpeg/
+
+ var environment = (MonoEnvironmentInfo) EnvironmentInfo;
+
+ if (environment.IsBsd)
+ {
+
+ }
+ else if (environment.OperatingSystem == Model.System.OperatingSystem.Linux)
+ {
+ info.ArchiveType = "7z";
+ info.Version = "20160215";
+ }
+
+ // No version available - user requirement
+ info.DownloadUrls = new string[] { };
+
+ return info;
+ }
+
+ protected override void RestartInternal()
+ {
+ MainClass.Restart(StartupOptions);
+ }
+
+ protected override List<Assembly> GetAssembliesWithPartsInternal()
+ {
+ 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(LinuxIsoManager).Assembly);
+
+ return list;
+ }
+
+ protected override void ShutdownInternal()
+ {
+ MainClass.Shutdown();
+ }
+
+ protected override bool SupportsDualModeSockets
+ {
+ get
+ {
+ return GetMonoVersion() >= new Version(4, 6);
+ }
+ }
+
+ private static Version GetMonoVersion()
+ {
+ Type type = Type.GetType("Mono.Runtime");
+ if (type != null)
+ {
+ MethodInfo displayName = type.GetTypeInfo().GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static);
+ var displayNameValue = displayName.Invoke(null, null).ToString().Trim().Split(' ')[0];
+
+ Version version;
+ if (Version.TryParse(displayNameValue, out version))
+ {
+ return version;
+ }
+ }
+
+ return new Version(1, 0);
+ }
+
+ protected override void AuthorizeServer()
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override void ConfigureAutoRunInternal(bool autorun)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override void EnableLoopbackInternal(string appName)
+ {
+ }
+
+ public override bool SupportsRunningAsService
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public override bool SupportsAutoRunAtStartup
+ {
+ get { return false; }
+ }
+
+ public override bool IsRunningAsService
+ {
+ get
+ {
+ return false;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs b/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs
deleted file mode 100644
index 73e2686d29..0000000000
--- a/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs
+++ /dev/null
@@ -1,281 +0,0 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.IsoMounter;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Server.Mono.Networking;
-using MediaBrowser.Server.Startup.Common;
-using Mono.Unix.Native;
-using System;
-using System.Collections.Generic;
-using System.Reflection;
-using System.Text.RegularExpressions;
-using MediaBrowser.Model.System;
-using MediaBrowser.Server.Implementations.Persistence;
-using MediaBrowser.Server.Startup.Common.FFMpeg;
-using OperatingSystem = MediaBrowser.Server.Startup.Common.OperatingSystem;
-
-namespace MediaBrowser.Server.Mono.Native
-{
- public abstract class BaseMonoApp : INativeApp
- {
- protected StartupOptions StartupOptions { get; private set; }
- protected ILogger Logger { get; private set; }
-
- protected BaseMonoApp(StartupOptions startupOptions, ILogger logger)
- {
- StartupOptions = startupOptions;
- Logger = logger;
- }
-
- /// <summary>
- /// Shutdowns this instance.
- /// </summary>
- public abstract void Shutdown();
-
- /// <summary>
- /// Restarts this instance.
- /// </summary>
- public virtual void Restart(StartupOptions startupOptions)
- {
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Determines whether this instance [can self restart].
- /// </summary>
- /// <returns><c>true</c> if this instance [can self restart]; otherwise, <c>false</c>.</returns>
- public virtual bool CanSelfRestart
- {
- get
- {
- return false;
- }
- }
-
- /// <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 bool CanSelfUpdate
- {
- get
- {
- return false;
- }
- }
-
- public bool SupportsAutoRunAtStartup
- {
- get { return false; }
- }
-
- public void PreventSystemStandby()
- {
-
- }
-
- public void AllowSystemStandby()
- {
-
- }
-
- public List<Assembly> GetAssembliesWithParts()
- {
- var list = new List<Assembly>();
-
- if (Environment.OperatingSystem == Startup.Common.OperatingSystem.Linux)
- {
- list.AddRange(GetLinuxAssemblies());
- }
-
- list.Add(GetType().Assembly);
-
- return list;
- }
-
- private IEnumerable<Assembly> GetLinuxAssemblies()
- {
- var list = new List<Assembly>();
-
- list.Add(typeof(LinuxIsoManager).Assembly);
-
- return list;
- }
-
- public void AuthorizeServer(int udpPort, int httpServerPort, int httpsPort, string applicationPath, string tempDirectory)
- {
- }
-
- private NativeEnvironment _nativeEnvironment;
- public NativeEnvironment Environment
- {
- get { return _nativeEnvironment ?? (_nativeEnvironment = GetEnvironmentInfo()); }
- }
-
- public bool SupportsRunningAsService
- {
- get
- {
- return false;
- }
- }
-
- public bool IsRunningAsService
- {
- get
- {
- return false;
- }
- }
-
- public bool SupportsLibraryMonitor
- {
- get
- {
- return Environment.OperatingSystem != Startup.Common.OperatingSystem.Osx;
- }
- }
-
- public void ConfigureAutoRun(bool autorun)
- {
- }
-
- public INetworkManager CreateNetworkManager(ILogger logger)
- {
- return new NetworkManager(logger);
- }
-
- private NativeEnvironment GetEnvironmentInfo()
- {
- var info = new NativeEnvironment
- {
- OperatingSystem = Startup.Common.OperatingSystem.Linux
- };
-
- var uname = GetUnixName();
-
- var sysName = uname.sysname ?? string.Empty;
-
- if (string.Equals(sysName, "Darwin", StringComparison.OrdinalIgnoreCase))
- {
- info.OperatingSystem = Startup.Common.OperatingSystem.Osx;
- }
- else if (string.Equals(sysName, "Linux", StringComparison.OrdinalIgnoreCase))
- {
- info.OperatingSystem = Startup.Common.OperatingSystem.Linux;
- }
- else if (string.Equals(sysName, "BSD", StringComparison.OrdinalIgnoreCase))
- {
- info.OperatingSystem = Startup.Common.OperatingSystem.Bsd;
- }
-
- var archX86 = new Regex("(i|I)[3-6]86");
-
- if (archX86.IsMatch(uname.machine))
- {
- info.SystemArchitecture = Architecture.X86;
- }
- else if (string.Equals(uname.machine, "x86_64", StringComparison.OrdinalIgnoreCase))
- {
- info.SystemArchitecture = Architecture.X64;
- }
- else if (uname.machine.StartsWith("arm", StringComparison.OrdinalIgnoreCase))
- {
- info.SystemArchitecture = Architecture.Arm;
- }
- else if (System.Environment.Is64BitOperatingSystem)
- {
- info.SystemArchitecture = Architecture.X64;
- }
- else
- {
- info.SystemArchitecture = Architecture.X86;
- }
-
- info.OperatingSystemVersionString = string.IsNullOrWhiteSpace(sysName) ?
- System.Environment.OSVersion.VersionString :
- sysName;
-
- return info;
- }
-
- private Uname _unixName;
-
- private Uname GetUnixName()
- {
- if (_unixName == null)
- {
- var uname = new Uname();
- try
- {
- Utsname utsname;
- var callResult = Syscall.uname(out utsname);
- if (callResult == 0)
- {
- uname.sysname = utsname.sysname ?? string.Empty;
- uname.machine = utsname.machine ?? string.Empty;
- }
-
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error getting unix name", ex);
- }
- _unixName = uname;
- }
- return _unixName;
- }
-
- public class Uname
- {
- public string sysname = string.Empty;
- public string machine = string.Empty;
- }
-
- public FFMpegInstallInfo GetFfmpegInstallInfo()
- {
- return GetInfo(Environment);
- }
-
- public void LaunchUrl(string url)
- {
- throw new NotImplementedException();
- }
-
- public IDbConnector GetDbConnector()
- {
- return new DbConnector(Logger);
- }
-
- public static FFMpegInstallInfo GetInfo(NativeEnvironment environment)
- {
- var info = new FFMpegInstallInfo();
-
- // Windows builds: http://ffmpeg.zeranoe.com/builds/
- // Linux builds: http://johnvansickle.com/ffmpeg/
- // OS X builds: http://ffmpegmac.net/
- // OS X x64: http://www.evermeet.cx/ffmpeg/
-
- switch (environment.OperatingSystem)
- {
- case OperatingSystem.Osx:
- case OperatingSystem.Bsd:
- break;
- case OperatingSystem.Linux:
-
- info.ArchiveType = "7z";
- info.Version = "20160215";
- break;
- }
-
- // No version available - user requirement
- info.DownloadUrls = new string[] { };
-
- return info;
- }
-
- public void EnableLoopback(string appName)
- {
-
- }
- }
-}
diff --git a/MediaBrowser.Server.Mono/Native/DbConnector.cs b/MediaBrowser.Server.Mono/Native/DbConnector.cs
deleted file mode 100644
index 39b53b3709..0000000000
--- a/MediaBrowser.Server.Mono/Native/DbConnector.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System.Data;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Server.Implementations.Persistence;
-
-namespace MediaBrowser.Server.Mono.Native
-{
- public class DbConnector : IDbConnector
- {
- private readonly ILogger _logger;
-
- public DbConnector(ILogger logger)
- {
- _logger = logger;
- }
-
- public Task<IDbConnection> Connect(string dbPath, bool isReadOnly, bool enablePooling = false, int? cacheSize = null)
- {
- return SqliteExtensions.ConnectToDb(dbPath, isReadOnly, enablePooling, cacheSize, _logger);
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Mono/Native/MonoFileSystem.cs b/MediaBrowser.Server.Mono/Native/MonoFileSystem.cs
new file mode 100644
index 0000000000..3d6a3e35dc
--- /dev/null
+++ b/MediaBrowser.Server.Mono/Native/MonoFileSystem.cs
@@ -0,0 +1,22 @@
+using Emby.Common.Implementations.IO;
+using MediaBrowser.Model.Logging;
+using Mono.Unix.Native;
+
+namespace MediaBrowser.Server.Mono.Native
+{
+ public class MonoFileSystem : ManagedFileSystem
+ {
+ public MonoFileSystem(ILogger logger, bool supportsAsyncFileStreams, bool enableManagedInvalidFileNameChars)
+ : base(logger, supportsAsyncFileStreams, enableManagedInvalidFileNameChars, true)
+ {
+ }
+
+ public override void SetExecutable(string path)
+ {
+ // Linux: File permission to 666, and user's execute bit
+ Logger.Info("Syscall.chmod {0} FilePermissions.DEFFILEMODE | FilePermissions.S_IRWXU | FilePermissions.S_IXGRP | FilePermissions.S_IXOTH", path);
+
+ Syscall.chmod(path, FilePermissions.DEFFILEMODE | FilePermissions.S_IRWXU | FilePermissions.S_IXGRP | FilePermissions.S_IXOTH);
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Mono/Native/NativeApp.cs b/MediaBrowser.Server.Mono/Native/NativeApp.cs
deleted file mode 100644
index c0874a1d82..0000000000
--- a/MediaBrowser.Server.Mono/Native/NativeApp.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Server.Startup.Common;
-
-namespace MediaBrowser.Server.Mono.Native
-{
- /// <summary>
- /// Class NativeApp
- /// </summary>
- internal class NativeApp : BaseMonoApp
- {
- public NativeApp(StartupOptions startupOptions, ILogger logger)
- : base(startupOptions, logger)
- {
- }
-
- /// <summary>
- /// Shutdowns this instance.
- /// </summary>
- public override void Shutdown()
- {
- MainClass.Shutdown();
- }
-
- /// <summary>
- /// Determines whether this instance [can self restart].
- /// </summary>
- /// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value>
- public override bool CanSelfRestart
- {
- get
- {
- // A restart script must be provided
- return StartupOptions.ContainsOption("-restartpath");
- }
- }
-
- /// <summary>
- /// Restarts this instance.
- /// </summary>
- public override void Restart(StartupOptions startupOptions)
- {
- MainClass.Restart(startupOptions);
- }
- }
-}
diff --git a/MediaBrowser.Server.Mono/Native/PowerManagement.cs b/MediaBrowser.Server.Mono/Native/PowerManagement.cs
new file mode 100644
index 0000000000..0fdd4de808
--- /dev/null
+++ b/MediaBrowser.Server.Mono/Native/PowerManagement.cs
@@ -0,0 +1,15 @@
+using MediaBrowser.Model.System;
+
+namespace MediaBrowser.Server.Mono.Native
+{
+ public class PowerManagement : IPowerManagement
+ {
+ public void PreventSystemStandby()
+ {
+ }
+
+ public void AllowSystemStandby()
+ {
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Mono/Networking/NetworkManager.cs b/MediaBrowser.Server.Mono/Networking/NetworkManager.cs
deleted file mode 100644
index e8c623fd1e..0000000000
--- a/MediaBrowser.Server.Mono/Networking/NetworkManager.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using MediaBrowser.Common.Implementations.Networking;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Server.Mono.Networking
-{
- /// <summary>
- /// Class NetUtils
- /// </summary>
- public class NetworkManager : BaseNetworkManager, INetworkManager
- {
- public NetworkManager(ILogger logger)
- : base(logger)
- {
- }
-
- /// <summary>
- /// Gets the network shares.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>IEnumerable{NetworkShare}.</returns>
- public IEnumerable<NetworkShare> GetNetworkShares(string path)
- {
- return new List<NetworkShare>();
- }
-
- /// <summary>
- /// Gets available devices within the domain
- /// </summary>
- /// <returns>PC's in the Domain</returns>
- public IEnumerable<FileSystemEntryInfo> GetNetworkDevices()
- {
- return new List<FileSystemEntryInfo>();
- }
-
- /// <summary>
- /// Generates a self signed certificate at the locatation specified by <paramref name="certificatePath"/>.
- /// </summary>
- /// <param name="certificatePath">The path to generate the certificate.</param>
- /// <param name="hostname">The common name for the certificate.</param>
- public void GenerateSelfSignedSslCertificate(string certificatePath, string hostname)
- {
- CertificateGenerator.CreateSelfSignCertificatePfx(certificatePath, hostname, Logger);
- }
- }
-}
diff --git a/MediaBrowser.Server.Mono/Program.cs b/MediaBrowser.Server.Mono/Program.cs
index 32de452421..b3aeb2027b 100644
--- a/MediaBrowser.Server.Mono/Program.cs
+++ b/MediaBrowser.Server.Mono/Program.cs
@@ -1,20 +1,29 @@
-using MediaBrowser.Common.Implementations.Logging;
using MediaBrowser.Model.Logging;
-using MediaBrowser.Server.Implementations;
using MediaBrowser.Server.Mono.Native;
using MediaBrowser.Server.Startup.Common;
-using Microsoft.Win32;
using System;
using System.Diagnostics;
+using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Reflection;
-using System.Security.Cryptography.X509Certificates;
+using System.Text.RegularExpressions;
using System.Threading.Tasks;
-using CommonIO;
-using MediaBrowser.Server.Implementations.Logging;
+using Emby.Common.Implementations.EnvironmentInfo;
+using Emby.Common.Implementations.Logging;
+using Emby.Common.Implementations.Networking;
+using Emby.Common.Implementations.Security;
+using Emby.Server.Core;
+using Emby.Server.Implementations;
+using Emby.Server.Implementations.IO;
+using MediaBrowser.Model.System;
+using MediaBrowser.Server.Startup.Common.IO;
+using Mono.Unix.Native;
+using NLog;
+using ILogger = MediaBrowser.Model.Logging.ILogger;
+using X509Certificate = System.Security.Cryptography.X509Certificates.X509Certificate;
namespace MediaBrowser.Server.Mono
{
@@ -27,8 +36,12 @@ namespace MediaBrowser.Server.Mono
public static void Main(string[] args)
{
var applicationPath = Assembly.GetEntryAssembly().Location;
+ var appFolderPath = Path.GetDirectoryName(applicationPath);
- var options = new StartupOptions();
+ TryCopySqliteConfigFile(appFolderPath);
+ SetSqliteProvider();
+
+ var options = new StartupOptions(Environment.GetCommandLineArgs());
// Allow this to be specified on the command line.
var customProgramDataPath = options.GetOption("-programdata");
@@ -42,7 +55,7 @@ namespace MediaBrowser.Server.Mono
var logger = _logger = logManager.GetLogger("Main");
ApplicationHost.LogEnvironmentInfo(logger, appPaths, true);
-
+
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
try
@@ -57,6 +70,25 @@ namespace MediaBrowser.Server.Mono
}
}
+ private static void TryCopySqliteConfigFile(string appFolderPath)
+ {
+ try
+ {
+ File.Copy(Path.Combine(appFolderPath, "System.Data.SQLite.dll.config"),
+ Path.Combine(appFolderPath, "SQLitePCLRaw.provider.sqlite3.dll.config"),
+ true);
+ }
+ catch
+ {
+
+ }
+ }
+
+ private static void SetSqliteProvider()
+ {
+ SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_sqlite3());
+ }
+
private static ServerApplicationPaths CreateApplicationPaths(string applicationPath, string programDataPath)
{
if (string.IsNullOrEmpty(programDataPath))
@@ -64,24 +96,38 @@ namespace MediaBrowser.Server.Mono
programDataPath = ApplicationPathHelper.GetProgramDataPath(applicationPath);
}
- return new ServerApplicationPaths(programDataPath, applicationPath, Path.GetDirectoryName(applicationPath));
+ var appFolderPath = Path.GetDirectoryName(applicationPath);
+
+ return new ServerApplicationPaths(programDataPath, appFolderPath, Path.GetDirectoryName(applicationPath));
}
private static readonly TaskCompletionSource<bool> ApplicationTaskCompletionSource = new TaskCompletionSource<bool>();
private static void RunApplication(ServerApplicationPaths appPaths, ILogManager logManager, StartupOptions options)
{
- SystemEvents.SessionEnding += SystemEvents_SessionEnding;
-
// Allow all https requests
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; });
- var fileSystem = new ManagedFileSystem(new PatternsLogger(logManager.GetLogger("FileSystem")), false, false);
+ var fileSystem = new MonoFileSystem(logManager.GetLogger("FileSystem"), false, false);
fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
- var nativeApp = new NativeApp(options, logManager.GetLogger("App"));
+ var environmentInfo = GetEnvironmentInfo();
- _appHost = new ApplicationHost(appPaths, logManager, options, fileSystem, "emby.mono.zip", nativeApp);
+ var imageEncoder = ImageEncoderHelper.GetImageEncoder(_logger, logManager, fileSystem, options, () => _appHost.HttpClient, appPaths);
+
+ _appHost = new MonoAppHost(appPaths,
+ logManager,
+ options,
+ fileSystem,
+ new PowerManagement(),
+ "emby.mono.zip",
+ environmentInfo,
+ imageEncoder,
+ new Startup.Common.SystemEvents(logManager.GetLogger("SystemEvents")),
+ new MemoryStreamProvider(),
+ new NetworkManager(logManager.GetLogger("NetworkManager")),
+ GenerateCertificate,
+ () => Environment.UserName);
if (options.ContainsOption("-v"))
{
@@ -106,17 +152,90 @@ namespace MediaBrowser.Server.Mono
Task.WaitAll(task);
}
- /// <summary>
- /// Handles the SessionEnding event of the SystemEvents control.
- /// </summary>
- /// <param name="sender">The source of the event.</param>
- /// <param name="e">The <see cref="SessionEndingEventArgs"/> instance containing the event data.</param>
- static void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
+ private static void GenerateCertificate(string certPath, string certHost)
+ {
+ CertificateGenerator.CreateSelfSignCertificatePfx(certPath, certHost, _logger);
+ }
+
+ private static MonoEnvironmentInfo GetEnvironmentInfo()
{
- if (e.Reason == SessionEndReasons.SystemShutdown)
+ var info = new MonoEnvironmentInfo();
+
+ var uname = GetUnixName();
+
+ var sysName = uname.sysname ?? string.Empty;
+
+ if (string.Equals(sysName, "Darwin", StringComparison.OrdinalIgnoreCase))
+ {
+ //info.OperatingSystem = Startup.Common.OperatingSystem.Osx;
+ }
+ else if (string.Equals(sysName, "Linux", StringComparison.OrdinalIgnoreCase))
+ {
+ //info.OperatingSystem = Startup.Common.OperatingSystem.Linux;
+ }
+ else if (string.Equals(sysName, "BSD", StringComparison.OrdinalIgnoreCase))
{
- Shutdown();
+ //info.OperatingSystem = Startup.Common.OperatingSystem.Bsd;
+ info.IsBsd = true;
}
+
+ var archX86 = new Regex("(i|I)[3-6]86");
+
+ if (archX86.IsMatch(uname.machine))
+ {
+ info.CustomArchitecture = Architecture.X86;
+ }
+ else if (string.Equals(uname.machine, "x86_64", StringComparison.OrdinalIgnoreCase))
+ {
+ info.CustomArchitecture = Architecture.X64;
+ }
+ else if (uname.machine.StartsWith("arm", StringComparison.OrdinalIgnoreCase))
+ {
+ info.CustomArchitecture = Architecture.Arm;
+ }
+ else if (System.Environment.Is64BitOperatingSystem)
+ {
+ info.CustomArchitecture = Architecture.X64;
+ }
+ else
+ {
+ info.CustomArchitecture = Architecture.X86;
+ }
+
+ return info;
+ }
+
+ private static Uname _unixName;
+
+ private static Uname GetUnixName()
+ {
+ if (_unixName == null)
+ {
+ var uname = new Uname();
+ try
+ {
+ Utsname utsname;
+ var callResult = Syscall.uname(out utsname);
+ if (callResult == 0)
+ {
+ uname.sysname = utsname.sysname ?? string.Empty;
+ uname.machine = utsname.machine ?? string.Empty;
+ }
+
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error getting unix name", ex);
+ }
+ _unixName = uname;
+ }
+ return _unixName;
+ }
+
+ public class Uname
+ {
+ public string sysname = string.Empty;
+ public string machine = string.Empty;
}
/// <summary>
@@ -191,4 +310,14 @@ namespace MediaBrowser.Server.Mono
return true;
}
}
+
+ public class MonoEnvironmentInfo : EnvironmentInfo
+ {
+ public bool IsBsd { get; set; }
+
+ public override string GetUserId()
+ {
+ return Syscall.getuid().ToString(CultureInfo.InvariantCulture);
+ }
+ }
}
diff --git a/MediaBrowser.Server.Mono/SQLitePCLRaw.provider.sqlite3.dll.config b/MediaBrowser.Server.Mono/SQLitePCLRaw.provider.sqlite3.dll.config
new file mode 100644
index 0000000000..83a6cd9f38
--- /dev/null
+++ b/MediaBrowser.Server.Mono/SQLitePCLRaw.provider.sqlite3.dll.config
@@ -0,0 +1,3 @@
+<configuration>
+ <dllmap dll="sqlite3" target="libsqlite3.so" os="linux"/>
+</configuration> \ No newline at end of file
diff --git a/MediaBrowser.Server.Mono/app.config b/MediaBrowser.Server.Mono/app.config
index e14b908adc..a77a3a5063 100644
--- a/MediaBrowser.Server.Mono/app.config
+++ b/MediaBrowser.Server.Mono/app.config
@@ -1,21 +1,25 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
- <section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/>
+ <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="ProgramData-Server" />
+ <add key="ReleaseProgramDataPath" value="ProgramData-Server" />
</appSettings>
<runtime>
<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"/>
+ <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>
</assemblyBinding>
</runtime>
-<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1"/></startup></configuration>
+<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" /></startup></configuration>
diff --git a/MediaBrowser.Server.Mono/packages.config b/MediaBrowser.Server.Mono/packages.config
index 238025a788..465a05c083 100644
--- a/MediaBrowser.Server.Mono/packages.config
+++ b/MediaBrowser.Server.Mono/packages.config
@@ -1,6 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="CommonIO" version="1.0.0.9" targetFramework="net45" />
<package id="Mono.Posix" version="4.0.0.0" targetFramework="net45" />
- <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
+ <package id="NLog" version="4.4.0-betaV15" targetFramework="net46" />
+ <package id="ServiceStack.Text" version="4.5.4" targetFramework="net46" />
+ <package id="SharpCompress" version="0.14.0" targetFramework="net46" />
+ <package id="SimpleInjector" version="3.2.4" targetFramework="net46" />
+ <package id="SQLitePCLRaw.core" version="1.1.1" targetFramework="net46" />
+ <package id="SQLitePCLRaw.provider.sqlite3.net45" version="1.1.1" targetFramework="net46" />
</packages> \ No newline at end of file
diff --git a/MediaBrowser.Server.Mono/Security/ASN1.cs b/MediaBrowser.Server.Startup.Common/Cryptography/ASN1.cs
index 2fcbde7c16..a25c270734 100644
--- a/MediaBrowser.Server.Mono/Security/ASN1.cs
+++ b/MediaBrowser.Server.Startup.Common/Cryptography/ASN1.cs
@@ -34,7 +34,7 @@ using System.Collections;
using System.IO;
using System.Text;
-namespace MediaBrowser.Server.Mono.Security {
+namespace Emby.Common.Implementations.Security {
// References:
// a. ITU ASN.1 standards (free download)
diff --git a/MediaBrowser.Server.Mono/Security/ASN1Convert.cs b/MediaBrowser.Server.Startup.Common/Cryptography/ASN1Convert.cs
index f25a5275af..8a2a487c82 100644
--- a/MediaBrowser.Server.Mono/Security/ASN1Convert.cs
+++ b/MediaBrowser.Server.Startup.Common/Cryptography/ASN1Convert.cs
@@ -34,11 +34,12 @@ using System.Globalization;
using System.Security.Cryptography;
using System.Text;
-namespace MediaBrowser.Server.Mono.Security {
+namespace Emby.Common.Implementations.Security
+{
- // References:
- // a. ITU ASN.1 standards (free download)
- // http://www.itu.int/ITU-T/studygroups/com17/languages/
+ // References:
+ // a. ITU ASN.1 standards (free download)
+ // http://www.itu.int/ITU-T/studygroups/com17/languages/
public static class ASN1Convert {
// RFC3280, section 4.2.1.5
diff --git a/MediaBrowser.Server.Mono/Security/BitConverterLE.cs b/MediaBrowser.Server.Startup.Common/Cryptography/BitConverterLE.cs
index 29b6ee023b..240c958a3a 100644
--- a/MediaBrowser.Server.Mono/Security/BitConverterLE.cs
+++ b/MediaBrowser.Server.Startup.Common/Cryptography/BitConverterLE.cs
@@ -29,9 +29,9 @@
using System;
-namespace MediaBrowser.Server.Mono.Security
+namespace Emby.Common.Implementations.Security
{
- internal sealed class BitConverterLE
+ internal sealed class BitConverterLE
{
private BitConverterLE ()
{
diff --git a/MediaBrowser.Server.Mono/Networking/CertificateGenerator.cs b/MediaBrowser.Server.Startup.Common/Cryptography/CertificateGenerator.cs
index 58c5bba2d2..9e14b7713d 100644
--- a/MediaBrowser.Server.Mono/Networking/CertificateGenerator.cs
+++ b/MediaBrowser.Server.Startup.Common/Cryptography/CertificateGenerator.cs
@@ -2,15 +2,14 @@
using System;
using System.Collections;
using System.Security.Cryptography;
-using MediaBrowser.Server.Mono.Security;
-namespace MediaBrowser.Server.Mono.Networking
+namespace Emby.Common.Implementations.Security
{
- internal class CertificateGenerator
+ public class CertificateGenerator
{
private const string MonoTestRootAgency = "<RSAKeyValue><Modulus>v/4nALBxCE+9JgEC0LnDUvKh6e96PwTpN4Rj+vWnqKT7IAp1iK/JjuqvAg6DQ2vTfv0dTlqffmHH51OyioprcT5nzxcSTsZb/9jcHScG0s3/FRIWnXeLk/fgm7mSYhjUaHNI0m1/NTTktipicjKxo71hGIg9qucCWnDum+Krh/k=</Modulus><Exponent>AQAB</Exponent><P>9jbKxMXEruW2CfZrzhxtull4O8P47+mNsEL+9gf9QsRO1jJ77C+jmzfU6zbzjf8+ViK+q62tCMdC1ZzulwdpXQ==</P><Q>x5+p198l1PkK0Ga2mRh0SIYSykENpY2aLXoyZD/iUpKYAvATm0/wvKNrE4dKJyPCA+y3hfTdgVag+SP9avvDTQ==</Q><DP>ISSjCvXsUfbOGG05eddN1gXxL2pj+jegQRfjpk7RAsnWKvNExzhqd5x+ZuNQyc6QH5wxun54inP4RTUI0P/IaQ==</DP><DQ>R815VQmR3RIbPqzDXzv5j6CSH6fYlcTiQRtkBsUnzhWmkd/y3XmamO+a8zJFjOCCx9CcjpVuGziivBqi65lVPQ==</DQ><InverseQ>iYiu0KwMWI/dyqN3RJYUzuuLj02/oTD1pYpwo2rvNCXU1Q5VscOeu2DpNg1gWqI+1RrRCsEoaTNzXB1xtKNlSw==</InverseQ><D>nIfh1LYF8fjRBgMdAH/zt9UKHWiaCnc+jXzq5tkR8HVSKTVdzitD8bl1JgAfFQD8VjSXiCJqluexy/B5SGrCXQ49c78NIQj0hD+J13Y8/E0fUbW1QYbhj6Ff7oHyhaYe1WOQfkp2t/h+llHOdt1HRf7bt7dUknYp7m8bQKGxoYE=</D></RSAKeyValue>";
- internal static void CreateSelfSignCertificatePfx(
+ public static void CreateSelfSignCertificatePfx(
string fileName,
string hostname,
ILogger logger)
diff --git a/MediaBrowser.Server.Mono/Security/CryptoConvert.cs b/MediaBrowser.Server.Startup.Common/Cryptography/CryptoConvert.cs
index 62c28bd27e..c6e466534b 100644
--- a/MediaBrowser.Server.Mono/Security/CryptoConvert.cs
+++ b/MediaBrowser.Server.Startup.Common/Cryptography/CryptoConvert.cs
@@ -32,7 +32,8 @@ using System.Globalization;
using System.Security.Cryptography;
using System.Text;
-namespace MediaBrowser.Server.Mono.Security {
+namespace Emby.Common.Implementations.Security
+{
public sealed class CryptoConvert {
diff --git a/MediaBrowser.Server.Mono/Security/PKCS1.cs b/MediaBrowser.Server.Startup.Common/Cryptography/PKCS1.cs
index 86ed6f2d8f..85bf9db380 100644
--- a/MediaBrowser.Server.Mono/Security/PKCS1.cs
+++ b/MediaBrowser.Server.Startup.Common/Cryptography/PKCS1.cs
@@ -31,12 +31,13 @@
using System;
using System.Security.Cryptography;
-namespace MediaBrowser.Server.Mono.Security {
+namespace Emby.Common.Implementations.Security
+{
+
+ // References:
+ // a. PKCS#1: RSA Cryptography Standard
+ // http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/index.html
- // References:
- // a. PKCS#1: RSA Cryptography Standard
- // http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/index.html
-
public sealed class PKCS1 {
private PKCS1 ()
diff --git a/MediaBrowser.Server.Mono/Security/PKCS12.cs b/MediaBrowser.Server.Startup.Common/Cryptography/PKCS12.cs
index b5da09c630..2205a71604 100644
--- a/MediaBrowser.Server.Mono/Security/PKCS12.cs
+++ b/MediaBrowser.Server.Startup.Common/Cryptography/PKCS12.cs
@@ -37,7 +37,8 @@ using System.IO;
using System.Security.Cryptography;
using System.Text;
-namespace MediaBrowser.Server.Mono.Security {
+namespace Emby.Common.Implementations.Security
+{
public class PKCS5 {
diff --git a/MediaBrowser.Server.Mono/Security/PKCS7.cs b/MediaBrowser.Server.Startup.Common/Cryptography/PKCS7.cs
index 7a34580e9f..1b1a3295b4 100644
--- a/MediaBrowser.Server.Mono/Security/PKCS7.cs
+++ b/MediaBrowser.Server.Startup.Common/Cryptography/PKCS7.cs
@@ -33,7 +33,8 @@ using System;
using System.Collections;
using System.Security.Cryptography;
-namespace MediaBrowser.Server.Mono.Security {
+namespace Emby.Common.Implementations.Security
+{
public sealed class PKCS7 {
diff --git a/MediaBrowser.Server.Mono/Security/PKCS8.cs b/MediaBrowser.Server.Startup.Common/Cryptography/PKCS8.cs
index b2f28f3188..b58ebdaf99 100644
--- a/MediaBrowser.Server.Mono/Security/PKCS8.cs
+++ b/MediaBrowser.Server.Startup.Common/Cryptography/PKCS8.cs
@@ -33,7 +33,8 @@ using System;
using System.Collections;
using System.Security.Cryptography;
-namespace MediaBrowser.Server.Mono.Security {
+namespace Emby.Common.Implementations.Security
+{
public sealed class PKCS8 {
diff --git a/MediaBrowser.Server.Startup.Common/Cryptography/PfxGenerator.cs b/MediaBrowser.Server.Startup.Common/Cryptography/PfxGenerator.cs
new file mode 100644
index 0000000000..3f9b90ac16
--- /dev/null
+++ b/MediaBrowser.Server.Startup.Common/Cryptography/PfxGenerator.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Collections;
+using System.Security.Cryptography;
+
+namespace Emby.Common.Implementations.Security
+{
+ public class PFXGenerator
+ {
+ // http://www.freekpaans.nl/2015/04/creating-self-signed-x-509-certificates-using-mono-security/
+ public static byte[] GeneratePfx(string certificateName, string password)
+ {
+ byte[] sn = GenerateSerialNumber();
+ string subject = string.Format("CN={0}", certificateName);
+
+ DateTime notBefore = DateTime.Now;
+ DateTime notAfter = DateTime.Now.AddYears(20);
+
+ RSA subjectKey = new RSACryptoServiceProvider(2048);
+
+
+ string hashName = "SHA256";
+
+ X509CertificateBuilder cb = new X509CertificateBuilder(3);
+ cb.SerialNumber = sn;
+ cb.IssuerName = subject;
+ cb.NotBefore = notBefore;
+ cb.NotAfter = notAfter;
+ cb.SubjectName = subject;
+ cb.SubjectPublicKey = subjectKey;
+ cb.Hash = hashName;
+
+ byte[] rawcert = cb.Sign(subjectKey);
+
+
+ PKCS12 p12 = new PKCS12();
+ p12.Password = password;
+
+ Hashtable attributes = GetAttributes();
+
+ p12.AddCertificate(new X509Certificate(rawcert), attributes);
+ p12.AddPkcs8ShroudedKeyBag(subjectKey, attributes);
+
+ return p12.GetBytes();
+ }
+
+ private static Hashtable GetAttributes()
+ {
+ ArrayList list = new ArrayList();
+ // we use a fixed array to avoid endianess issues
+ // (in case some tools requires the ID to be 1).
+ list.Add(new byte[4] { 1, 0, 0, 0 });
+ Hashtable attributes = new Hashtable(1);
+ attributes.Add(PKCS9.localKeyId, list);
+ return attributes;
+ }
+
+ private static byte[] GenerateSerialNumber()
+ {
+ byte[] sn = Guid.NewGuid().ToByteArray();
+
+ //must be positive
+ if ((sn[0] & 0x80) == 0x80)
+ sn[0] -= 0x80;
+ return sn;
+ }
+
+ public static byte[] GetCertificateForBytes(byte[] pfx, string password)
+ {
+ var pkcs = new PKCS12(pfx, password);
+ var cert = pkcs.GetCertificate(GetAttributes());
+
+ return cert.RawData;
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Mono/Security/X501Name.cs b/MediaBrowser.Server.Startup.Common/Cryptography/X501Name.cs
index d77158e450..93e1a6bef4 100644
--- a/MediaBrowser.Server.Mono/Security/X501Name.cs
+++ b/MediaBrowser.Server.Startup.Common/Cryptography/X501Name.cs
@@ -31,15 +31,16 @@ using System;
using System.Globalization;
using System.Text;
-namespace MediaBrowser.Server.Mono.Security {
+namespace Emby.Common.Implementations.Security
+{
- // References:
- // 1. Information technology - Open Systems Interconnection - The Directory: Models
- // http://www.itu.int/rec/recommendation.asp?type=items&lang=e&parent=T-REC-X.501-200102-I
- // 2. RFC2253: Lightweight Directory Access Protocol (v3): UTF-8 String Representation of Distinguished Names
- // http://www.ietf.org/rfc/rfc2253.txt
+ // References:
+ // 1. Information technology - Open Systems Interconnection - The Directory: Models
+ // http://www.itu.int/rec/recommendation.asp?type=items&lang=e&parent=T-REC-X.501-200102-I
+ // 2. RFC2253: Lightweight Directory Access Protocol (v3): UTF-8 String Representation of Distinguished Names
+ // http://www.ietf.org/rfc/rfc2253.txt
- /*
+ /*
* Name ::= CHOICE { RDNSequence }
*
* RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
diff --git a/MediaBrowser.Server.Mono/Security/X509Builder.cs b/MediaBrowser.Server.Startup.Common/Cryptography/X509Builder.cs
index 4801f3d8a2..e168559591 100644
--- a/MediaBrowser.Server.Mono/Security/X509Builder.cs
+++ b/MediaBrowser.Server.Startup.Common/Cryptography/X509Builder.cs
@@ -33,9 +33,10 @@ using System;
using System.Globalization;
using System.Security.Cryptography;
-namespace MediaBrowser.Server.Mono.Security {
+namespace Emby.Common.Implementations.Security
+{
- public abstract class X509Builder {
+ public abstract class X509Builder {
private const string defaultHash = "SHA1";
private string hashName;
diff --git a/MediaBrowser.Server.Mono/Security/X509Certificate.cs b/MediaBrowser.Server.Startup.Common/Cryptography/X509Certificate.cs
index fa817d9597..f49445f0e7 100644
--- a/MediaBrowser.Server.Mono/Security/X509Certificate.cs
+++ b/MediaBrowser.Server.Startup.Common/Cryptography/X509Certificate.cs
@@ -34,7 +34,8 @@ using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
-namespace MediaBrowser.Server.Mono.Security {
+namespace Emby.Common.Implementations.Security
+{
// References:
// a. Internet X.509 Public Key Infrastructure Certificate and CRL Profile
diff --git a/MediaBrowser.Server.Mono/Security/X509CertificateBuilder.cs b/MediaBrowser.Server.Startup.Common/Cryptography/X509CertificateBuilder.cs
index 9b51d9e4ec..fb6f8ec727 100644
--- a/MediaBrowser.Server.Mono/Security/X509CertificateBuilder.cs
+++ b/MediaBrowser.Server.Startup.Common/Cryptography/X509CertificateBuilder.cs
@@ -32,9 +32,10 @@
using System;
using System.Security.Cryptography;
-namespace MediaBrowser.Server.Mono.Security {
- // From RFC3280
- /*
+namespace Emby.Common.Implementations.Security
+{
+ // From RFC3280
+ /*
* Certificate ::= SEQUENCE {
* tbsCertificate TBSCertificate,
* signatureAlgorithm AlgorithmIdentifier,
@@ -66,7 +67,7 @@ namespace MediaBrowser.Server.Mono.Security {
* generalTime GeneralizedTime
* }
*/
- public class X509CertificateBuilder : X509Builder {
+ public class X509CertificateBuilder : X509Builder {
private byte version;
private byte[] sn;
diff --git a/MediaBrowser.Server.Mono/Security/X509CertificateCollection.cs b/MediaBrowser.Server.Startup.Common/Cryptography/X509CertificateCollection.cs
index 6bb465b283..5d353f9cfd 100644
--- a/MediaBrowser.Server.Mono/Security/X509CertificateCollection.cs
+++ b/MediaBrowser.Server.Startup.Common/Cryptography/X509CertificateCollection.cs
@@ -31,9 +31,10 @@
using System;
using System.Collections;
-namespace MediaBrowser.Server.Mono.Security {
+namespace Emby.Common.Implementations.Security
+{
- [Serializable]
+ [Serializable]
public class X509CertificateCollection : CollectionBase, IEnumerable {
public X509CertificateCollection ()
diff --git a/MediaBrowser.Server.Mono/Security/X509Extension.cs b/MediaBrowser.Server.Startup.Common/Cryptography/X509Extension.cs
index 984c3542b4..e82f52bac7 100644
--- a/MediaBrowser.Server.Mono/Security/X509Extension.cs
+++ b/MediaBrowser.Server.Startup.Common/Cryptography/X509Extension.cs
@@ -31,8 +31,9 @@ using System;
using System.Globalization;
using System.Text;
-namespace MediaBrowser.Server.Mono.Security {
- /*
+namespace Emby.Common.Implementations.Security
+{
+ /*
* Extension ::= SEQUENCE {
* extnID OBJECT IDENTIFIER,
* critical BOOLEAN DEFAULT FALSE,
diff --git a/MediaBrowser.Server.Mono/Security/X509Extensions.cs b/MediaBrowser.Server.Startup.Common/Cryptography/X509Extensions.cs
index b86fe1c401..c7d5f0046e 100644
--- a/MediaBrowser.Server.Mono/Security/X509Extensions.cs
+++ b/MediaBrowser.Server.Startup.Common/Cryptography/X509Extensions.cs
@@ -32,8 +32,9 @@
using System;
using System.Collections;
-namespace MediaBrowser.Server.Mono.Security {
- /*
+namespace Emby.Common.Implementations.Security
+{
+ /*
* Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
*
* Note: 1..MAX -> There shouldn't be 0 Extensions in the ASN1 structure
diff --git a/MediaBrowser.Server.Mono/Security/X520Attributes.cs b/MediaBrowser.Server.Startup.Common/Cryptography/X520Attributes.cs
index a61d31ad9f..5062bf80ad 100644
--- a/MediaBrowser.Server.Mono/Security/X520Attributes.cs
+++ b/MediaBrowser.Server.Startup.Common/Cryptography/X520Attributes.cs
@@ -30,19 +30,20 @@
using System;
using System.Text;
-namespace MediaBrowser.Server.Mono.Security {
-
- // References:
- // 1. Information technology - Open Systems Interconnection - The Directory: Selected attribute types
- // http://www.itu.int/rec/recommendation.asp?type=folders&lang=e&parent=T-REC-X.520
- // 2. Internet X.509 Public Key Infrastructure Certificate and CRL Profile
- // http://www.ietf.org/rfc/rfc3280.txt
- // 3. A Summary of the X.500(96) User Schema for use with LDAPv3
- // http://www.faqs.org/rfcs/rfc2256.html
- // 4. RFC 2247 - Using Domains in LDAP/X.500 Distinguished Names
- // http://www.faqs.org/rfcs/rfc2247.html
-
- /*
+namespace Emby.Common.Implementations.Security
+{
+
+ // References:
+ // 1. Information technology - Open Systems Interconnection - The Directory: Selected attribute types
+ // http://www.itu.int/rec/recommendation.asp?type=folders&lang=e&parent=T-REC-X.520
+ // 2. Internet X.509 Public Key Infrastructure Certificate and CRL Profile
+ // http://www.ietf.org/rfc/rfc3280.txt
+ // 3. A Summary of the X.500(96) User Schema for use with LDAPv3
+ // http://www.faqs.org/rfcs/rfc2256.html
+ // 4. RFC 2247 - Using Domains in LDAP/X.500 Distinguished Names
+ // http://www.faqs.org/rfcs/rfc2247.html
+
+ /*
* AttributeTypeAndValue ::= SEQUENCE {
* type AttributeType,
* value AttributeValue
diff --git a/MediaBrowser.Server.Startup.Common/EntryPoints/KeepServerAwake.cs b/MediaBrowser.Server.Startup.Common/EntryPoints/KeepServerAwake.cs
deleted file mode 100644
index dbfd6f4e88..0000000000
--- a/MediaBrowser.Server.Startup.Common/EntryPoints/KeepServerAwake.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Linq;
-using MediaBrowser.Common.Threading;
-
-namespace MediaBrowser.Server.Startup.Common.EntryPoints
-{
- public class KeepServerAwake : IServerEntryPoint
- {
- private readonly ISessionManager _sessionManager;
- private readonly ILogger _logger;
- private PeriodicTimer _timer;
- private readonly IServerApplicationHost _appHost;
-
- public KeepServerAwake(ISessionManager sessionManager, ILogger logger, IServerApplicationHost appHost)
- {
- _sessionManager = sessionManager;
- _logger = logger;
- _appHost = appHost;
- }
-
- public void Run()
- {
- _timer = new PeriodicTimer(obj =>
- {
- var now = DateTime.UtcNow;
- var nativeApp = ((ApplicationHost)_appHost).NativeApp;
-
- try
- {
- if (_sessionManager.Sessions.Any(i => (now - i.LastActivityDate).TotalMinutes < 15))
- {
- nativeApp.PreventSystemStandby();
- }
- else
- {
- nativeApp.AllowSystemStandby();
- }
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error resetting system standby timer", ex);
- }
-
- }, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
- }
-
- public void Dispose()
- {
- if (_timer != null)
- {
- _timer.Dispose();
- _timer = null;
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Startup.Common/INativeApp.cs b/MediaBrowser.Server.Startup.Common/INativeApp.cs
deleted file mode 100644
index c56bb9b4bc..0000000000
--- a/MediaBrowser.Server.Startup.Common/INativeApp.cs
+++ /dev/null
@@ -1,106 +0,0 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.Logging;
-using System.Collections.Generic;
-using System.Reflection;
-using MediaBrowser.Server.Implementations.Persistence;
-using MediaBrowser.Server.Startup.Common.FFMpeg;
-
-namespace MediaBrowser.Server.Startup.Common
-{
- public interface INativeApp
- {
- /// <summary>
- /// Gets the assemblies with parts.
- /// </summary>
- /// <returns>List&lt;Assembly&gt;.</returns>
- List<Assembly> GetAssembliesWithParts();
-
- /// <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 temporary directory.</param>
- void AuthorizeServer(int udpPort, int httpServerPort, int httpsServerPort, string applicationPath, string tempDirectory);
-
- /// <summary>
- /// Gets the environment.
- /// </summary>
- /// <value>The environment.</value>
- NativeEnvironment Environment { get; }
-
- /// <summary>
- /// Gets a value indicating whether [supports running as service].
- /// </summary>
- /// <value><c>true</c> if [supports running as service]; otherwise, <c>false</c>.</value>
- bool SupportsRunningAsService { get; }
-
- /// <summary>
- /// Gets a value indicating whether this instance is running as service.
- /// </summary>
- /// <value><c>true</c> if this instance is running as service; otherwise, <c>false</c>.</value>
- bool IsRunningAsService { 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>
- /// Gets a value indicating whether [supports autorun at startup].
- /// </summary>
- /// <value><c>true</c> if [supports autorun at startup]; otherwise, <c>false</c>.</value>
- bool SupportsAutoRunAtStartup { get; }
-
- /// <summary>
- /// Gets a value indicating whether [supports library monitor].
- /// </summary>
- /// <value><c>true</c> if [supports library monitor]; otherwise, <c>false</c>.</value>
- bool SupportsLibraryMonitor { get; }
-
- /// <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>
- bool CanSelfUpdate { get; }
-
- /// <summary>
- /// Shutdowns this instance.
- /// </summary>
- void Shutdown();
-
- /// <summary>
- /// Restarts this instance.
- /// </summary>
- void Restart(StartupOptions startupOptions);
-
- /// <summary>
- /// Configures the automatic run.
- /// </summary>
- /// <param name="autorun">if set to <c>true</c> [autorun].</param>
- void ConfigureAutoRun(bool autorun);
-
- /// <summary>
- /// Gets the network manager.
- /// </summary>
- /// <returns>INetworkManager.</returns>
- INetworkManager CreateNetworkManager(ILogger logger);
-
- /// <summary>
- /// Prevents the system stand by.
- /// </summary>
- void PreventSystemStandby();
-
- void AllowSystemStandby();
-
- FFMpegInstallInfo GetFfmpegInstallInfo();
-
- void LaunchUrl(string url);
-
- IDbConnector GetDbConnector();
-
- void EnableLoopback(string appName);
- }
-}
diff --git a/MediaBrowser.Common.Implementations/IO/MemoryStreamProvider.cs b/MediaBrowser.Server.Startup.Common/IO/MemoryStreamProvider.cs
index c429474816..3ca0f4db5a 100644
--- a/MediaBrowser.Common.Implementations/IO/MemoryStreamProvider.cs
+++ b/MediaBrowser.Server.Startup.Common/IO/MemoryStreamProvider.cs
@@ -1,10 +1,10 @@
using System.IO;
-using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using Microsoft.IO;
-namespace MediaBrowser.Common.Implementations.IO
+namespace MediaBrowser.Server.Startup.Common.IO
{
- public class RecyclableMemoryStreamProvider : IMemoryStreamProvider
+ public class RecyclableMemoryStreamProvider : IMemoryStreamFactory
{
readonly RecyclableMemoryStreamManager _manager = new RecyclableMemoryStreamManager();
@@ -22,9 +22,15 @@ namespace MediaBrowser.Common.Implementations.IO
{
return _manager.GetStream("RecyclableMemoryStream", buffer, 0, buffer.Length);
}
+
+ public bool TryGetBuffer(MemoryStream stream, out byte[] buffer)
+ {
+ buffer = stream.GetBuffer();
+ return true;
+ }
}
- public class MemoryStreamProvider : IMemoryStreamProvider
+ public class MemoryStreamProvider : IMemoryStreamFactory
{
public MemoryStream CreateNew()
{
@@ -40,5 +46,11 @@ namespace MediaBrowser.Common.Implementations.IO
{
return new MemoryStream(buffer);
}
+
+ public bool TryGetBuffer(MemoryStream stream, out byte[] buffer)
+ {
+ buffer = stream.GetBuffer();
+ return true;
+ }
}
}
diff --git a/MediaBrowser.Server.Startup.Common/ImageEncoderHelper.cs b/MediaBrowser.Server.Startup.Common/ImageEncoderHelper.cs
new file mode 100644
index 0000000000..ddbde2f666
--- /dev/null
+++ b/MediaBrowser.Server.Startup.Common/ImageEncoderHelper.cs
@@ -0,0 +1,48 @@
+using System;
+using Emby.Drawing;
+using Emby.Drawing.Net;
+using Emby.Drawing.ImageMagick;
+using Emby.Server.Core;
+using Emby.Server.Implementations;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+
+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)
+ {
+ if (!startupOptions.ContainsOption("-enablegdi"))
+ {
+ try
+ {
+ return new ImageMagickEncoder(logManager.GetLogger("ImageMagick"), appPaths, httpClient, fileSystem);
+ }
+ catch
+ {
+ logger.Error("Error loading ImageMagick. Will revert to GDI.");
+ }
+ }
+
+ try
+ {
+ return new GDIImageEncoder(fileSystem, logManager.GetLogger("GDI"));
+ }
+ catch
+ {
+ logger.Error("Error loading GDI. Will revert to NullImageEncoder.");
+ }
+
+ return new NullImageEncoder();
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ChannelScan.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ChannelScan.cs
index fdeae25b0e..fdeae25b0e 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ChannelScan.cs
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ChannelScan.cs
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/ReportBlock.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/ReportBlock.cs
index dddd771790..dddd771790 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/ReportBlock.cs
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/ReportBlock.cs
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpAppPacket.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/RtcpAppPacket.cs
index 990b6dd949..990b6dd949 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpAppPacket.cs
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/RtcpAppPacket.cs
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpByePacket.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/RtcpByePacket.cs
index c79ea31a89..c79ea31a89 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpByePacket.cs
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/RtcpByePacket.cs
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpListener.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/RtcpListener.cs
index 2c54f06654..2c54f06654 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpListener.cs
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/RtcpListener.cs
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpPacket.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/RtcpPacket.cs
index 0a949eb7ed..0a949eb7ed 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpPacket.cs
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/RtcpPacket.cs
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpReceiverReportPacket.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/RtcpReceiverReportPacket.cs
index abb8636522..abb8636522 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpReceiverReportPacket.cs
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/RtcpReceiverReportPacket.cs
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSenderReportPacket.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSenderReportPacket.cs
index dda5d6a033..dda5d6a033 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSenderReportPacket.cs
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSenderReportPacket.cs
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSourceDescriptionPacket.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSourceDescriptionPacket.cs
index 0a95a44133..0a95a44133 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSourceDescriptionPacket.cs
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/RtcpSourceDescriptionPacket.cs
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionBlock.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionBlock.cs
index bf56087cd8..bf56087cd8 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionBlock.cs
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionBlock.cs
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionItem.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionItem.cs
index 5dd0336421..5dd0336421 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionItem.cs
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtcp/SourceDescriptionItem.cs
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtp/RtpListener.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtp/RtpListener.cs
index ea6a9ba6aa..ea6a9ba6aa 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtp/RtpListener.cs
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtp/RtpListener.cs
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtp/RtpPacket.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtp/RtpPacket.cs
index 489d7f087c..489d7f087c 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtp/RtpPacket.cs
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtp/RtpPacket.cs
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspMethod.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtsp/RtspMethod.cs
index 5f286f1db5..5f286f1db5 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspMethod.cs
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtsp/RtspMethod.cs
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspRequest.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtsp/RtspRequest.cs
index 600eda02da..600eda02da 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspRequest.cs
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtsp/RtspRequest.cs
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspResponse.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtsp/RtspResponse.cs
index 97290623b9..97290623b9 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspResponse.cs
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtsp/RtspResponse.cs
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspSession.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtsp/RtspSession.cs
index 0f8682b7cc..0f8682b7cc 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspSession.cs
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtsp/RtspSession.cs
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspStatusCode.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtsp/RtspStatusCode.cs
index 6d6d50623b..6d6d50623b 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Rtsp/RtspStatusCode.cs
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Rtsp/RtspStatusCode.cs
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs
index a0b8ef5f79..8ecdca46b3 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs
@@ -14,6 +14,7 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Extensions;
using System.Xml.Linq;
+using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Events;
namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/SatIpHost.cs
index 1fe767e521..55101ce10f 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/SatIpHost.cs
@@ -4,12 +4,15 @@ using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using Emby.Server.Implementations.LiveTv.TunerHosts;
+using MediaBrowser.Model.IO;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dto;
@@ -18,7 +21,6 @@ using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Server.Implementations.LiveTv.EmbyTV;
namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp
{
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/TransmissionMode.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/TransmissionMode.cs
index 71d7656d95..71d7656d95 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/TransmissionMode.cs
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/TransmissionMode.cs
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Utils.cs b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Utils.cs
index 3595e4b0ad..3595e4b0ad 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/Utils.cs
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/Utils.cs
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0030.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0030.ini
index 1caa948cf6..1caa948cf6 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0030.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0030.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0049.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0049.ini
index 92a0e7dda5..92a0e7dda5 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0049.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0049.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0070.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0070.ini
index 800b097c89..800b097c89 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0070.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0070.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0090.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0090.ini
index 6202569d99..6202569d99 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0090.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0090.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0100.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0100.ini
index 0614ba88c6..0614ba88c6 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0100.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0100.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0130.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0130.ini
index 265104298f..265104298f 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0130.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0130.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0160.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0160.ini
index 9a9503eb58..9a9503eb58 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0160.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0160.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0170.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0170.ini
index 52ba9e5f70..52ba9e5f70 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0170.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0170.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0192.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0192.ini
index fbe65c9b5d..fbe65c9b5d 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0192.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0192.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0200.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0200.ini
index 6eb757f162..6eb757f162 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0200.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0200.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0215.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0215.ini
index 30f3d5c6e9..30f3d5c6e9 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0215.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0215.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0235.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0235.ini
index b1abb39c66..b1abb39c66 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0235.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0235.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0255.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0255.ini
index f72c91b41a..f72c91b41a 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0255.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0255.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0260.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0260.ini
index 299779fb54..299779fb54 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0260.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0260.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0282.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0282.ini
index 07c5ccebe7..07c5ccebe7 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0282.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0282.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0305.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0305.ini
index 196196c956..196196c956 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0305.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0305.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0308.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0308.ini
index fe4f41569b..fe4f41569b 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0308.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0308.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0310.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0310.ini
index be556fdd2b..be556fdd2b 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0310.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0310.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0315.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0315.ini
index 6581c91691..6581c91691 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0315.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0315.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0330.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0330.ini
index b395913cc6..b395913cc6 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0330.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0330.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0360.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0360.ini
index 49042dfb7c..49042dfb7c 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0360.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0360.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0380.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0380.ini
index 25b53cd3d8..25b53cd3d8 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0380.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0380.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0390.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0390.ini
index e779197537..e779197537 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0390.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0390.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0400.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0400.ini
index c1a56c8458..c1a56c8458 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0400.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0400.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0420.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0420.ini
index 3d884a86bd..3d884a86bd 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0420.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0420.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0435.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0435.ini
index a7e5fdb799..a7e5fdb799 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0435.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0435.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0450.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0450.ini
index 0737fb4f18..0737fb4f18 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0450.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0450.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0460.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0460.ini
index 7611715ec8..7611715ec8 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0460.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0460.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0475.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0475.ini
index bc671123fe..bc671123fe 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0475.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0475.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0480.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0480.ini
index fa955de652..fa955de652 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0480.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0480.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0490.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0490.ini
index a85ad11b97..a85ad11b97 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0490.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0490.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0505.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0505.ini
index 5bbfe62977..5bbfe62977 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0505.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0505.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0510.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0510.ini
index 355d0e5b5a..355d0e5b5a 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0510.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0510.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0520.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0520.ini
index 8631604aa7..8631604aa7 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0520.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0520.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0525.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0525.ini
index 2eb001a81f..2eb001a81f 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0525.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0525.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0530.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0530.ini
index 965c41f150..965c41f150 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0530.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0530.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0549.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0549.ini
index b2fdf5a5c8..b2fdf5a5c8 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0549.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0549.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0560.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0560.ini
index bed5b87b5a..bed5b87b5a 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0560.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0560.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0570.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0570.ini
index 27df363114..27df363114 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0570.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0570.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0600.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0600.ini
index 1a711cf263..1a711cf263 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0600.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0600.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0620.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0620.ini
index ae5cf34bf1..ae5cf34bf1 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0620.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0620.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0642.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0642.ini
index 324d99e83c..324d99e83c 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0642.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0642.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0650.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0650.ini
index 81021a0b21..81021a0b21 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0650.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0650.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0660.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0660.ini
index a118dfa389..a118dfa389 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0660.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0660.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0685.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0685.ini
index 7c2cab6818..7c2cab6818 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0685.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0685.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0705.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0705.ini
index 0af8237c99..0af8237c99 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0705.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0705.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0721.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0721.ini
index c25a6ecf5c..c25a6ecf5c 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0721.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0721.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0740.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0740.ini
index 91356547c5..91356547c5 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0740.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0740.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0750.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0750.ini
index 8cdc075909..8cdc075909 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0750.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0750.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0765.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0765.ini
index 1a6195f9ae..1a6195f9ae 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0765.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0765.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0785.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0785.ini
index 8dcf19d376..8dcf19d376 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0785.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0785.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0830.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0830.ini
index ac7588e57d..ac7588e57d 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0830.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0830.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0851.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0851.ini
index faefe54dc7..faefe54dc7 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0851.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0851.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0865.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0865.ini
index f5a946f000..f5a946f000 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0865.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0865.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0875.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0875.ini
index 3f856ba713..3f856ba713 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0875.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0875.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0880.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0880.ini
index a2716d7625..a2716d7625 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0880.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0880.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0900.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0900.ini
index d423f69b64..d423f69b64 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0900.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0900.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0915.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0915.ini
index 3c28ff2540..3c28ff2540 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0915.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0915.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0922.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0922.ini
index 1aaa166417..1aaa166417 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0922.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0922.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0935.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0935.ini
index e47df32779..e47df32779 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0935.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0935.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0950.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0950.ini
index 1b6ba4775e..1b6ba4775e 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0950.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0950.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0965.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0965.ini
index 45d28a1fe6..45d28a1fe6 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/0965.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/0965.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1005.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1005.ini
index fe9374bcf0..fe9374bcf0 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1005.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1005.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1030.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1030.ini
index f4607ef89f..f4607ef89f 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1030.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1030.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1055.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1055.ini
index 79c54d9d81..79c54d9d81 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1055.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1055.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1082.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1082.ini
index a53b0c2e8b..a53b0c2e8b 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1082.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1082.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1100.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1100.ini
index 789b56cca7..789b56cca7 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1100.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1100.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1105.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1105.ini
index aafe004557..aafe004557 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1105.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1105.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1130.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1130.ini
index d9c69d3f73..d9c69d3f73 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1130.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1130.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1155.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1155.ini
index 4bfa662708..4bfa662708 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1155.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1155.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1160.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1160.ini
index 3ef951d9a5..3ef951d9a5 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1160.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1160.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1180.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1180.ini
index d41e3f5ad2..d41e3f5ad2 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1180.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1180.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1195.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1195.ini
index 42af4d1c98..42af4d1c98 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1195.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1195.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1222.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1222.ini
index 4d833f21bb..4d833f21bb 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1222.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1222.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1240.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1240.ini
index ed948c78ea..ed948c78ea 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1240.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1240.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1250.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1250.ini
index 738a7debe7..738a7debe7 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1250.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1250.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1280.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1280.ini
index bb2f556c08..bb2f556c08 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1280.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1280.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1320.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1320.ini
index 4694425d39..4694425d39 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1320.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1320.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1340.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1340.ini
index 4ca7c83a01..4ca7c83a01 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1340.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1340.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1380.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1380.ini
index cb952425b2..cb952425b2 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1380.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1380.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1400.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1400.ini
index b4e6dc7c0c..b4e6dc7c0c 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1400.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1400.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1440.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1440.ini
index e31e028abf..e31e028abf 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1440.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1440.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1500.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1500.ini
index 6acfb16798..6acfb16798 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1500.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1500.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1520.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1520.ini
index 0034b3e138..0034b3e138 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1520.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1520.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1540.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1540.ini
index 365575a8ce..365575a8ce 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1540.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1540.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1560.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1560.ini
index be5b9c2f69..be5b9c2f69 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1560.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1560.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1590.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1590.ini
index 567b3cc09e..567b3cc09e 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1590.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1590.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1600 OPTUS D1 FTA (160.0E).ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1600 OPTUS D1 FTA (160.0E).ini
index 8650617397..8650617397 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1600 OPTUS D1 FTA (160.0E).ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1600 OPTUS D1 FTA (160.0E).ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1600.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1600.ini
index 08083f62f2..08083f62f2 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1600.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1600.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1620.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1620.ini
index 9a16504dc5..9a16504dc5 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1620.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1620.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1640.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1640.ini
index 9650ab47a0..9650ab47a0 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1640.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1640.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1660.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1660.ini
index 8c040e8841..8c040e8841 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1660.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1660.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1690.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1690.ini
index c7470a5e4a..c7470a5e4a 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1690.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1690.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1720.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1720.ini
index 6f0a6f0ea1..6f0a6f0ea1 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1720.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1720.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1800.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1800.ini
index a3f5044cfb..a3f5044cfb 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1800.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1800.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1830.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1830.ini
index 8775bb5d3e..8775bb5d3e 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/1830.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/1830.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2210.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2210.ini
index 123055dd4c..123055dd4c 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2210.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2210.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2230.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2230.ini
index fe661385bb..fe661385bb 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2230.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2230.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2250.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2250.ini
index 4f5a9494da..4f5a9494da 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2250.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2250.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2270.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2270.ini
index 18a777c4b7..18a777c4b7 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2270.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2270.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2290.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2290.ini
index 631af13d23..631af13d23 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2290.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2290.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2310.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2310.ini
index 5915c23650..5915c23650 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2310.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2310.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2330.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2330.ini
index 3ab17e6a1f..3ab17e6a1f 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2330.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2330.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2350.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2350.ini
index cdaa5042ea..cdaa5042ea 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2350.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2350.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2370.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2370.ini
index ce1a1f239f..ce1a1f239f 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2370.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2370.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2390.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2390.ini
index 73ca36a7b6..73ca36a7b6 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2390.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2390.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2410.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2410.ini
index 188507e9dd..188507e9dd 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2410.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2410.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2432.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2432.ini
index aacb92c284..aacb92c284 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2432.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2432.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2451.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2451.ini
index 0272503f2e..0272503f2e 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2451.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2451.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2470.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2470.ini
index 0042e0ef12..0042e0ef12 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2470.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2470.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2489.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2489.ini
index c71cfdd790..c71cfdd790 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2489.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2489.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2500.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2500.ini
index 0fb566b488..0fb566b488 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2500.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2500.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2527.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2527.ini
index 41b4e2981c..41b4e2981c 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2527.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2527.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2550.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2550.ini
index d573433b8b..d573433b8b 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2550.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2550.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2570.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2570.ini
index 1fcd356437..1fcd356437 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2570.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2570.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2590.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2590.ini
index 322d683c18..322d683c18 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2590.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2590.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2608.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2608.ini
index e3200b7cdf..e3200b7cdf 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2608.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2608.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2630.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2630.ini
index 1a888553ad..1a888553ad 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2630.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2630.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2650.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2650.ini
index f3c4e0de6f..f3c4e0de6f 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2650.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2650.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2669.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2669.ini
index ccb7e85319..ccb7e85319 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2669.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2669.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2690.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2690.ini
index e3c73e65c4..e3c73e65c4 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2690.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2690.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2710.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2710.ini
index 8e5aa26881..8e5aa26881 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2710.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2710.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2728.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2728.ini
index 9b148a0460..9b148a0460 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2728.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2728.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2730.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2730.ini
index fdb17c5c09..fdb17c5c09 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2730.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2730.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2750.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2750.ini
index 8f2358d56e..8f2358d56e 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2750.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2750.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2760.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2760.ini
index 253d2cced0..253d2cced0 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2760.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2760.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2770.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2770.ini
index 12cbcc729e..12cbcc729e 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2770.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2770.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2780.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2780.ini
index 7080fc519a..7080fc519a 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2780.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2780.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2812.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2812.ini
index 61b29bb930..61b29bb930 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2812.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2812.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2820.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2820.ini
index 0aa5cc903c..0aa5cc903c 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2820.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2820.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2830.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2830.ini
index c6536b22be..c6536b22be 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2830.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2830.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2850.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2850.ini
index cc6088079e..cc6088079e 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2850.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2850.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2873.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2873.ini
index 6c39037ae5..6c39037ae5 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2873.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2873.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2880.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2880.ini
index 2abb9f716b..2abb9f716b 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2880.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2880.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2881.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2881.ini
index 5a3d34b2c1..5a3d34b2c1 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2881.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2881.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2882.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2882.ini
index 591ba2fe42..591ba2fe42 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2882.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2882.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2900.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2900.ini
index 3a4144bf6b..3a4144bf6b 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2900.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2900.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2930.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2930.ini
index b144562e4d..b144562e4d 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2930.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2930.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2950.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2950.ini
index 3b6a477fc3..3b6a477fc3 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2950.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2950.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2970.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2970.ini
index 60e1423fd8..60e1423fd8 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2970.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2970.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2985.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2985.ini
index 1dc03e67ff..1dc03e67ff 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2985.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2985.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2990.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2990.ini
index ce35c62f5d..ce35c62f5d 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/2990.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/2990.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3020.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3020.ini
index 3b6673b47d..3b6673b47d 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3020.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3020.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3045.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3045.ini
index 1d462c1a83..1d462c1a83 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3045.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3045.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3070.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3070.ini
index 16baee6ce0..16baee6ce0 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3070.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3070.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3100.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3100.ini
index 2155127dc4..2155127dc4 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3100.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3100.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3125.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3125.ini
index 07c07c7ff9..07c07c7ff9 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3125.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3125.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3150.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3150.ini
index e322e36f86..e322e36f86 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3150.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3150.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3169.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3169.ini
index 8ed74f2ef9..8ed74f2ef9 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3169.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3169.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3195.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3195.ini
index b331b7d64b..b331b7d64b 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3195.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3195.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3225.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3225.ini
index 800e74334f..800e74334f 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3225.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3225.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3255.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3255.ini
index eb56de14b9..eb56de14b9 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3255.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3255.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3285.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3285.ini
index c5632384c1..c5632384c1 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3285.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3285.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3300.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3300.ini
index 7b1595a531..7b1595a531 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3300.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3300.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3325.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3325.ini
index 380ed40725..380ed40725 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3325.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3325.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3355.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3355.ini
index 542d9bd46b..542d9bd46b 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3355.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3355.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3380.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3380.ini
index 39d26fdbb6..39d26fdbb6 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3380.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3380.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3400.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3400.ini
index c23fe0b194..c23fe0b194 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3400.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3400.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3420.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3420.ini
index 2470d71f95..2470d71f95 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3420.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3420.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3450.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3450.ini
index cae2fe8b9d..cae2fe8b9d 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3450.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3450.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3460.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3460.ini
index 46a7c1b624..46a7c1b624 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3460.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3460.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3475.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3475.ini
index e7a7afc1d4..e7a7afc1d4 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3475.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3475.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3490.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3490.ini
index 7d57379795..7d57379795 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3490.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3490.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3520.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3520.ini
index 6274b967e3..6274b967e3 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3520.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3520.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3527.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3527.ini
index b5aa217800..b5aa217800 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3527.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3527.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3550.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3550.ini
index 5ae30b7787..5ae30b7787 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3550.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3550.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3560.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3560.ini
index bf3006b2e4..bf3006b2e4 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3560.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3560.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3592.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3592.ini
index b1118d4fbb..b1118d4fbb 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3592.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3592.ini
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3594.ini b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3594.ini
index f762d2f6fb..f762d2f6fb 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/ini/satellite/3594.ini
+++ b/MediaBrowser.Server.Startup.Common/LiveTv/TunerHosts/SatIp/ini/satellite/3594.ini
diff --git a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj
index 3dbcaaaf48..a1bca62e03 100644
--- a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj
+++ b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj
@@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MediaBrowser.Server.Startup.Common</RootNamespace>
<AssemblyName>MediaBrowser.Server.Startup.Common</AssemblyName>
- <TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
+ <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<TargetFrameworkProfile />
@@ -22,6 +22,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>None</DebugType>
@@ -30,27 +31,19 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
- <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
+ <Reference Include="Emby.Server.Core">
+ <HintPath>..\ThirdParty\emby\Emby.Server.Core.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 Include="INIFileParser, Version=2.3.0.0, Culture=neutral, PublicKeyToken=79af7b307b65cf3c, processorArchitecture=MSIL">
+ <HintPath>..\packages\ini-parser.2.3.0\lib\net20\INIFileParser.dll</HintPath>
+ <Private>True</Private>
</Reference>
- <Reference Include="Patterns.Logging, Version=1.0.5494.41209, Culture=neutral, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
- </Reference>
- <Reference Include="ServiceStack.Interfaces, Version=4.0.0.0, Culture=neutral, PublicKeyToken=e06fbc6124f57c43, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath>
- </Reference>
- <Reference Include="ServiceStack.Text, Version=4.0.0.0, Culture=neutral, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\ThirdParty\ServiceStack.Text\ServiceStack.Text.dll</HintPath>
+ <Reference Include="Microsoft.IO.RecyclableMemoryStream, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
+ <HintPath>..\packages\Microsoft.IO.RecyclableMemoryStream.1.2.1\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll</HintPath>
+ <Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
@@ -65,37 +58,68 @@
<Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link>
</Compile>
- <Compile Include="ApplicationHost.cs" />
<Compile Include="ApplicationPathHelper.cs" />
- <Compile Include="Browser\BrowserLauncher.cs" />
- <Compile Include="EntryPoints\KeepServerAwake.cs" />
- <Compile Include="EntryPoints\StartupWizard.cs" />
- <Compile Include="FFMpeg\FFMpegLoader.cs" />
- <Compile Include="FFMpeg\FFMpegInstallInfo.cs" />
- <Compile Include="FFMpeg\FFMpegInfo.cs" />
- <Compile Include="INativeApp.cs" />
- <Compile Include="MbLinkShortcutHandler.cs" />
- <Compile Include="Migrations\IVersionMigration.cs" />
- <Compile Include="Migrations\DbMigration.cs" />
- <Compile Include="Migrations\MovieDbEpisodeProviderMigration.cs" />
- <Compile Include="Migrations\UpdateLevelMigration.cs" />
- <Compile Include="NativeEnvironment.cs" />
+ <Compile Include="Cryptography\ASN1.cs" />
+ <Compile Include="Cryptography\ASN1Convert.cs" />
+ <Compile Include="Cryptography\BitConverterLE.cs" />
+ <Compile Include="Cryptography\CertificateGenerator.cs" />
+ <Compile Include="Cryptography\CryptoConvert.cs" />
+ <Compile Include="Cryptography\PfxGenerator.cs" />
+ <Compile Include="Cryptography\PKCS1.cs" />
+ <Compile Include="Cryptography\PKCS12.cs" />
+ <Compile Include="Cryptography\PKCS7.cs" />
+ <Compile Include="Cryptography\PKCS8.cs" />
+ <Compile Include="Cryptography\X501Name.cs" />
+ <Compile Include="Cryptography\X509Builder.cs" />
+ <Compile Include="Cryptography\X509Certificate.cs" />
+ <Compile Include="Cryptography\X509CertificateBuilder.cs" />
+ <Compile Include="Cryptography\X509CertificateCollection.cs" />
+ <Compile Include="Cryptography\X509Extension.cs" />
+ <Compile Include="Cryptography\X509Extensions.cs" />
+ <Compile Include="Cryptography\X520Attributes.cs" />
+ <Compile Include="ImageEncoderHelper.cs" />
+ <Compile Include="IO\MemoryStreamProvider.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\ChannelScan.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\ReportBlock.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpAppPacket.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpByePacket.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpListener.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpPacket.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpReceiverReportPacket.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpSenderReportPacket.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpSourceDescriptionPacket.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\SourceDescriptionBlock.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\SourceDescriptionItem.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtp\RtpListener.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtp\RtpPacket.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspMethod.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspRequest.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspResponse.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspSession.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspStatusCode.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\SatIpDiscovery.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\SatIpHost.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\TransmissionMode.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\Utils.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="StartupOptions.cs" />
- <Compile Include="UnhandledExceptionWriter.cs" />
+ <Compile Include="SystemEvents.cs" />
</ItemGroup>
<ItemGroup>
+ <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.Net\Emby.Drawing.Net.csproj">
+ <Project>{c97a239e-a96c-4d64-a844-ccf8cc30aecb}</Project>
+ <Name>Emby.Drawing.Net</Name>
+ </ProjectReference>
<ProjectReference Include="..\Emby.Drawing\Emby.Drawing.csproj">
<Project>{08fff49b-f175-4807-a2b5-73b0ebd9f716}</Project>
<Name>Emby.Drawing</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.Implementations\MediaBrowser.Common.Implementations.csproj">
- <Project>{c4d2573a-3fd3-441f-81af-174ac4cd4e1d}</Project>
- <Name>MediaBrowser.Common.Implementations</Name>
+ <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.Common\MediaBrowser.Common.csproj">
<Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
@@ -105,42 +129,185 @@
<Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project>
<Name>MediaBrowser.Controller</Name>
</ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Dlna\MediaBrowser.Dlna.csproj">
- <Project>{734098eb-6dc1-4dd0-a1ca-3140dcd2737c}</Project>
- <Name>MediaBrowser.Dlna</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj">
- <Project>{7ef9f3e0-697d-42f3-a08f-19deb5f84392}</Project>
- <Name>MediaBrowser.LocalMetadata</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj">
- <Project>{0bd82fa6-eb8a-4452-8af5-74f9c3849451}</Project>
- <Name>MediaBrowser.MediaEncoding</Name>
- </ProjectReference>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
<Name>MediaBrowser.Model</Name>
</ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Providers\MediaBrowser.Providers.csproj">
- <Project>{442b5058-dcaf-4263-bb6a-f21e31120a1b}</Project>
- <Name>MediaBrowser.Providers</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Server.Implementations\MediaBrowser.Server.Implementations.csproj">
- <Project>{2e781478-814d-4a48-9d80-bff206441a65}</Project>
- <Name>MediaBrowser.Server.Implementations</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.WebDashboard\MediaBrowser.WebDashboard.csproj">
- <Project>{5624b7b5-b5a7-41d8-9f10-cc5611109619}</Project>
- <Name>MediaBrowser.WebDashboard</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.XbmcMetadata\MediaBrowser.XbmcMetadata.csproj">
- <Project>{23499896-b135-4527-8574-c26e926ea99e}</Project>
- <Name>MediaBrowser.XbmcMetadata</Name>
- </ProjectReference>
</ItemGroup>
<ItemGroup>
+ <None Include="app.config" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0030.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0049.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0070.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0090.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0100.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0130.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0160.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0170.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0192.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0200.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0215.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0235.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0255.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0260.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0282.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0305.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0308.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0310.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0315.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0330.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0360.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0380.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0390.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0400.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0420.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0435.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0450.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0460.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0475.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0480.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0490.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0505.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0510.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0520.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0525.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0530.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0549.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0560.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0570.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0600.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0620.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0642.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0650.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0660.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0685.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0705.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0721.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0740.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0750.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0765.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0785.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0830.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0851.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0865.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0875.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0880.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0900.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0915.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0922.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0935.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0950.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0965.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1005.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1030.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1055.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1082.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1100.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1105.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1130.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1155.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1160.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1180.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1195.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1222.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1240.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1250.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1280.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1320.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1340.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1380.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1400.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1440.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1500.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1520.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1540.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1560.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1590.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1600 OPTUS D1 FTA %28160.0E%29.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1600.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1620.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1640.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1660.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1690.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1720.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1800.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1830.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2210.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2230.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2250.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2270.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2290.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2310.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2330.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2350.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2370.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2390.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2410.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2432.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2451.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2470.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2489.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2500.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2527.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2550.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2570.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2590.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2608.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2630.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2650.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2669.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2690.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2710.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2728.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2730.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2750.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2760.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2770.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2780.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2812.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2820.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2830.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2850.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2873.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2880.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2881.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2882.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2900.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2930.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2950.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2970.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2985.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2990.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3020.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3045.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3070.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3100.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3125.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3150.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3169.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3195.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3225.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3255.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3285.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3300.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3325.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3355.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3380.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3400.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3420.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3450.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3460.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3475.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3490.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3520.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3527.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3550.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3560.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3592.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3594.ini" />
<None Include="packages.config" />
</ItemGroup>
+ <ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
diff --git a/MediaBrowser.Server.Startup.Common/Migrations/DbMigration.cs b/MediaBrowser.Server.Startup.Common/Migrations/DbMigration.cs
deleted file mode 100644
index 6bcdcca879..0000000000
--- a/MediaBrowser.Server.Startup.Common/Migrations/DbMigration.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using System.Threading.Tasks;
-using MediaBrowser.Common.ScheduledTasks;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Server.Implementations.Persistence;
-
-namespace MediaBrowser.Server.Startup.Common.Migrations
-{
- public class DbMigration : IVersionMigration
- {
- private readonly IServerConfigurationManager _config;
- private readonly ITaskManager _taskManager;
-
- public DbMigration(IServerConfigurationManager config, ITaskManager taskManager)
- {
- _config = config;
- _taskManager = taskManager;
- }
-
- public async Task Run()
- {
- // If a forced migration is required, do that now
- if (_config.Configuration.MigrationVersion < CleanDatabaseScheduledTask.MigrationVersion)
- {
- if (!_config.Configuration.IsStartupWizardCompleted)
- {
- _config.Configuration.MigrationVersion = CleanDatabaseScheduledTask.MigrationVersion;
- _config.SaveConfiguration();
- return;
- }
-
- _taskManager.SuspendTriggers = true;
- CleanDatabaseScheduledTask.EnableUnavailableMessage = true;
-
- Task.Run(async () =>
- {
- await Task.Delay(1000).ConfigureAwait(false);
-
- _taskManager.Execute<CleanDatabaseScheduledTask>();
- });
-
- return;
- }
-
- if (_config.Configuration.SchemaVersion < SqliteItemRepository.LatestSchemaVersion)
- {
- if (!_config.Configuration.IsStartupWizardCompleted)
- {
- _config.Configuration.SchemaVersion = SqliteItemRepository.LatestSchemaVersion;
- _config.SaveConfiguration();
- return;
- }
-
- Task.Run(async () =>
- {
- await Task.Delay(1000).ConfigureAwait(false);
-
- _taskManager.Execute<CleanDatabaseScheduledTask>();
- });
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Startup.Common/Migrations/MovieDbEpisodeProviderMigration.cs b/MediaBrowser.Server.Startup.Common/Migrations/MovieDbEpisodeProviderMigration.cs
deleted file mode 100644
index cd2122e57b..0000000000
--- a/MediaBrowser.Server.Startup.Common/Migrations/MovieDbEpisodeProviderMigration.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using System.Linq;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Startup.Common.Migrations
-{
- class MovieDbEpisodeProviderMigration : IVersionMigration
- {
- private readonly IServerConfigurationManager _config;
- private const string _providerName = "TheMovieDb";
-
- public MovieDbEpisodeProviderMigration(IServerConfigurationManager config)
- {
- _config = config;
- }
-
- public async Task Run()
- {
- var migrationKey = this.GetType().FullName;
- var migrationKeyList = _config.Configuration.Migrations.ToList();
-
- if (!migrationKeyList.Contains(migrationKey))
- {
- foreach (var metaDataOption in _config.Configuration.MetadataOptions)
- {
- if (metaDataOption.ItemType == "Episode")
- {
- var disabledFetchers = metaDataOption.DisabledMetadataFetchers.ToList();
- if (!disabledFetchers.Contains(_providerName))
- {
- disabledFetchers.Add(_providerName);
- metaDataOption.DisabledMetadataFetchers = disabledFetchers.ToArray();
- }
- }
- }
-
- migrationKeyList.Add(migrationKey);
- _config.Configuration.Migrations = migrationKeyList.ToArray();
- _config.SaveConfiguration();
- }
-
- }
- }
-}
diff --git a/MediaBrowser.Server.Startup.Common/NativeEnvironment.cs b/MediaBrowser.Server.Startup.Common/NativeEnvironment.cs
deleted file mode 100644
index b30509982e..0000000000
--- a/MediaBrowser.Server.Startup.Common/NativeEnvironment.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using MediaBrowser.Model.System;
-
-namespace MediaBrowser.Server.Startup.Common
-{
- public class NativeEnvironment
- {
- public OperatingSystem OperatingSystem { get; set; }
- public Architecture SystemArchitecture { get; set; }
- public string OperatingSystemVersionString { get; set; }
- }
-
- public enum OperatingSystem
- {
- Windows = 0,
- Osx = 1,
- Bsd = 2,
- Linux = 3
- }
-}
diff --git a/MediaBrowser.Server.Startup.Common/SystemEvents.cs b/MediaBrowser.Server.Startup.Common/SystemEvents.cs
new file mode 100644
index 0000000000..8d5cd4ad8c
--- /dev/null
+++ b/MediaBrowser.Server.Startup.Common/SystemEvents.cs
@@ -0,0 +1,50 @@
+using System;
+using MediaBrowser.Common.Events;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.System;
+
+namespace MediaBrowser.Server.Startup.Common
+{
+ public class SystemEvents : ISystemEvents
+ {
+ public event EventHandler Resume;
+ public event EventHandler Suspend;
+ public event EventHandler SessionLogoff;
+ public event EventHandler SystemShutdown;
+
+ private readonly ILogger _logger;
+
+ 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/MediaBrowser.Server.Startup.Common/app.config b/MediaBrowser.Server.Startup.Common/app.config
new file mode 100644
index 0000000000..e5b8d3d027
--- /dev/null
+++ b/MediaBrowser.Server.Startup.Common/app.config
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+ <runtime>
+ <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
+ <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>
+ </runtime>
+</configuration> \ No newline at end of file
diff --git a/MediaBrowser.Server.Startup.Common/packages.config b/MediaBrowser.Server.Startup.Common/packages.config
index 238025a788..b12895000b 100644
--- a/MediaBrowser.Server.Startup.Common/packages.config
+++ b/MediaBrowser.Server.Startup.Common/packages.config
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="CommonIO" version="1.0.0.9" targetFramework="net45" />
- <package id="Mono.Posix" version="4.0.0.0" targetFramework="net45" />
- <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
+ <package id="ini-parser" version="2.3.0" targetFramework="net46" />
+ <package id="Microsoft.IO.RecyclableMemoryStream" version="1.2.1" targetFramework="net46" />
</packages> \ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/App.config b/MediaBrowser.ServerApplication/App.config
index fa7bc9f895..4bac6bb70f 100644
--- a/MediaBrowser.ServerApplication/App.config
+++ b/MediaBrowser.ServerApplication/App.config
@@ -1,80 +1,84 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
- <section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/>
+ <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"/>
+ <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"/>
+ <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=""/>
+ <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"/>
+ <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2" />
</startup>
<runtime>
- <gcAllowVeryLargeObjects enabled="true"/>
- <gcServer enabled="true"/>
+ <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"/>
+ <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"/>
+ <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"/>
+ <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"/>
+ <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"/>
+ <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"/>
+ <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-2.3.6.0" newVersion="2.3.6.0"/>
+ <assemblyIdentity name="SimpleInjector" publicKeyToken="984cb50dea722e99" culture="neutral" />
+ <bindingRedirect oldVersion="0.0.0.0-2.3.6.0" newVersion="2.3.6.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"/>
+ <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=""/>
+ <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"/>
+ <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"/>
+ <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"/>
+ <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>
diff --git a/MediaBrowser.ServerApplication/MainForm.Designer.cs b/MediaBrowser.ServerApplication/MainForm.Designer.cs
index 480e10a46d..855a64899c 100644
--- a/MediaBrowser.ServerApplication/MainForm.Designer.cs
+++ b/MediaBrowser.ServerApplication/MainForm.Designer.cs
@@ -35,14 +35,8 @@
this.cmdBrowse = new System.Windows.Forms.ToolStripMenuItem();
this.cmdConfigure = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
- this.cmdLibraryExplorer = new System.Windows.Forms.ToolStripMenuItem();
this.cmdRestart = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
- this.cmdApiDocs = new System.Windows.Forms.ToolStripMenuItem();
- this.cmdStandardDocs = new System.Windows.Forms.ToolStripMenuItem();
- this.cmdSwagger = new System.Windows.Forms.ToolStripMenuItem();
- this.cmdGtihub = new System.Windows.Forms.ToolStripMenuItem();
- this.cmdLogWindow = new System.Windows.Forms.ToolStripMenuItem();
this.cmdCommunity = new System.Windows.Forms.ToolStripMenuItem();
this.cmdExit = new System.Windows.Forms.ToolStripMenuItem();
this.contextMenuStrip1.SuspendLayout();
@@ -52,7 +46,7 @@
//
this.notifyIcon1.ContextMenuStrip = this.contextMenuStrip1;
this.notifyIcon1.Icon = ((System.Drawing.Icon)(resources.GetObject("notifyIcon1.Icon")));
- this.notifyIcon1.Text = "Media Browser";
+ this.notifyIcon1.Text = "Emby";
this.notifyIcon1.Visible = true;
//
// contextMenuStrip1
@@ -61,11 +55,8 @@
this.cmdBrowse,
this.cmdConfigure,
this.toolStripSeparator2,
- this.cmdLibraryExplorer,
this.cmdRestart,
this.toolStripSeparator1,
- this.cmdApiDocs,
- this.cmdLogWindow,
this.cmdCommunity,
this.cmdExit});
this.contextMenuStrip1.Name = "contextMenuStrip1";
@@ -90,12 +81,6 @@
this.toolStripSeparator2.Name = "toolStripSeparator2";
this.toolStripSeparator2.Size = new System.Drawing.Size(205, 6);
//
- // cmdLibraryExplorer
- //
- this.cmdLibraryExplorer.Name = "cmdLibraryExplorer";
- this.cmdLibraryExplorer.Size = new System.Drawing.Size(208, 22);
- this.cmdLibraryExplorer.Text = "Open Library Explorer";
- //
// cmdRestart
//
this.cmdRestart.Name = "cmdRestart";
@@ -107,41 +92,6 @@
this.toolStripSeparator1.Name = "toolStripSeparator1";
this.toolStripSeparator1.Size = new System.Drawing.Size(205, 6);
//
- // cmdApiDocs
- //
- this.cmdApiDocs.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
- this.cmdStandardDocs,
- this.cmdSwagger,
- this.cmdGtihub});
- this.cmdApiDocs.Name = "cmdApiDocs";
- this.cmdApiDocs.Size = new System.Drawing.Size(208, 22);
- this.cmdApiDocs.Text = "View Api Documentation";
- //
- // cmdStandardDocs
- //
- this.cmdStandardDocs.Name = "cmdStandardDocs";
- this.cmdStandardDocs.Size = new System.Drawing.Size(136, 22);
- this.cmdStandardDocs.Text = "Standard";
- //
- // cmdSwagger
- //
- this.cmdSwagger.Name = "cmdSwagger";
- this.cmdSwagger.Size = new System.Drawing.Size(136, 22);
- this.cmdSwagger.Text = "Swagger";
- //
- // cmdGtihub
- //
- this.cmdGtihub.Name = "cmdGtihub";
- this.cmdGtihub.Size = new System.Drawing.Size(136, 22);
- this.cmdGtihub.Text = "Github Wiki";
- //
- // cmdLogWindow
- //
- this.cmdLogWindow.CheckOnClick = true;
- this.cmdLogWindow.Name = "cmdLogWindow";
- this.cmdLogWindow.Size = new System.Drawing.Size(208, 22);
- this.cmdLogWindow.Text = "Show Log Window";
- //
// cmdCommunity
//
this.cmdCommunity.Name = "cmdCommunity";
@@ -179,14 +129,8 @@
private System.Windows.Forms.ToolStripMenuItem cmdBrowse;
private System.Windows.Forms.ToolStripMenuItem cmdConfigure;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
- private System.Windows.Forms.ToolStripMenuItem cmdLibraryExplorer;
private System.Windows.Forms.ToolStripMenuItem cmdRestart;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
- private System.Windows.Forms.ToolStripMenuItem cmdLogWindow;
private System.Windows.Forms.ToolStripMenuItem cmdCommunity;
- private System.Windows.Forms.ToolStripMenuItem cmdApiDocs;
- private System.Windows.Forms.ToolStripMenuItem cmdStandardDocs;
- private System.Windows.Forms.ToolStripMenuItem cmdSwagger;
- private System.Windows.Forms.ToolStripMenuItem cmdGtihub;
}
} \ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs
index 92ff781525..c349401415 100644
--- a/MediaBrowser.ServerApplication/MainStartup.cs
+++ b/MediaBrowser.ServerApplication/MainStartup.cs
@@ -1,8 +1,6 @@
-using MediaBrowser.Common.Implementations.Logging;
-using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Logging;
using MediaBrowser.Server.Implementations;
using MediaBrowser.Server.Startup.Common;
-using MediaBrowser.Server.Startup.Common.Browser;
using MediaBrowser.ServerApplication.Native;
using MediaBrowser.ServerApplication.Splash;
using MediaBrowser.ServerApplication.Updates;
@@ -19,10 +17,18 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
-using CommonIO.Windows;
+using Emby.Common.Implementations.EnvironmentInfo;
+using Emby.Common.Implementations.IO;
+using Emby.Common.Implementations.Logging;
+using Emby.Common.Implementations.Networking;
+using Emby.Common.Implementations.Security;
+using Emby.Server.Core;
+using Emby.Server.Implementations;
+using Emby.Server.Implementations.Browser;
+using Emby.Server.Implementations.IO;
using ImageMagickSharp;
using MediaBrowser.Common.Net;
-using MediaBrowser.Server.Implementations.Logging;
+using MediaBrowser.Server.Startup.Common.IO;
namespace MediaBrowser.ServerApplication
{
@@ -32,13 +38,15 @@ namespace MediaBrowser.ServerApplication
private static ILogger _logger;
- private static bool _isRunningAsService = false;
+ public static bool IsRunningAsService = false;
private static bool _canRestartService = false;
private static bool _appHostDisposed;
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetDllDirectory(string lpPathName);
+ public static string ApplicationPath;
+
public static bool TryGetLocalFromUncDirectory(string local, out string unc)
{
if ((local == null) || (local == ""))
@@ -62,28 +70,30 @@ namespace MediaBrowser.ServerApplication
return false;
}
/// <summary>
- /// Defines the entry point of the application.
- /// </summary>
+ /// Defines the entry point of the application.
+ /// </summary>
public static void Main()
{
- var options = new StartupOptions();
- _isRunningAsService = options.ContainsOption("-service");
+ var options = new StartupOptions(Environment.GetCommandLineArgs());
+ IsRunningAsService = options.ContainsOption("-service");
- if (_isRunningAsService)
+ if (IsRunningAsService)
{
//_canRestartService = CanRestartWindowsService();
}
var currentProcess = Process.GetCurrentProcess();
- var applicationPath = currentProcess.MainModule.FileName;
- var architecturePath = Path.Combine(Path.GetDirectoryName(applicationPath), Environment.Is64BitProcess ? "x64" : "x86");
+ ApplicationPath = currentProcess.MainModule.FileName;
+ var architecturePath = Path.Combine(Path.GetDirectoryName(ApplicationPath), Environment.Is64BitProcess ? "x64" : "x86");
Wand.SetMagickCoderModulePath(architecturePath);
var success = SetDllDirectory(architecturePath);
- var appPaths = CreateApplicationPaths(applicationPath, _isRunningAsService);
+ SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_sqlite3());
+
+ var appPaths = CreateApplicationPaths(ApplicationPath, IsRunningAsService);
var logManager = new NlogManager(appPaths.LogDirectoryPath, "server");
logManager.ReloadLogger(LogSeverity.Debug);
@@ -97,7 +107,7 @@ namespace MediaBrowser.ServerApplication
if (options.ContainsOption("-installservice"))
{
logger.Info("Performing service installation");
- InstallService(applicationPath, logger);
+ InstallService(ApplicationPath, logger);
return;
}
@@ -105,7 +115,7 @@ namespace MediaBrowser.ServerApplication
if (options.ContainsOption("-installserviceasadmin"))
{
logger.Info("Performing service installation");
- RunServiceInstallation(applicationPath);
+ RunServiceInstallation(ApplicationPath);
return;
}
@@ -113,7 +123,7 @@ namespace MediaBrowser.ServerApplication
if (options.ContainsOption("-uninstallservice"))
{
logger.Info("Performing service uninstallation");
- UninstallService(applicationPath, logger);
+ UninstallService(ApplicationPath, logger);
return;
}
@@ -121,15 +131,15 @@ namespace MediaBrowser.ServerApplication
if (options.ContainsOption("-uninstallserviceasadmin"))
{
logger.Info("Performing service uninstallation");
- RunServiceUninstallation(applicationPath);
+ RunServiceUninstallation(ApplicationPath);
return;
}
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
- RunServiceInstallationIfNeeded(applicationPath);
+ RunServiceInstallationIfNeeded(ApplicationPath);
- if (IsAlreadyRunning(applicationPath, currentProcess))
+ if (IsAlreadyRunning(ApplicationPath, currentProcess))
{
logger.Info("Shutting down because another instance of Emby Server is already running.");
return;
@@ -143,7 +153,7 @@ namespace MediaBrowser.ServerApplication
try
{
- RunApplication(appPaths, logManager, _isRunningAsService, options);
+ RunApplication(appPaths, logManager, IsRunningAsService, options);
}
finally
{
@@ -199,7 +209,7 @@ namespace MediaBrowser.ServerApplication
}
}
- if (!_isRunningAsService)
+ if (!IsRunningAsService)
{
return IsAlreadyRunningAsService(applicationPath);
}
@@ -245,6 +255,8 @@ namespace MediaBrowser.ServerApplication
/// <returns>ServerApplicationPaths.</returns>
private static ServerApplicationPaths CreateApplicationPaths(string applicationPath, bool runAsService)
{
+ var appFolderPath = Path.GetDirectoryName(applicationPath);
+
var resourcesPath = Path.GetDirectoryName(applicationPath);
if (runAsService)
@@ -253,10 +265,10 @@ namespace MediaBrowser.ServerApplication
var programDataPath = Path.GetDirectoryName(systemPath);
- return new ServerApplicationPaths(programDataPath, applicationPath, resourcesPath);
+ return new ServerApplicationPaths(programDataPath, appFolderPath, resourcesPath);
}
- return new ServerApplicationPaths(ApplicationPathHelper.GetProgramDataPath(applicationPath), applicationPath, resourcesPath);
+ return new ServerApplicationPaths(ApplicationPathHelper.GetProgramDataPath(applicationPath), appFolderPath, resourcesPath);
}
/// <summary>
@@ -267,7 +279,7 @@ namespace MediaBrowser.ServerApplication
{
get
{
- if (_isRunningAsService)
+ if (IsRunningAsService)
{
return _canRestartService;
}
@@ -286,7 +298,11 @@ namespace MediaBrowser.ServerApplication
{
get
{
- if (_isRunningAsService)
+#if DEBUG
+ return false;
+#endif
+
+ if (IsRunningAsService)
{
return _canRestartService;
}
@@ -308,21 +324,25 @@ namespace MediaBrowser.ServerApplication
/// <param name="options">The options.</param>
private static void RunApplication(ServerApplicationPaths appPaths, ILogManager logManager, bool runService, StartupOptions options)
{
- var fileSystem = new WindowsFileSystem(new PatternsLogger(logManager.GetLogger("FileSystem")));
+ var fileSystem = new ManagedFileSystem(logManager.GetLogger("FileSystem"), true, true, true);
+ fileSystem.AddShortcutHandler(new LnkShortcutHandler());
fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
- //fileSystem.AddShortcutHandler(new LnkShortcutHandler(fileSystem));
- var nativeApp = new WindowsApp(fileSystem, _logger)
- {
- IsRunningAsService = runService
- };
+ var imageEncoder = ImageEncoderHelper.GetImageEncoder(_logger, logManager, fileSystem, options, () => _appHost.HttpClient, appPaths);
- _appHost = new ApplicationHost(appPaths,
+ _appHost = new WindowsAppHost(appPaths,
logManager,
options,
fileSystem,
+ new PowerManagement(),
"emby.windows.zip",
- nativeApp);
+ new EnvironmentInfo(),
+ imageEncoder,
+ new Server.Startup.Common.SystemEvents(logManager.GetLogger("SystemEvents")),
+ new RecyclableMemoryStreamProvider(),
+ new Networking.NetworkManager(logManager.GetLogger("NetworkManager")),
+ GenerateCertificate,
+ () => Environment.UserDomainName);
var initProgress = new Progress<double>();
@@ -351,8 +371,7 @@ namespace MediaBrowser.ServerApplication
task = InstallVcredist2013IfNeeded(_appHost, _logger);
Task.WaitAll(task);
- SystemEvents.SessionEnding += SystemEvents_SessionEnding;
- SystemEvents.SessionSwitch += SystemEvents_SessionSwitch;
+ Microsoft.Win32.SystemEvents.SessionSwitch += SystemEvents_SessionSwitch;
HideSplashScreen();
@@ -363,6 +382,11 @@ namespace MediaBrowser.ServerApplication
}
}
+ private static void GenerateCertificate(string certPath, string certHost)
+ {
+ CertificateGenerator.CreateSelfSignCertificatePfx(certPath, certHost, _logger);
+ }
+
private static ServerNotifyIcon _serverNotifyIcon;
private static TaskScheduler _mainTaskScheduler;
private static void ShowTrayIcon()
@@ -416,7 +440,7 @@ namespace MediaBrowser.ServerApplication
public static void Invoke(Action action)
{
- if (_isRunningAsService)
+ if (IsRunningAsService)
{
action();
}
@@ -548,19 +572,6 @@ namespace MediaBrowser.ServerApplication
}
/// <summary>
- /// Handles the SessionEnding event of the SystemEvents control.
- /// </summary>
- /// <param name="sender">The source of the event.</param>
- /// <param name="e">The <see cref="SessionEndingEventArgs"/> instance containing the event data.</param>
- static void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
- {
- if (e.Reason == SessionEndReasons.SystemShutdown || !_isRunningAsService)
- {
- Shutdown();
- }
- }
-
- /// <summary>
/// Handles the UnhandledException event of the CurrentDomain control.
/// </summary>
/// <param name="sender">The source of the event.</param>
@@ -571,7 +582,7 @@ namespace MediaBrowser.ServerApplication
new UnhandledExceptionWriter(_appHost.ServerConfigurationManager.ApplicationPaths, _logger, _appHost.LogManager).Log(exception);
- if (!_isRunningAsService)
+ if (!IsRunningAsService)
{
MessageBox.Show("Unhandled exception: " + exception.Message);
}
@@ -599,7 +610,7 @@ namespace MediaBrowser.ServerApplication
// Update is there - execute update
try
{
- var serviceName = _isRunningAsService ? BackgroundService.GetExistingServiceName() : string.Empty;
+ var serviceName = IsRunningAsService ? BackgroundService.GetExistingServiceName() : string.Empty;
new ApplicationUpdater().UpdateApplication(appPaths, updateArchive, logger, serviceName);
// And just let the app exit so it can update
@@ -618,7 +629,7 @@ namespace MediaBrowser.ServerApplication
public static void Shutdown()
{
- if (_isRunningAsService)
+ if (IsRunningAsService)
{
ShutdownWindowsService();
}
@@ -634,7 +645,7 @@ namespace MediaBrowser.ServerApplication
{
DisposeAppHost();
- if (_isRunningAsService)
+ if (IsRunningAsService)
{
RestartWindowsService();
}
@@ -645,7 +656,7 @@ namespace MediaBrowser.ServerApplication
_logger.Info("Starting new instance");
//Application.Restart();
- Process.Start(_appHost.ServerConfigurationManager.ApplicationPaths.ApplicationPath);
+ Process.Start(ApplicationPath);
ShutdownWindowsApplication();
}
diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
index 0a7a3a6a54..7badccef35 100644
--- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
+++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
@@ -64,32 +64,51 @@
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
- <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
+ <Reference Include="Emby.Common.Implementations">
+ <HintPath>..\ThirdParty\emby\Emby.Common.Implementations.dll</HintPath>
+ </Reference>
+ <Reference Include="Emby.Server.Core">
+ <HintPath>..\ThirdParty\emby\Emby.Server.Core.dll</HintPath>
</Reference>
<Reference Include="ImageMagickSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ImageMagickSharp.1.0.0.18\lib\net45\ImageMagickSharp.dll</HintPath>
</Reference>
- <Reference Include="Patterns.Logging, Version=1.0.5494.41209, Culture=neutral, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
+ <Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
+ <HintPath>..\packages\NLog.4.4.0-betaV15\lib\net45\NLog.dll</HintPath>
+ <Private>True</Private>
</Reference>
- <Reference Include="ServiceStack.Interfaces">
- <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath>
+ <Reference Include="ServiceStack.Text, Version=4.5.4.0, Culture=neutral, processorArchitecture=MSIL">
+ <HintPath>..\packages\ServiceStack.Text.4.5.4\lib\net45\ServiceStack.Text.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="SharpCompress, Version=0.14.0.0, Culture=neutral, processorArchitecture=MSIL">
+ <HintPath>..\packages\SharpCompress.0.14.0\lib\net45\SharpCompress.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="SimpleInjector, Version=3.2.4.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
+ <HintPath>..\packages\SimpleInjector.3.2.4\lib\net45\SimpleInjector.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
+ <HintPath>..\packages\SQLitePCLRaw.core.1.1.1\lib\net45\SQLitePCLRaw.core.dll</HintPath>
+ <Private>True</Private>
+ </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.1\lib\net45\SQLitePCLRaw.provider.sqlite3.dll</HintPath>
+ <Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Configuration.Install" />
<Reference Include="System.Core" />
- <Reference Include="System.Data.SQLite, Version=1.0.103.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL">
- <HintPath>..\packages\System.Data.SQLite.Core.1.0.103\lib\net46\System.Data.SQLite.dll</HintPath>
- <Private>True</Private>
- </Reference>
<Reference Include="System.Drawing" />
+ <Reference Include="System.IO.Compression" />
<Reference Include="System.Management" />
+ <Reference Include="System.Runtime.Serialization" />
+ <Reference Include="System.ServiceModel" />
<Reference Include="System.ServiceProcess" />
+ <Reference Include="System.Transactions" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
@@ -98,9 +117,6 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
- <Compile Include="..\MediaBrowser.Server.Implementations\Persistence\SqliteExtensions.cs">
- <Link>Native\SqliteExtensions.cs</Link>
- </Compile>
<Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link>
</Compile>
@@ -118,12 +134,10 @@
</Compile>
<Compile Include="MainStartup.cs" />
<Compile Include="Native\LnkShortcutHandler.cs" />
- <Compile Include="Native\DbConnector.cs" />
<Compile Include="Native\LoopbackUtil.cs" />
+ <Compile Include="Native\PowerManagement.cs" />
<Compile Include="Native\Standby.cs" />
<Compile Include="Native\ServerAuthorization.cs" />
- <Compile Include="Native\WindowsApp.cs" />
- <Compile Include="Networking\CertificateGenerator.cs" />
<Compile Include="Networking\NativeMethods.cs" />
<Compile Include="Networking\NetworkManager.cs" />
<Compile Include="Networking\NetworkShares.cs" />
@@ -141,6 +155,7 @@
<DependentUpon>SplashForm.cs</DependentUpon>
</Compile>
<Compile Include="Updates\ApplicationUpdater.cs" />
+ <Compile Include="WindowsAppHost.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
@@ -608,6 +623,9 @@
<Content Include="x64\IM_MOD_RL_yuv_.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="x64\sqlite3.dll">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="x86\CORE_RL_bzlib_.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1048,20 +1066,47 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<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.ImageMagick\Emby.Drawing.ImageMagick.csproj">
+ <Project>{6cfee013-6e7c-432b-ac37-cabf0880c69a}</Project>
+ <Name>Emby.Drawing.ImageMagick</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Emby.Drawing.Net\Emby.Drawing.Net.csproj">
+ <Project>{c97a239e-a96c-4d64-a844-ccf8cc30aecb}</Project>
+ <Name>Emby.Drawing.Net</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.Implementations\MediaBrowser.Common.Implementations.csproj">
- <Project>{c4d2573a-3fd3-441f-81af-174ac4cd4e1d}</Project>
- <Name>MediaBrowser.Common.Implementations</Name>
- </ProjectReference>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
<Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
<Name>MediaBrowser.Common</Name>
@@ -1070,10 +1115,6 @@
<Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project>
<Name>MediaBrowser.Controller</Name>
</ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Dlna\MediaBrowser.Dlna.csproj">
- <Project>{734098eb-6dc1-4dd0-a1ca-3140dcd2737c}</Project>
- <Name>MediaBrowser.Dlna</Name>
- </ProjectReference>
<ProjectReference Include="..\MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj">
<Project>{7ef9f3e0-697d-42f3-a08f-19deb5f84392}</Project>
<Name>MediaBrowser.LocalMetadata</Name>
@@ -1106,20 +1147,28 @@
<Project>{23499896-b135-4527-8574-c26e926ea99e}</Project>
<Name>MediaBrowser.XbmcMetadata</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>
+ <ProjectReference Include="..\ServiceStack\ServiceStack.csproj">
+ <Project>{680a1709-25eb-4d52-a87f-ee03ffd94baa}</Project>
+ <Name>ServiceStack</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\SocketHttpListener.Portable\SocketHttpListener.Portable.csproj">
+ <Project>{4f26d5d8-a7b0-42b3-ba42-7cb7d245934e}</Project>
+ <Name>SocketHttpListener.Portable</Name>
+ </ProjectReference>
</ItemGroup>
- <ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
- <Import Project="..\packages\System.Data.SQLite.Core.1.0.103\build\net46\System.Data.SQLite.Core.targets" Condition="Exists('..\packages\System.Data.SQLite.Core.1.0.103\build\net46\System.Data.SQLite.Core.targets')" />
- <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
- <PropertyGroup>
- <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
- </PropertyGroup>
- <Error Condition="!Exists('..\packages\System.Data.SQLite.Core.1.0.103\build\net46\System.Data.SQLite.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\System.Data.SQLite.Core.1.0.103\build\net46\System.Data.SQLite.Core.targets'))" />
- </Target>
<!-- 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">
diff --git a/MediaBrowser.ServerApplication/Native/DbConnector.cs b/MediaBrowser.ServerApplication/Native/DbConnector.cs
deleted file mode 100644
index f403ce2ce4..0000000000
--- a/MediaBrowser.ServerApplication/Native/DbConnector.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System.Data;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Server.Implementations.Persistence;
-
-namespace MediaBrowser.ServerApplication.Native
-{
- public class DbConnector : IDbConnector
- {
- private readonly ILogger _logger;
-
- public DbConnector(ILogger logger)
- {
- _logger = logger;
- }
-
- public Task<IDbConnection> Connect(string dbPath, bool isReadOnly, bool enablePooling = false, int? cacheSize = null)
- {
- return SqliteExtensions.ConnectToDb(dbPath, isReadOnly, enablePooling, cacheSize, _logger);
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/Native/LnkShortcutHandler.cs b/MediaBrowser.ServerApplication/Native/LnkShortcutHandler.cs
index dc1e3c79b2..91ff7033ee 100644
--- a/MediaBrowser.ServerApplication/Native/LnkShortcutHandler.cs
+++ b/MediaBrowser.ServerApplication/Native/LnkShortcutHandler.cs
@@ -3,11 +3,11 @@ using System.IO;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
-using CommonIO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.ServerApplication.Native
{
- public class LnkShortcutHandler : IShortcutHandler
+ public class LnkShortcutHandler :IShortcutHandler
{
public string Extension
{
diff --git a/MediaBrowser.ServerApplication/Native/PowerManagement.cs b/MediaBrowser.ServerApplication/Native/PowerManagement.cs
new file mode 100644
index 0000000000..0bd3db1dae
--- /dev/null
+++ b/MediaBrowser.ServerApplication/Native/PowerManagement.cs
@@ -0,0 +1,17 @@
+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
index 85baa0d03a..504df21995 100644
--- a/MediaBrowser.ServerApplication/Native/RegisterServer.bat
+++ b/MediaBrowser.ServerApplication/Native/RegisterServer.bat
@@ -26,6 +26,9 @@ 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/WindowsApp.cs b/MediaBrowser.ServerApplication/Native/WindowsApp.cs
deleted file mode 100644
index 7ebede40c2..0000000000
--- a/MediaBrowser.ServerApplication/Native/WindowsApp.cs
+++ /dev/null
@@ -1,262 +0,0 @@
-using System;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Server.Startup.Common;
-using MediaBrowser.ServerApplication.Networking;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Reflection;
-using System.Windows.Forms;
-using CommonIO;
-using MediaBrowser.Model.System;
-using MediaBrowser.Server.Implementations.Persistence;
-using MediaBrowser.Server.Startup.Common.FFMpeg;
-using OperatingSystem = MediaBrowser.Server.Startup.Common.OperatingSystem;
-
-namespace MediaBrowser.ServerApplication.Native
-{
- public class WindowsApp : INativeApp
- {
- private readonly IFileSystem _fileSystem;
- private readonly ILogger _logger;
-
- public WindowsApp(IFileSystem fileSystem, ILogger logger)
- {
- _fileSystem = fileSystem;
- _logger = logger;
- }
-
- public List<Assembly> GetAssembliesWithParts()
- {
- var list = new List<Assembly>();
-
- if (!System.Environment.Is64BitProcess)
- {
- //list.Add(typeof(PismoIsoManager).Assembly);
- }
-
- list.Add(GetType().Assembly);
-
- return list;
- }
-
- public void AuthorizeServer(int udpPort, int httpServerPort, int httpsPort, string applicationPath, string tempDirectory)
- {
- ServerAuthorization.AuthorizeServer(udpPort, httpServerPort, httpsPort, applicationPath, tempDirectory);
- }
-
- public NativeEnvironment Environment
- {
- get
- {
- return new NativeEnvironment
- {
- OperatingSystem = OperatingSystem.Windows,
- SystemArchitecture = System.Environment.Is64BitOperatingSystem ? Architecture.X64 : Architecture.X86,
- OperatingSystemVersionString = System.Environment.OSVersion.VersionString
- };
- }
- }
-
- public bool SupportsLibraryMonitor
- {
- get { return true; }
- }
-
- public bool SupportsRunningAsService
- {
- get
- {
- return true;
- }
- }
-
- public bool IsRunningAsService
- {
- get;
- set;
- }
-
- public bool CanSelfRestart
- {
- get
- {
- return MainStartup.CanSelfRestart;
- }
- }
-
- public bool SupportsAutoRunAtStartup
- {
- get
- {
- return true;
- }
- }
-
- public bool CanSelfUpdate
- {
- get
- {
- return MainStartup.CanSelfUpdate;
- }
- }
-
- public void Shutdown()
- {
- MainStartup.Shutdown();
- }
-
- public void Restart(StartupOptions startupOptions)
- {
- MainStartup.Restart();
- }
-
- public void ConfigureAutoRun(bool autorun)
- {
- var shortcutPath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.StartMenu), "Emby", "Emby Server.lnk");
-
- var startupPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Startup);
-
- if (autorun)
- {
- //Copy our shortut into the startup folder for this user
- var targetPath = Path.Combine(startupPath, Path.GetFileName(shortcutPath) ?? "Emby Server.lnk");
- _fileSystem.CreateDirectory(Path.GetDirectoryName(targetPath));
- File.Copy(shortcutPath, targetPath, true);
- }
- else
- {
- //Remove our shortcut from the startup folder for this user
- _fileSystem.DeleteFile(Path.Combine(startupPath, Path.GetFileName(shortcutPath) ?? "Emby Server.lnk"));
- }
- }
-
- public INetworkManager CreateNetworkManager(ILogger logger)
- {
- return new NetworkManager(logger);
- }
-
- public void PreventSystemStandby()
- {
- MainStartup.Invoke(Standby.PreventSleep);
- }
-
- public void AllowSystemStandby()
- {
- MainStartup.Invoke(Standby.AllowSleep);
- }
-
- public FFMpegInstallInfo GetFfmpegInstallInfo()
- {
- var info = new FFMpegInstallInfo();
-
- info.FFMpegFilename = "ffmpeg.exe";
- info.FFProbeFilename = "ffprobe.exe";
- info.Version = "0";
-
- return info;
- }
-
- public void LaunchUrl(string url)
- {
- var process = new Process
- {
- StartInfo = new ProcessStartInfo
- {
- FileName = url
- },
-
- EnableRaisingEvents = true,
- };
-
- process.Exited += ProcessExited;
-
- try
- {
- process.Start();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error launching url: {0}", ex, url);
-
- throw;
- }
- }
-
- public IDbConnector GetDbConnector()
- {
- return new DbConnector(_logger);
- }
-
- /// <summary>
- /// Processes the exited.
- /// </summary>
- /// <param name="sender">The sender.</param>
- /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
- private static void ProcessExited(object sender, EventArgs e)
- {
- ((Process)sender).Dispose();
- }
-
- public void EnableLoopback(string appName)
- {
- LoopUtil.Run(appName);
- }
-
- public bool PortsRequireAuthorization(string applicationPath)
- {
- var appNameSrch = Path.GetFileName(applicationPath);
-
- var startInfo = new ProcessStartInfo
- {
- FileName = "netsh",
-
- Arguments = "advfirewall firewall show rule \"" + appNameSrch + "\"",
-
- CreateNoWindow = true,
- UseShellExecute = false,
- WindowStyle = ProcessWindowStyle.Hidden,
- ErrorDialog = false,
- RedirectStandardOutput = true
- };
-
- using (var process = Process.Start(startInfo))
- {
- process.Start();
-
- try
- {
- var data = process.StandardOutput.ReadToEnd() ?? string.Empty;
-
- if (data.IndexOf("Block", StringComparison.OrdinalIgnoreCase) != -1)
- {
- _logger.Info("Found potential windows firewall rule blocking Emby Server: " + data);
- }
-
- //var parts = data.Split('\n');
-
- //return parts.Length > 4;
- //return Confirm();
- return false;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error querying windows firewall", ex);
-
- // Hate having to do this
- try
- {
- process.Kill();
- }
- catch (Exception ex1)
- {
- _logger.ErrorException("Error killing process", ex1);
- }
-
- throw;
- }
- }
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/Networking/CertificateGenerator.cs b/MediaBrowser.ServerApplication/Networking/CertificateGenerator.cs
deleted file mode 100644
index a7b0d6c324..0000000000
--- a/MediaBrowser.ServerApplication/Networking/CertificateGenerator.cs
+++ /dev/null
@@ -1,244 +0,0 @@
-using MediaBrowser.Model.Logging;
-using System;
-using System.IO;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-namespace MediaBrowser.ServerApplication.Networking
-{
- // Copied from: http://blogs.msdn.com/b/dcook/archive/2014/05/16/9143036.aspx
- // In case anybody is interested, source code is attached and is free for use by anybody as long as you don't hold me or Microsoft liable for it --
- // I have no idea whether this is actually the right or best way to do this. Give it the X500 distinguished name, validity start and end dates,
- // and an optional password for encrypting the key data, and it will give you the PFX file data. Let me know if you find any bugs or have any suggestions.
- internal class CertificateGenerator
- {
- internal static void CreateSelfSignCertificatePfx(
- string fileName,
- string hostname,
- ILogger logger)
- {
- if (string.IsNullOrWhiteSpace(fileName))
- {
- throw new ArgumentNullException("fileName");
- }
-
- string x500 = string.Format("CN={0}", hostname);
-
- DateTime startTime = DateTime.Now.AddDays(-2);
- DateTime endTime = DateTime.Now.AddYears(10);
-
- byte[] pfxData = CreateSelfSignCertificatePfx(
- x500,
- startTime,
- endTime);
-
- File.WriteAllBytes(fileName, pfxData);
- }
-
- private static byte[] CreateSelfSignCertificatePfx(
- string x500,
- DateTime startTime,
- DateTime endTime)
- {
- byte[] pfxData;
-
- if (x500 == null)
- {
- x500 = "";
- }
-
- SystemTime startSystemTime = ToSystemTime(startTime);
- SystemTime endSystemTime = ToSystemTime(endTime);
- string containerName = Guid.NewGuid().ToString();
-
- GCHandle dataHandle = new GCHandle();
- IntPtr providerContext = IntPtr.Zero;
- IntPtr cryptKey = IntPtr.Zero;
- IntPtr certContext = IntPtr.Zero;
- IntPtr certStore = IntPtr.Zero;
- IntPtr storeCertContext = IntPtr.Zero;
- IntPtr passwordPtr = IntPtr.Zero;
- RuntimeHelpers.PrepareConstrainedRegions();
- try
- {
- Check(NativeMethods.CryptAcquireContextW(
- out providerContext,
- containerName,
- null,
- 1, // PROV_RSA_FULL
- 8)); // CRYPT_NEWKEYSET
-
- Check(NativeMethods.CryptGenKey(
- providerContext,
- 1, // AT_KEYEXCHANGE
- 1 | 2048 << 16, // CRYPT_EXPORTABLE 2048 bit key
- out cryptKey));
-
- IntPtr errorStringPtr;
- int nameDataLength = 0;
- byte[] nameData;
-
- // errorStringPtr gets a pointer into the middle of the x500 string,
- // so x500 needs to be pinned until after we've copied the value
- // of errorStringPtr.
- dataHandle = GCHandle.Alloc(x500, GCHandleType.Pinned);
-
- if (!NativeMethods.CertStrToNameW(
- 0x00010001, // X509_ASN_ENCODING | PKCS_7_ASN_ENCODING
- dataHandle.AddrOfPinnedObject(),
- 3, // CERT_X500_NAME_STR = 3
- IntPtr.Zero,
- null,
- ref nameDataLength,
- out errorStringPtr))
- {
- string error = Marshal.PtrToStringUni(errorStringPtr);
- throw new ArgumentException(error);
- }
-
- nameData = new byte[nameDataLength];
-
- if (!NativeMethods.CertStrToNameW(
- 0x00010001, // X509_ASN_ENCODING | PKCS_7_ASN_ENCODING
- dataHandle.AddrOfPinnedObject(),
- 3, // CERT_X500_NAME_STR = 3
- IntPtr.Zero,
- nameData,
- ref nameDataLength,
- out errorStringPtr))
- {
- string error = Marshal.PtrToStringUni(errorStringPtr);
- throw new ArgumentException(error);
- }
-
- dataHandle.Free();
-
- dataHandle = GCHandle.Alloc(nameData, GCHandleType.Pinned);
- CryptoApiBlob nameBlob = new CryptoApiBlob(
- nameData.Length,
- dataHandle.AddrOfPinnedObject());
-
- CryptKeyProviderInformation kpi = new CryptKeyProviderInformation();
- kpi.ContainerName = containerName;
- kpi.ProviderType = 1; // PROV_RSA_FULL
- kpi.KeySpec = 1; // AT_KEYEXCHANGE
-
- CryptAlgorithmIdentifier sha256Identifier = new CryptAlgorithmIdentifier();
- sha256Identifier.pszObjId = "1.2.840.113549.1.1.11";
-
- certContext = NativeMethods.CertCreateSelfSignCertificate(
- providerContext,
- ref nameBlob,
- 0,
- ref kpi,
- ref sha256Identifier,
- ref startSystemTime,
- ref endSystemTime,
- IntPtr.Zero);
- Check(certContext != IntPtr.Zero);
- dataHandle.Free();
-
- certStore = NativeMethods.CertOpenStore(
- "Memory", // sz_CERT_STORE_PROV_MEMORY
- 0,
- IntPtr.Zero,
- 0x2000, // CERT_STORE_CREATE_NEW_FLAG
- IntPtr.Zero);
- Check(certStore != IntPtr.Zero);
-
- Check(NativeMethods.CertAddCertificateContextToStore(
- certStore,
- certContext,
- 1, // CERT_STORE_ADD_NEW
- out storeCertContext));
-
- NativeMethods.CertSetCertificateContextProperty(
- storeCertContext,
- 2, // CERT_KEY_PROV_INFO_PROP_ID
- 0,
- ref kpi);
-
- CryptoApiBlob pfxBlob = new CryptoApiBlob();
- Check(NativeMethods.PFXExportCertStoreEx(
- certStore,
- ref pfxBlob,
- passwordPtr,
- IntPtr.Zero,
- 7)); // EXPORT_PRIVATE_KEYS | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY
-
- pfxData = new byte[pfxBlob.DataLength];
- dataHandle = GCHandle.Alloc(pfxData, GCHandleType.Pinned);
- pfxBlob.Data = dataHandle.AddrOfPinnedObject();
- Check(NativeMethods.PFXExportCertStoreEx(
- certStore,
- ref pfxBlob,
- passwordPtr,
- IntPtr.Zero,
- 7)); // EXPORT_PRIVATE_KEYS | REPORT_NO_PRIVATE_KEY | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY
- dataHandle.Free();
- }
- finally
- {
- if (passwordPtr != IntPtr.Zero)
- {
- Marshal.ZeroFreeCoTaskMemUnicode(passwordPtr);
- }
-
- if (dataHandle.IsAllocated)
- {
- dataHandle.Free();
- }
-
- if (certContext != IntPtr.Zero)
- {
- NativeMethods.CertFreeCertificateContext(certContext);
- }
-
- if (storeCertContext != IntPtr.Zero)
- {
- NativeMethods.CertFreeCertificateContext(storeCertContext);
- }
-
- if (certStore != IntPtr.Zero)
- {
- NativeMethods.CertCloseStore(certStore, 0);
- }
-
- if (cryptKey != IntPtr.Zero)
- {
- NativeMethods.CryptDestroyKey(cryptKey);
- }
-
- if (providerContext != IntPtr.Zero)
- {
- NativeMethods.CryptReleaseContext(providerContext, 0);
- NativeMethods.CryptAcquireContextW(
- out providerContext,
- containerName,
- null,
- 1, // PROV_RSA_FULL
- 0x10); // CRYPT_DELETEKEYSET
- }
- }
-
- return pfxData;
- }
-
- private static SystemTime ToSystemTime(DateTime dateTime)
- {
- long fileTime = dateTime.ToFileTime();
- SystemTime systemTime;
- Check(NativeMethods.FileTimeToSystemTime(ref fileTime, out systemTime));
- return systemTime;
- }
-
- private static void Check(bool nativeCallSucceeded)
- {
- if (!nativeCallSucceeded)
- {
- int error = Marshal.GetHRForLastWin32Error();
- Marshal.ThrowExceptionForHR(error);
- }
- }
- }
-}
diff --git a/MediaBrowser.ServerApplication/Networking/NetworkManager.cs b/MediaBrowser.ServerApplication/Networking/NetworkManager.cs
index ed60de9d20..6d3d96e19a 100644
--- a/MediaBrowser.ServerApplication/Networking/NetworkManager.cs
+++ b/MediaBrowser.ServerApplication/Networking/NetworkManager.cs
@@ -1,6 +1,4 @@
-using MediaBrowser.Common.Implementations.Networking;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
using System;
@@ -15,7 +13,7 @@ namespace MediaBrowser.ServerApplication.Networking
/// <summary>
/// Class NetUtils
/// </summary>
- public class NetworkManager : BaseNetworkManager, INetworkManager
+ public class NetworkManager : Emby.Common.Implementations.Networking.NetworkManager
{
public NetworkManager(ILogger logger)
: base(logger)
@@ -27,7 +25,7 @@ namespace MediaBrowser.ServerApplication.Networking
/// </summary>
/// <param name="path">The path.</param>
/// <returns>IEnumerable{NetworkShare}.</returns>
- public IEnumerable<NetworkShare> GetNetworkShares(string path)
+ public override IEnumerable<NetworkShare> GetNetworkShares(string path)
{
Logger.Info("Getting network shares from {0}", path);
return new ShareCollection(path).OfType<Share>().Select(ToNetworkShare);
@@ -150,7 +148,7 @@ namespace MediaBrowser.ServerApplication.Networking
/// Gets available devices within the domain
/// </summary>
/// <returns>PC's in the Domain</returns>
- public IEnumerable<FileSystemEntryInfo> GetNetworkDevices()
+ public override IEnumerable<FileSystemEntryInfo> GetNetworkDevices()
{
return GetNetworkDevicesInternal().Select(c => new FileSystemEntryInfo
{
@@ -161,16 +159,6 @@ namespace MediaBrowser.ServerApplication.Networking
}
/// <summary>
- /// Generates a self signed certificate at the locatation specified by <paramref name="certificatePath"/>.
- /// </summary>
- /// <param name="certificatePath">The path to generate the certificate.</param>
- /// <param name="hostname">The common name for the certificate.</param>
- public void GenerateSelfSignedSslCertificate(string certificatePath, string hostname)
- {
- CertificateGenerator.CreateSelfSignCertificatePfx(certificatePath, hostname, Logger);
- }
-
- /// <summary>
/// Gets the network prefix.
/// </summary>
/// <value>The network prefix.</value>
diff --git a/MediaBrowser.ServerApplication/ServerNotifyIcon.cs b/MediaBrowser.ServerApplication/ServerNotifyIcon.cs
index 7805c0d685..c421dd9ebd 100644
--- a/MediaBrowser.ServerApplication/ServerNotifyIcon.cs
+++ b/MediaBrowser.ServerApplication/ServerNotifyIcon.cs
@@ -1,11 +1,11 @@
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Model.Logging;
-using MediaBrowser.Server.Startup.Common.Browser;
using System;
using System.ComponentModel;
using System.Windows.Forms;
+using Emby.Server.Implementations.Browser;
+using MediaBrowser.Model.Globalization;
namespace MediaBrowser.ServerApplication
{
diff --git a/MediaBrowser.ServerApplication/Updates/ApplicationUpdater.cs b/MediaBrowser.ServerApplication/Updates/ApplicationUpdater.cs
index 08c8a4dea1..aa8679bd50 100644
--- a/MediaBrowser.ServerApplication/Updates/ApplicationUpdater.cs
+++ b/MediaBrowser.ServerApplication/Updates/ApplicationUpdater.cs
@@ -37,11 +37,6 @@ namespace MediaBrowser.ServerApplication.Updates
logger.Info("Copying updater dependencies to temporary location");
File.Copy(source, tempUpdaterDll, true);
var product = "server";
- // Our updater needs SS and ionic
- source = Path.Combine(systemPath, "ServiceStack.Text.dll");
- File.Copy(source, Path.Combine(tempPath, "ServiceStack.Text.dll"), true);
- source = Path.Combine(systemPath, "SharpCompress.dll");
- File.Copy(source, Path.Combine(tempPath, "SharpCompress.dll"), true);
logger.Info("Starting updater process.");
@@ -49,10 +44,20 @@ namespace MediaBrowser.ServerApplication.Updates
// 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, appPaths.ApplicationPath, systemPath);
+ product, archive, Process.GetCurrentProcess().Id, version, restartServiceName ?? string.Empty, appPaths.ProgramDataPath, MainStartup.ApplicationPath, systemPath);
logger.Info("Args: {0}", args);
- Process.Start(tempUpdater, 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
new file mode 100644
index 0000000000..c2cdb9ab01
--- /dev/null
+++ b/MediaBrowser.ServerApplication/WindowsAppHost.cs
@@ -0,0 +1,215 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using Emby.Server.Core;
+using Emby.Server.Implementations;
+using Emby.Server.Implementations.EntryPoints;
+using Emby.Server.Implementations.FFMpeg;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.System;
+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, IMemoryStreamFactory memoryStreamFactory, MediaBrowser.Common.Net.INetworkManager networkManager, Action<string, string> certificateGenerator, Func<string> defaultUsernameFactory)
+ : base(applicationPaths, logManager, options, fileSystem, powerManagement, releaseAssetFilename, environmentInfo, imageEncoder, systemEvents, memoryStreamFactory, networkManager, certificateGenerator, defaultUsernameFactory)
+ {
+ }
+
+ public override bool IsRunningAsService
+ {
+ get { return MainStartup.IsRunningAsService; }
+ }
+
+ protected override FFMpegInstallInfo GetFfmpegInstallInfo()
+ {
+ var info = new FFMpegInstallInfo();
+
+ info.FFMpegFilename = "ffmpeg.exe";
+ info.FFProbeFilename = "ffprobe.exe";
+ info.Version = "20160410";
+ info.ArchiveType = "7z";
+ info.DownloadUrls = GetDownloadUrls();
+
+ return info;
+ }
+
+ private string[] GetDownloadUrls()
+ {
+ switch (EnvironmentInfo.SystemArchitecture)
+ {
+ case Architecture.X64:
+ return new[]
+ {
+ "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20160410-win64.7z"
+ };
+ case Architecture.X86:
+ return new[]
+ {
+ "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20160410-win32.7z"
+ };
+ }
+
+ return new string[] { };
+ }
+
+ protected override void RestartInternal()
+ {
+ MainStartup.Restart();
+ }
+
+ protected override List<Assembly> GetAssembliesWithPartsInternal()
+ {
+ var list = new List<Assembly>();
+
+ if (!Environment.Is64BitProcess)
+ {
+ //list.Add(typeof(PismoIsoManager).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 shortcutPath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.StartMenu), "Emby", "Emby Server.lnk");
+
+ var startupPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Startup);
+
+ if (autorun)
+ {
+ //Copy our shortut into the startup folder for this user
+ var targetPath = Path.Combine(startupPath, Path.GetFileName(shortcutPath) ?? "Emby Server.lnk");
+ FileSystemManager.CreateDirectory(Path.GetDirectoryName(targetPath));
+ File.Copy(shortcutPath, targetPath, true);
+ }
+ else
+ {
+ //Remove our shortcut from the startup folder for this user
+ FileSystemManager.DeleteFile(Path.Combine(startupPath, Path.GetFileName(shortcutPath) ?? "Emby Server.lnk"));
+ }
+ }
+
+ protected override bool SupportsDualModeSockets
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ protected override void EnableLoopbackInternal(string appName)
+ {
+ LoopUtil.Run(appName);
+ }
+
+ public override bool SupportsRunningAsService
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ public override bool CanSelfRestart
+ {
+ get
+ {
+ return MainStartup.CanSelfRestart;
+ }
+ }
+
+ public override bool SupportsAutoRunAtStartup
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ public override bool CanSelfUpdate
+ {
+ get
+ {
+ return MainStartup.CanSelfUpdate;
+ }
+ }
+
+ public bool PortsRequireAuthorization(string applicationPath)
+ {
+ var appNameSrch = Path.GetFileName(applicationPath);
+
+ var startInfo = new ProcessStartInfo
+ {
+ FileName = "netsh",
+
+ Arguments = "advfirewall firewall show rule \"" + appNameSrch + "\"",
+
+ CreateNoWindow = true,
+ UseShellExecute = false,
+ WindowStyle = ProcessWindowStyle.Hidden,
+ ErrorDialog = false,
+ RedirectStandardOutput = true
+ };
+
+ using (var process = Process.Start(startInfo))
+ {
+ process.Start();
+
+ try
+ {
+ var data = process.StandardOutput.ReadToEnd() ?? string.Empty;
+
+ if (data.IndexOf("Block", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ Logger.Info("Found potential windows firewall rule blocking Emby Server: " + data);
+ }
+
+ //var parts = data.Split('\n');
+
+ //return parts.Length > 4;
+ //return Confirm();
+ return false;
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error querying windows firewall", ex);
+
+ // Hate having to do this
+ try
+ {
+ process.Kill();
+ }
+ catch (Exception ex1)
+ {
+ Logger.ErrorException("Error killing process", ex1);
+ }
+
+ throw;
+ }
+ }
+ }
+
+ }
+}
diff --git a/MediaBrowser.ServerApplication/packages.config b/MediaBrowser.ServerApplication/packages.config
index 63d3bab8a5..2cebb9aea6 100644
--- a/MediaBrowser.ServerApplication/packages.config
+++ b/MediaBrowser.ServerApplication/packages.config
@@ -1,7 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="CommonIO" version="1.0.0.9" targetFramework="net45" />
<package id="ImageMagickSharp" version="1.0.0.18" targetFramework="net45" />
- <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
- <package id="System.Data.SQLite.Core" version="1.0.103" targetFramework="net462" />
+ <package id="NLog" version="4.4.0-betaV15" targetFramework="net462" />
+ <package id="ServiceStack.Text" version="4.5.4" targetFramework="net462" />
+ <package id="SharpCompress" version="0.14.0" targetFramework="net462" />
+ <package id="SimpleInjector" version="3.2.4" targetFramework="net462" />
+ <package id="SQLitePCLRaw.core" version="1.1.1" targetFramework="net462" />
+ <package id="SQLitePCLRaw.provider.sqlite3.net45" version="1.1.1" targetFramework="net462" />
</packages> \ No newline at end of file
diff --git a/MediaBrowser.Tests/MediaBrowser.Tests.csproj b/MediaBrowser.Tests/MediaBrowser.Tests.csproj
index 76a1861097..4ea2cb0c0e 100644
--- a/MediaBrowser.Tests/MediaBrowser.Tests.csproj
+++ b/MediaBrowser.Tests/MediaBrowser.Tests.csproj
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
@@ -8,7 +8,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MediaBrowser.Tests</RootNamespace>
<AssemblyName>MediaBrowser.Tests</AssemblyName>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
@@ -16,6 +16,7 @@
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
+ <TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
diff --git a/MediaBrowser.Tests/app.config b/MediaBrowser.Tests/app.config
index 14f2f055ff..9d8c1ac93a 100644
--- a/MediaBrowser.Tests/app.config
+++ b/MediaBrowser.Tests/app.config
@@ -1,11 +1,11 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<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" />
+ <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>
</assemblyBinding>
</runtime>
-</configuration> \ No newline at end of file
+<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/></startup></configuration>
diff --git a/MediaBrowser.WebDashboard/Api/ConfigurationPageInfo.cs b/MediaBrowser.WebDashboard/Api/ConfigurationPageInfo.cs
index 16aa14cb71..33289e76c8 100644
--- a/MediaBrowser.WebDashboard/Api/ConfigurationPageInfo.cs
+++ b/MediaBrowser.WebDashboard/Api/ConfigurationPageInfo.cs
@@ -1,4 +1,6 @@
-using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Common.Plugins;
+using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Model.Plugins;
namespace MediaBrowser.WebDashboard.Api
{
@@ -30,5 +32,13 @@ namespace MediaBrowser.WebDashboard.Api
// Don't use "N" because it needs to match Plugin.Id
PluginId = page.Plugin.Id.ToString();
}
+
+ public ConfigurationPageInfo(IPlugin plugin, PluginPageInfo page)
+ {
+ Name = page.Name;
+
+ // Don't use "N" because it needs to match Plugin.Id
+ PluginId = plugin.Id.ToString();
+ }
}
}
diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs
index 4850b6fe05..7fcfbfb131 100644
--- a/MediaBrowser.WebDashboard/Api/DashboardService.cs
+++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs
@@ -1,23 +1,23 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Localization;
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 ServiceStack;
-using ServiceStack.Web;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Text;
using System.Threading.Tasks;
-using CommonIO;
-using WebMarkupMin.Core;
+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 MediaBrowser.WebDashboard.Api
{
@@ -79,19 +79,19 @@ namespace MediaBrowser.WebDashboard.Api
/// <summary>
/// Class DashboardService
/// </summary>
- public class DashboardService : IRestfulService, IHasResultFactory
+ public class DashboardService : IService, IRequiresRequest
{
/// <summary>
/// Gets or sets the logger.
/// </summary>
/// <value>The logger.</value>
- public ILogger Logger { get; set; }
+ private readonly ILogger _logger;
/// <summary>
/// Gets or sets the HTTP result factory.
/// </summary>
/// <value>The HTTP result factory.</value>
- public IHttpResultFactory ResultFactory { get; set; }
+ private readonly IHttpResultFactory _resultFactory;
/// <summary>
/// Gets or sets the request context.
@@ -112,6 +112,8 @@ namespace MediaBrowser.WebDashboard.Api
private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization;
private readonly IJsonSerializer _jsonSerializer;
+ private readonly IAssemblyInfo _assemblyInfo;
+ private readonly IMemoryStreamFactory _memoryStreamFactory;
/// <summary>
/// Initializes a new instance of the <see cref="DashboardService" /> class.
@@ -119,13 +121,17 @@ namespace MediaBrowser.WebDashboard.Api
/// <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)
+ public DashboardService(IServerApplicationHost appHost, IServerConfigurationManager serverConfigurationManager, IFileSystem fileSystem, ILocalizationManager localization, IJsonSerializer jsonSerializer, IAssemblyInfo assemblyInfo, ILogger logger, IHttpResultFactory resultFactory, IMemoryStreamFactory memoryStreamFactory)
{
_appHost = appHost;
_serverConfigurationManager = serverConfigurationManager;
_fileSystem = fileSystem;
_localization = localization;
_jsonSerializer = jsonSerializer;
+ _assemblyInfo = assemblyInfo;
+ _logger = logger;
+ _resultFactory = resultFactory;
+ _memoryStreamFactory = memoryStreamFactory;
}
/// <summary>
@@ -135,9 +141,32 @@ namespace MediaBrowser.WebDashboard.Api
/// <returns>System.Object.</returns>
public Task<object> Get(GetDashboardConfigurationPage request)
{
- var page = ServerEntryPoint.Instance.PluginConfigurationPages.First(p => p.Name.Equals(request.Name, StringComparison.OrdinalIgnoreCase));
+ IPlugin plugin = null;
+ Stream stream = null;
- return ResultFactory.GetStaticResult(Request, page.Plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => GetPackageCreator().ModifyHtml("dummy.html", page.GetHtmlStream(), null, _appHost.ApplicationVersion.ToString(), null, false));
+ var page = ServerEntryPoint.Instance.PluginConfigurationPages.FirstOrDefault(p => string.Equals(p.Name, request.Name, StringComparison.OrdinalIgnoreCase));
+ if (page != null)
+ {
+ plugin = page.Plugin;
+ stream = page.GetHtmlStream();
+ }
+
+ if (plugin == null)
+ {
+ var altPage = GetPluginPages().FirstOrDefault(p => string.Equals(p.Item1.Name, request.Name, StringComparison.OrdinalIgnoreCase));
+ if (altPage != null)
+ {
+ plugin = altPage.Item2;
+ stream = _assemblyInfo.GetManifestResourceStream(plugin.GetType(), altPage.Item1.EmbeddedResourcePath);
+ }
+ }
+
+ if (plugin != null && stream != null)
+ {
+ return _resultFactory.GetStaticResult(Request, plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => GetPackageCreator().ModifyHtml("dummy.html", stream, null, _appHost.ApplicationVersion.ToString(), null));
+ }
+
+ throw new ResourceNotFoundException();
}
/// <summary>
@@ -165,7 +194,7 @@ namespace MediaBrowser.WebDashboard.Api
if (request.PageType.HasValue)
{
- pages = pages.Where(p => p.ConfigurationPageType == request.PageType.Value);
+ pages = pages.Where(p => p.ConfigurationPageType == request.PageType.Value).ToList();
}
// Don't allow a failing plugin to fail them all
@@ -178,14 +207,38 @@ namespace MediaBrowser.WebDashboard.Api
}
catch (Exception ex)
{
- Logger.ErrorException("Error getting plugin information from {0}", ex, p.GetType().Name);
+ _logger.ErrorException("Error getting plugin information from {0}", ex, p.GetType().Name);
return null;
}
})
.Where(i => i != null)
.ToList();
- return ResultFactory.GetOptimizedResult(Request, configPages);
+ configPages.AddRange(_appHost.Plugins.SelectMany(GetConfigPages));
+
+ return _resultFactory.GetOptimizedResult(Request, configPages);
+ }
+
+ private IEnumerable<Tuple<PluginPageInfo, IPlugin>> GetPluginPages()
+ {
+ return _appHost.Plugins.SelectMany(GetPluginPages);
+ }
+
+ private IEnumerable<Tuple<PluginPageInfo, IPlugin>> GetPluginPages(IPlugin plugin)
+ {
+ var hasConfig = plugin as IHasWebPages;
+
+ if (hasConfig == null)
+ {
+ return new List<Tuple<PluginPageInfo, IPlugin>>();
+ }
+
+ return hasConfig.GetPages().Select(i => new Tuple<PluginPageInfo, IPlugin>(i, plugin));
+ }
+
+ private IEnumerable<ConfigurationPageInfo> GetConfigPages(IPlugin plugin)
+ {
+ return GetPluginPages(plugin).Select(i => new ConfigurationPageInfo(plugin, i.Item1));
}
public object Get(GetRobotsTxt request)
@@ -231,7 +284,7 @@ namespace MediaBrowser.WebDashboard.Api
!contentType.StartsWith("font/", StringComparison.OrdinalIgnoreCase))
{
var stream = await GetResourceStream(path, localizationCulture).ConfigureAwait(false);
- return ResultFactory.GetResult(stream, contentType);
+ return _resultFactory.GetResult(stream, contentType);
}
TimeSpan? cacheDuration = null;
@@ -243,11 +296,9 @@ namespace MediaBrowser.WebDashboard.Api
cacheDuration = TimeSpan.FromDays(365);
}
- var assembly = GetType().Assembly.GetName();
-
- var cacheKey = (assembly.Version + (localizationCulture ?? string.Empty) + path).GetMD5();
+ var cacheKey = (_appHost.ApplicationVersion + (localizationCulture ?? string.Empty) + path).GetMD5();
- return await ResultFactory.GetStaticResult(Request, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(path, localizationCulture)).ConfigureAwait(false);
+ return await _resultFactory.GetStaticResult(Request, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(path, localizationCulture)).ConfigureAwait(false);
}
private string GetLocalizationCulture()
@@ -263,15 +314,13 @@ namespace MediaBrowser.WebDashboard.Api
/// <returns>Task{Stream}.</returns>
private Task<Stream> GetResourceStream(string path, string localizationCulture)
{
- var minify = _serverConfigurationManager.Configuration.EnableDashboardResourceMinification;
-
return GetPackageCreator()
- .GetResource(path, null, localizationCulture, _appHost.ApplicationVersion.ToString(), minify);
+ .GetResource(path, null, localizationCulture, _appHost.ApplicationVersion.ToString());
}
private PackageCreator GetPackageCreator()
{
- return new PackageCreator(_fileSystem, _localization, Logger, _serverConfigurationManager, _jsonSerializer);
+ return new PackageCreator(_fileSystem, _logger, _serverConfigurationManager, _memoryStreamFactory);
}
private List<string> GetDeployIgnoreExtensions()
@@ -339,11 +388,17 @@ namespace MediaBrowser.WebDashboard.Api
// Try to trim the output size a bit
var bowerPath = Path.Combine(path, "bower_components");
- GetDeployIgnoreExtensions().ForEach(i => DeleteFilesByExtension(bowerPath, i));
+ foreach (var ext in GetDeployIgnoreExtensions())
+ {
+ DeleteFilesByExtension(bowerPath, ext);
+ }
DeleteFilesByExtension(bowerPath, ".json", "strings\\");
- GetDeployIgnoreFilenames().ForEach(i => DeleteFilesByName(bowerPath, i.Item1, i.Item2));
+ foreach (var ignore in GetDeployIgnoreFilenames())
+ {
+ DeleteFilesByName(bowerPath, ignore.Item1, ignore.Item2);
+ }
DeleteFoldersByName(bowerPath, "demo");
DeleteFoldersByName(bowerPath, "test");
@@ -442,9 +497,9 @@ namespace MediaBrowser.WebDashboard.Api
private async Task DumpHtml(string source, string destination, string mode, string culture, string appVersion)
{
- foreach (var file in Directory.GetFiles(source, "*", SearchOption.TopDirectoryOnly))
+ foreach (var file in _fileSystem.GetFiles(source))
{
- var filename = Path.GetFileName(file);
+ var filename = file.Name;
await DumpFile(filename, Path.Combine(destination, filename), mode, culture, appVersion).ConfigureAwait(false);
}
@@ -452,9 +507,9 @@ namespace MediaBrowser.WebDashboard.Api
private async Task DumpFile(string resourceVirtualPath, string destinationFilePath, string mode, string culture, string appVersion)
{
- using (var stream = await GetPackageCreator().GetResource(resourceVirtualPath, mode, culture, appVersion, false).ConfigureAwait(false))
+ using (var stream = await GetPackageCreator().GetResource(resourceVirtualPath, mode, culture, appVersion).ConfigureAwait(false))
{
- using (var fs = _fileSystem.GetFileStream(destinationFilePath, FileMode.Create, FileAccess.Write, FileShare.Read))
+ using (var fs = _fileSystem.GetFileStream(destinationFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
{
stream.CopyTo(fs);
}
@@ -466,14 +521,12 @@ namespace MediaBrowser.WebDashboard.Api
_fileSystem.CreateDirectory(destination);
//Now Create all of the directories
- foreach (string dirPath in Directory.GetDirectories(source, "*",
- SearchOption.AllDirectories))
- _fileSystem.CreateDirectory(dirPath.Replace(source, destination));
+ foreach (var dirPath in _fileSystem.GetDirectories(source, true))
+ _fileSystem.CreateDirectory(dirPath.FullName.Replace(source, destination));
//Copy all the files & Replaces any files with the same name
- foreach (string newPath in Directory.GetFiles(source, "*.*",
- SearchOption.AllDirectories))
- _fileSystem.CopyFile(newPath, newPath.Replace(source, destination), true);
+ foreach (var newPath in _fileSystem.GetFiles(source, true))
+ _fileSystem.CopyFile(newPath.FullName, newPath.FullName.Replace(source, destination), true);
}
}
diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs
index eaac3e2a0e..f2df019769 100644
--- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs
+++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs
@@ -1,5 +1,4 @@
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Localization;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using System;
@@ -8,41 +7,37 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using CommonIO;
using MediaBrowser.Controller.Net;
-using WebMarkupMin.Core;
+using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.WebDashboard.Api
{
public class PackageCreator
{
private readonly IFileSystem _fileSystem;
- private readonly ILocalizationManager _localization;
private readonly ILogger _logger;
private readonly IServerConfigurationManager _config;
- private readonly IJsonSerializer _jsonSerializer;
+ private readonly IMemoryStreamFactory _memoryStreamFactory;
- public PackageCreator(IFileSystem fileSystem, ILocalizationManager localization, ILogger logger, IServerConfigurationManager config, IJsonSerializer jsonSerializer)
+ public PackageCreator(IFileSystem fileSystem, ILogger logger, IServerConfigurationManager config, IMemoryStreamFactory memoryStreamFactory)
{
_fileSystem = fileSystem;
- _localization = localization;
_logger = logger;
_config = config;
- _jsonSerializer = jsonSerializer;
+ _memoryStreamFactory = memoryStreamFactory;
}
public async Task<Stream> GetResource(string path,
string mode,
string localizationCulture,
- string appVersion,
- bool enableMinification)
+ string appVersion)
{
Stream resourceStream;
if (path.Equals("css/all.css", StringComparison.OrdinalIgnoreCase))
{
- resourceStream = await GetAllCss(enableMinification).ConfigureAwait(false);
- enableMinification = false;
+ resourceStream = await GetAllCss().ConfigureAwait(false);
}
else
{
@@ -57,21 +52,7 @@ namespace MediaBrowser.WebDashboard.Api
{
if (IsCoreHtml(path))
{
- resourceStream = await ModifyHtml(path, resourceStream, mode, appVersion, localizationCulture, enableMinification).ConfigureAwait(false);
- }
- }
- else if (IsFormat(path, "js"))
- {
- if (path.IndexOf(".min.", StringComparison.OrdinalIgnoreCase) == -1 && path.IndexOf("bower_components", StringComparison.OrdinalIgnoreCase) == -1)
- {
- resourceStream = await ModifyJs(resourceStream, enableMinification).ConfigureAwait(false);
- }
- }
- else if (IsFormat(path, "css"))
- {
- if (path.IndexOf(".min.", StringComparison.OrdinalIgnoreCase) == -1 && path.IndexOf("bower_components", StringComparison.OrdinalIgnoreCase) == -1)
- {
- resourceStream = await ModifyCss(resourceStream, enableMinification).ConfigureAwait(false);
+ resourceStream = await ModifyHtml(path, resourceStream, mode, appVersion, localizationCulture).ConfigureAwait(false);
}
}
}
@@ -116,11 +97,11 @@ namespace MediaBrowser.WebDashboard.Api
{
var rootPath = DashboardUIPath;
- var fullPath = Path.Combine(rootPath, virtualPath.Replace('/', Path.DirectorySeparatorChar));
+ var fullPath = Path.Combine(rootPath, virtualPath.Replace('/', _fileSystem.DirectorySeparatorChar));
try
{
- fullPath = Path.GetFullPath(fullPath);
+ fullPath = _fileSystem.GetFullPath(fullPath);
}
catch (Exception ex)
{
@@ -136,86 +117,6 @@ namespace MediaBrowser.WebDashboard.Api
return fullPath;
}
- public async Task<Stream> ModifyCss(Stream sourceStream, bool enableMinification)
- {
- using (sourceStream)
- {
- string content;
-
- using (var memoryStream = new MemoryStream())
- {
- await sourceStream.CopyToAsync(memoryStream).ConfigureAwait(false);
-
- content = Encoding.UTF8.GetString(memoryStream.ToArray());
-
- if (enableMinification)
- {
- try
- {
- var result = new KristensenCssMinifier().Minify(content, false, Encoding.UTF8);
-
- if (result.Errors.Count > 0)
- {
- _logger.Error("Error minifying css: " + result.Errors[0].Message);
- }
- else
- {
- content = result.MinifiedContent;
- }
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error minifying css", ex);
- }
- }
- }
-
- var bytes = Encoding.UTF8.GetBytes(content);
-
- return new MemoryStream(bytes);
- }
- }
-
- public async Task<Stream> ModifyJs(Stream sourceStream, bool enableMinification)
- {
- using (sourceStream)
- {
- string content;
-
- using (var memoryStream = new MemoryStream())
- {
- await sourceStream.CopyToAsync(memoryStream).ConfigureAwait(false);
-
- content = Encoding.UTF8.GetString(memoryStream.ToArray());
-
- if (enableMinification)
- {
- try
- {
- var result = new CrockfordJsMinifier().Minify(content, false, Encoding.UTF8);
-
- if (result.Errors.Count > 0)
- {
- _logger.Error("Error minifying javascript: " + result.Errors[0].Message);
- }
- else
- {
- content = result.MinifiedContent;
- }
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error minifying javascript", ex);
- }
- }
- }
-
- var bytes = Encoding.UTF8.GetBytes(content);
-
- return new MemoryStream(bytes);
- }
- }
-
public bool IsCoreHtml(string path)
{
if (path.IndexOf(".template.html", StringComparison.OrdinalIgnoreCase) != -1)
@@ -235,24 +136,20 @@ namespace MediaBrowser.WebDashboard.Api
/// <summary>
/// Modifies the HTML by adding common meta tags, css and js.
/// </summary>
- /// <param name="path">The path.</param>
- /// <param name="sourceStream">The source stream.</param>
- /// <param name="mode">The mode.</param>
- /// <param name="appVersion">The application version.</param>
- /// <param name="localizationCulture">The localization culture.</param>
- /// <param name="enableMinification">if set to <c>true</c> [enable minification].</param>
/// <returns>Task{Stream}.</returns>
- public async Task<Stream> ModifyHtml(string path, Stream sourceStream, string mode, string appVersion, string localizationCulture, bool enableMinification)
+ public async Task<Stream> ModifyHtml(string path, Stream sourceStream, string mode, string appVersion, string localizationCulture)
{
using (sourceStream)
{
string html;
- using (var memoryStream = new MemoryStream())
+ using (var memoryStream = _memoryStreamFactory.CreateNew())
{
await sourceStream.CopyToAsync(memoryStream).ConfigureAwait(false);
- html = Encoding.UTF8.GetString(memoryStream.ToArray());
+ var originalBytes = memoryStream.ToArray();
+
+ html = Encoding.UTF8.GetString(originalBytes, 0, originalBytes.Length);
if (string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase))
{
@@ -269,7 +166,7 @@ namespace MediaBrowser.WebDashboard.Api
html = html.Substring(0, index+7);
}
}
- var mainFile = File.ReadAllText(GetDashboardResourcePath("index.html"));
+ var mainFile = _fileSystem.ReadAllText(GetDashboardResourcePath("index.html"));
html = ReplaceFirst(mainFile, "<div class=\"mainAnimatedPages skinBody\"></div>", "<div class=\"mainAnimatedPages skinBody hide\">" + html + "</div>");
}
@@ -280,33 +177,6 @@ namespace MediaBrowser.WebDashboard.Api
html = html.Replace("<html", "<html data-culture=\"" + localizationCulture + "\" lang=\"" + lang + "\"");
}
-
- if (enableMinification)
- {
- try
- {
- var minifier = new HtmlMinifier(new HtmlMinificationSettings
- {
- AttributeQuotesRemovalMode = HtmlAttributeQuotesRemovalMode.KeepQuotes,
- RemoveOptionalEndTags = false,
- RemoveTagsWithoutContent = false
- });
- var result = minifier.Minify(html, false);
-
- if (result.Errors.Count > 0)
- {
- _logger.Error("Error minifying html: " + result.Errors[0].Message);
- }
- else
- {
- html = result.MinifiedContent;
- }
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error minifying html", ex);
- }
- }
}
html = html.Replace("<head>", "<head>" + GetMetaTags(mode) + GetCommonCss(mode, appVersion));
@@ -322,7 +192,7 @@ namespace MediaBrowser.WebDashboard.Api
var bytes = Encoding.UTF8.GetBytes(html);
- return new MemoryStream(bytes);
+ return _memoryStreamFactory.CreateNew(bytes);
}
}
@@ -452,9 +322,9 @@ namespace MediaBrowser.WebDashboard.Api
/// Gets all CSS.
/// </summary>
/// <returns>Task{Stream}.</returns>
- private async Task<Stream> GetAllCss(bool enableMinification)
+ private async Task<Stream> GetAllCss()
{
- var memoryStream = new MemoryStream();
+ var memoryStream = _memoryStreamFactory.CreateNew();
var files = new[]
{
@@ -470,7 +340,7 @@ namespace MediaBrowser.WebDashboard.Api
{
var path = GetDashboardResourcePath(file);
- using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
+ using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, true))
{
using (var streamReader = new StreamReader(fs))
{
@@ -483,27 +353,6 @@ namespace MediaBrowser.WebDashboard.Api
var css = builder.ToString();
- if (enableMinification)
- {
- try
- {
- var result = new KristensenCssMinifier().Minify(builder.ToString(), false, Encoding.UTF8);
-
- if (result.Errors.Count > 0)
- {
- _logger.Error("Error minifying css: " + result.Errors[0].Message);
- }
- else
- {
- css = result.MinifiedContent;
- }
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error minifying css", ex);
- }
- }
-
var bytes = Encoding.UTF8.GetBytes(css);
memoryStream.Write(bytes, 0, bytes.Length);
@@ -518,7 +367,7 @@ namespace MediaBrowser.WebDashboard.Api
/// <returns>Task{Stream}.</returns>
private Stream GetRawResourceStream(string path)
{
- return _fileSystem.GetFileStream(GetDashboardResourcePath(path), FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true);
+ return _fileSystem.GetFileStream(GetDashboardResourcePath(path), FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, true);
}
}
diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
index a561a48afc..8678925675 100644
--- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
+++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<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>
@@ -13,6 +13,8 @@
<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' ">
@@ -23,7 +25,6 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
@@ -32,7 +33,6 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release Mono|AnyCPU' ">
<DebugType>none</DebugType>
@@ -41,34 +41,11 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
</PropertyGroup>
<ItemGroup>
- <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
- </Reference>
- <Reference Include="Patterns.Logging">
- <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
- </Reference>
- <Reference Include="System" />
- <Reference Include="System.Core" />
- <Reference Include="System.Data.DataSetExtensions" />
- <Reference Include="Microsoft.CSharp" />
- <Reference Include="System.Data" />
- <Reference Include="System.Xml" />
- <Reference Include="ServiceStack.Interfaces">
- <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath>
- </Reference>
- <Reference Include="WebMarkupMin.Core, Version=2.1.0.0, Culture=neutral, PublicKeyToken=99472178d266584b, processorArchitecture=MSIL">
- <HintPath>..\packages\WebMarkupMin.Core.2.1.0\lib\net40-client\WebMarkupMin.Core.dll</HintPath>
- <Private>True</Private>
- </Reference>
- </ItemGroup>
- <ItemGroup>
<Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link>
</Compile>
@@ -203,6 +180,9 @@
<Content Include="dashboard-ui\scripts\livetvschedule.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\scripts\livetvseriestimers.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\scripts\userpasswordpage.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -716,9 +696,6 @@
<Content Include="dashboard-ui\files\dummy.mp4">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\librarypathmapping.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\metadatanfo.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -740,9 +717,6 @@
<Content Include="dashboard-ui\reports.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\livetvseriestimer.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\livetvsettings.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -899,9 +873,6 @@
<Content Include="dashboard-ui\scripts\librarymenu.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\scripts\librarypathmapping.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\scripts\metadatanfo.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -926,9 +897,6 @@
<Content Include="dashboard-ui\scripts\livetvchannel.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\scripts\livetvseriestimer.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\scripts\livetvsettings.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -971,9 +939,6 @@
<Content Include="dashboard-ui\scripts\episodes.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\scripts\livetvseriestimers.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\scripts\livetvsuggested.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1429,9 +1394,6 @@
<Content Include="dashboard-ui\css\images\clients\dlna.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\about.html">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\scripts\mediaplayer.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1575,7 +1537,7 @@
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
- <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>
</PostBuildEvent>
diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.nuget.targets b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.nuget.targets
new file mode 100644
index 0000000000..e69ce0e64f
--- /dev/null
+++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.nuget.targets
@@ -0,0 +1,6 @@
+<?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 690c07d8ff..b939e41070 100644
--- a/MediaBrowser.WebDashboard/ServerEntryPoint.cs
+++ b/MediaBrowser.WebDashboard/ServerEntryPoint.cs
@@ -1,6 +1,7 @@
using MediaBrowser.Common;
using MediaBrowser.Controller.Plugins;
using System.Collections.Generic;
+using System.Linq;
namespace MediaBrowser.WebDashboard
{
@@ -10,7 +11,7 @@ namespace MediaBrowser.WebDashboard
/// Gets the list of plugin configuration pages
/// </summary>
/// <value>The configuration pages.</value>
- public IEnumerable<IPluginConfigurationPage> PluginConfigurationPages { get; private set; }
+ public List<IPluginConfigurationPage> PluginConfigurationPages { get; private set; }
private readonly IApplicationHost _appHost;
@@ -24,7 +25,7 @@ namespace MediaBrowser.WebDashboard
public void Run()
{
- PluginConfigurationPages = _appHost.GetExports<IPluginConfigurationPage>();
+ PluginConfigurationPages = _appHost.GetExports<IPluginConfigurationPage>().ToList();
}
public void Dispose()
diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config
index 3637c6c84e..6b8deb9c96 100644
--- a/MediaBrowser.WebDashboard/packages.config
+++ b/MediaBrowser.WebDashboard/packages.config
@@ -1,6 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="CommonIO" version="1.0.0.9" targetFramework="net45" />
- <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
- <package id="WebMarkupMin.Core" version="2.1.0" targetFramework="net45" />
</packages> \ No newline at end of file
diff --git a/MediaBrowser.WebDashboard/project.json b/MediaBrowser.WebDashboard/project.json
new file mode 100644
index 0000000000..fbbe9eaf32
--- /dev/null
+++ b/MediaBrowser.WebDashboard/project.json
@@ -0,0 +1,17 @@
+{
+ "frameworks":{
+ "netstandard1.6":{
+ "dependencies":{
+ "NETStandard.Library":"1.6.0",
+ }
+ },
+ ".NETPortable,Version=v4.5,Profile=Profile7":{
+ "buildOptions": {
+ "define": [ ]
+ },
+ "frameworkAssemblies":{
+
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.XbmcMetadata/EntryPoint.cs b/MediaBrowser.XbmcMetadata/EntryPoint.cs
index bf3d3c3039..cd18e56706 100644
--- a/MediaBrowser.XbmcMetadata/EntryPoint.cs
+++ b/MediaBrowser.XbmcMetadata/EntryPoint.cs
@@ -52,7 +52,7 @@ namespace MediaBrowser.XbmcMetadata
var items = _libraryManager.GetItemList(new InternalItemsQuery
{
- Person = person.Name
+ PersonIds = new [] { person.Id.ToString("N") }
}).ToList();
diff --git a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj
index f3147a0653..e84fa8e653 100644
--- a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj
+++ b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<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>
@@ -9,9 +9,11 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MediaBrowser.XbmcMetadata</RootNamespace>
<AssemblyName>MediaBrowser.XbmcMetadata</AssemblyName>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<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>
@@ -31,22 +33,6 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
- <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
- </Reference>
- <Reference Include="Patterns.Logging">
- <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.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.Xml" />
- </ItemGroup>
- <ItemGroup>
<Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link>
</Compile>
@@ -91,7 +77,7 @@
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
- <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <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">
diff --git a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.nuget.targets b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.nuget.targets
new file mode 100644
index 0000000000..e69ce0e64f
--- /dev/null
+++ b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.nuget.targets
@@ -0,0 +1,6 @@
+<?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 59f6e87220..dba6d96ac4 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
@@ -15,6 +15,8 @@ using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.XbmcMetadata.Parsers
{
@@ -25,7 +27,9 @@ namespace MediaBrowser.XbmcMetadata.Parsers
/// The logger
/// </summary>
protected ILogger Logger { get; private set; }
+ protected IFileSystem FileSystem { get; private set; }
protected IProviderManager ProviderManager { get; private set; }
+ protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; }
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly IConfigurationManager _config;
@@ -34,13 +38,13 @@ namespace MediaBrowser.XbmcMetadata.Parsers
/// <summary>
/// Initializes a new instance of the <see cref="BaseNfoParser{T}" /> class.
/// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="config">The configuration.</param>
- public BaseNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager)
+ public BaseNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
{
Logger = logger;
_config = config;
ProviderManager = providerManager;
+ FileSystem = fileSystem;
+ XmlReaderSettingsFactory = xmlReaderSettingsFactory;
}
/// <summary>
@@ -63,15 +67,13 @@ namespace MediaBrowser.XbmcMetadata.Parsers
throw new ArgumentNullException();
}
- var settings = new XmlReaderSettings
- {
- CheckCharacters = false,
- IgnoreProcessingInstructions = true,
- IgnoreComments = true,
- ValidationType = ValidationType.None
- };
+ var settings = XmlReaderSettingsFactory.Create(false);
- _validProviderIds = _validProviderIds = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
+
+ _validProviderIds = _validProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var idInfos = ProviderManager.GetExternalIdInfos(item.Item);
@@ -108,23 +110,31 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
if (!SupportsUrlAfterClosingXmlTag)
{
- using (var streamReader = BaseNfoSaver.GetStreamReader(metadataFile))
+ using (var fileStream = FileSystem.OpenRead(metadataFile))
{
- // Use XmlReader for best performance
- using (var reader = XmlReader.Create(streamReader, settings))
+ using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
{
- item.ResetPeople();
-
- reader.MoveToContent();
-
- // Loop through each element
- while (reader.Read())
+ // Use XmlReader for best performance
+ using (var reader = XmlReader.Create(streamReader, settings))
{
- cancellationToken.ThrowIfCancellationRequested();
+ item.ResetPeople();
+
+ reader.MoveToContent();
+ reader.Read();
- if (reader.NodeType == XmlNodeType.Element)
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
- FetchDataFromXmlNode(reader, item);
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ FetchDataFromXmlNode(reader, item);
+ }
+ else
+ {
+ reader.Read();
+ }
}
}
}
@@ -132,78 +142,86 @@ namespace MediaBrowser.XbmcMetadata.Parsers
return;
}
- using (var streamReader = BaseNfoSaver.GetStreamReader(metadataFile))
+ using (var fileStream = FileSystem.OpenRead(metadataFile))
{
- item.ResetPeople();
-
- // Need to handle a url after the xml data
- // http://kodi.wiki/view/NFO_files/movies
-
- var xml = streamReader.ReadToEnd();
-
- // Find last closing Tag
- // Need to do this in two steps to account for random > characters after the closing xml
- var index = xml.LastIndexOf(@"</", StringComparison.Ordinal);
-
- // If closing tag exists, move to end of Tag
- if (index != -1)
+ using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
{
- index = xml.IndexOf('>', index);
- }
+ item.ResetPeople();
- if (index != -1)
- {
- var endingXml = xml.Substring(index);
+ // Need to handle a url after the xml data
+ // http://kodi.wiki/view/NFO_files/movies
+
+ var xml = streamReader.ReadToEnd();
- ParseProviderLinks(item.Item, endingXml);
+ // Find last closing Tag
+ // Need to do this in two steps to account for random > characters after the closing xml
+ var index = xml.LastIndexOf(@"</", StringComparison.Ordinal);
- // If the file is just an imdb url, don't go any further
- if (index == 0)
+ // If closing tag exists, move to end of Tag
+ if (index != -1)
{
- return;
+ index = xml.IndexOf('>', index);
}
- xml = xml.Substring(0, index + 1);
- }
- else
- {
- // If the file is just an Imdb url, handle that
+ if (index != -1)
+ {
+ var endingXml = xml.Substring(index);
- ParseProviderLinks(item.Item, xml);
+ ParseProviderLinks(item.Item, endingXml);
- return;
- }
+ // If the file is just an imdb url, don't go any further
+ if (index == 0)
+ {
+ return;
+ }
- using (var ms = new MemoryStream())
- {
- var bytes = Encoding.UTF8.GetBytes(xml);
+ xml = xml.Substring(0, index + 1);
+ }
+ else
+ {
+ // If the file is just an Imdb url, handle that
+
+ ParseProviderLinks(item.Item, xml);
- ms.Write(bytes, 0, bytes.Length);
- ms.Position = 0;
+ return;
+ }
- // 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 ms = new MemoryStream())
{
- // Use XmlReader for best performance
- using (var reader = XmlReader.Create(ms, settings))
- {
- reader.MoveToContent();
+ var bytes = Encoding.UTF8.GetBytes(xml);
- // Loop through each element
- while (reader.Read())
+ 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
+ {
+ // Use XmlReader for best performance
+ using (var reader = XmlReader.Create(ms, settings))
{
- cancellationToken.ThrowIfCancellationRequested();
+ reader.MoveToContent();
+ reader.Read();
- if (reader.NodeType == XmlNodeType.Element)
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
- FetchDataFromXmlNode(reader, item);
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ FetchDataFromXmlNode(reader, item);
+ }
+ else
+ {
+ reader.Read();
+ }
}
}
}
- }
- catch (XmlException)
- {
+ catch (XmlException)
+ {
+ }
}
}
}
@@ -270,13 +288,9 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
var val = reader.ReadElementContentAsString();
- var hasOriginalTitle = item as IHasOriginalTitle;
- if (hasOriginalTitle != null)
+ if (!string.IsNullOrEmpty(val))
{
- if (!string.IsNullOrEmpty(hasOriginalTitle.OriginalTitle))
- {
- hasOriginalTitle.OriginalTitle = val;
- }
+ item.OriginalTitle = val;
}
break;
}
@@ -578,21 +592,23 @@ namespace MediaBrowser.XbmcMetadata.Parsers
if (!string.IsNullOrWhiteSpace(val))
{
- var parts = val.Split('/')
- .Select(i => i.Trim())
- .Where(i => !string.IsNullOrWhiteSpace(i));
-
- foreach (var p in parts)
- {
- item.AddStudio(p);
- }
+ //var parts = val.Split('/')
+ // .Select(i => i.Trim())
+ // .Where(i => !string.IsNullOrWhiteSpace(i));
+
+ //foreach (var p in parts)
+ //{
+ // item.AddStudio(p);
+ //}
+ item.AddStudio(val);
}
break;
}
case "director":
{
- foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonType.Director }))
+ var val = reader.ReadElementContentAsString();
+ foreach (var p in SplitNames(val).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonType.Director }))
{
if (string.IsNullOrWhiteSpace(p.Name))
{
@@ -625,7 +641,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers
case "writer":
{
- foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonType.Writer }))
+ var val = reader.ReadElementContentAsString();
+ foreach (var p in SplitNames(val).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonType.Writer }))
{
if (string.IsNullOrWhiteSpace(p.Name))
{
@@ -638,15 +655,22 @@ namespace MediaBrowser.XbmcMetadata.Parsers
case "actor":
{
- using (var subtree = reader.ReadSubtree())
+ if (!reader.IsEmptyElement)
{
- var person = GetPersonFromXmlNode(subtree);
-
- if (!string.IsNullOrWhiteSpace(person.Name))
+ using (var subtree = reader.ReadSubtree())
{
- itemResult.AddPerson(person);
+ var person = GetPersonFromXmlNode(subtree);
+
+ if (!string.IsNullOrWhiteSpace(person.Name))
+ {
+ itemResult.AddPerson(person);
+ }
}
}
+ else
+ {
+ reader.Read();
+ }
break;
}
@@ -814,9 +838,16 @@ namespace MediaBrowser.XbmcMetadata.Parsers
case "fileinfo":
{
- using (var subtree = reader.ReadSubtree())
+ if (!reader.IsEmptyElement)
{
- FetchFromFileInfoNode(subtree, item);
+ using (var subtree = reader.ReadSubtree())
+ {
+ FetchFromFileInfoNode(subtree, item);
+ }
+ }
+ else
+ {
+ reader.Read();
}
break;
}
@@ -879,15 +910,22 @@ namespace MediaBrowser.XbmcMetadata.Parsers
case "resume":
{
- using (var subtree = reader.ReadSubtree())
+ if (!reader.IsEmptyElement)
{
- if (!string.IsNullOrWhiteSpace(userDataUserId))
+ using (var subtree = reader.ReadSubtree())
{
- var userData = GetOrAdd(itemResult, userDataUserId);
+ if (!string.IsNullOrWhiteSpace(userDataUserId))
+ {
+ var userData = GetOrAdd(itemResult, userDataUserId);
- FetchFromResumeNode(subtree, item, userData);
+ FetchFromResumeNode(subtree, item, userData);
+ }
}
}
+ else
+ {
+ reader.Read();
+ }
break;
}
@@ -939,8 +977,10 @@ namespace MediaBrowser.XbmcMetadata.Parsers
private void FetchFromResumeNode(XmlReader reader, T item, UserItemData userData)
{
reader.MoveToContent();
+ reader.Read();
- while (reader.Read())
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -966,14 +1006,20 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
}
private void FetchFromFileInfoNode(XmlReader reader, T item)
{
reader.MoveToContent();
+ reader.Read();
- while (reader.Read())
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -981,6 +1027,11 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
case "streamdetails":
{
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ continue;
+ }
using (var subtree = reader.ReadSubtree())
{
FetchFromStreamDetailsNode(subtree, item);
@@ -993,14 +1044,20 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
}
private void FetchFromStreamDetailsNode(XmlReader reader, T item)
{
reader.MoveToContent();
+ reader.Read();
- while (reader.Read())
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -1008,6 +1065,11 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
case "video":
{
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ continue;
+ }
using (var subtree = reader.ReadSubtree())
{
FetchFromVideoNode(subtree, item);
@@ -1020,14 +1082,20 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
}
private void FetchFromVideoNode(XmlReader reader, T item)
{
reader.MoveToContent();
+ reader.Read();
- while (reader.Read())
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -1035,12 +1103,12 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
case "format3d":
{
+ var val = reader.ReadElementContentAsString();
+
var video = item as Video;
if (video != null)
{
- var val = reader.ReadElementContentAsString();
-
if (string.Equals("HSBS", val, StringComparison.OrdinalIgnoreCase))
{
video.Video3DFormat = Video3DFormat.HalfSideBySide;
@@ -1070,6 +1138,10 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
}
@@ -1086,8 +1158,10 @@ namespace MediaBrowser.XbmcMetadata.Parsers
int? sortOrder = null;
reader.MoveToContent();
+ reader.Read();
- while (reader.Read())
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
@@ -1138,6 +1212,10 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
}
+ else
+ {
+ reader.Read();
+ }
}
return new PersonInfo
diff --git a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs
index a5a86fc581..d10a55e07c 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs
@@ -7,15 +7,13 @@ using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using System.Xml;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.XbmcMetadata.Parsers
{
public class EpisodeNfoParser : BaseNfoParser<Episode>
{
- public EpisodeNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(logger, config, providerManager)
- {
- }
-
public void Fetch(MetadataResult<Episode> item,
List<LocalImageInfo> images,
string metadataFile,
@@ -230,5 +228,9 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
}
+
+ public EpisodeNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(logger, config, providerManager, fileSystem, xmlReaderSettingsFactory)
+ {
+ }
}
}
diff --git a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
index 3e6196238c..035ac15c0b 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs
@@ -5,16 +5,13 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using System.Xml;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.XbmcMetadata.Parsers
{
class MovieNfoParser : BaseNfoParser<Video>
{
- public MovieNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager)
- : base(logger, config, providerManager)
- {
- }
-
protected override bool SupportsUrlAfterClosingXmlTag
{
get
@@ -103,5 +100,9 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
}
+
+ public MovieNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(logger, config, providerManager, fileSystem, xmlReaderSettingsFactory)
+ {
+ }
}
}
diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs
index c051ad4f81..ef1b8ebb27 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs
@@ -4,15 +4,13 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System.Globalization;
using System.Xml;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.XbmcMetadata.Parsers
{
public class SeasonNfoParser : BaseNfoParser<Season>
{
- public SeasonNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(logger, config, providerManager)
- {
- }
-
/// <summary>
/// Fetches the data from XML node.
/// </summary>
@@ -45,5 +43,9 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
}
+
+ public SeasonNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(logger, config, providerManager, fileSystem, xmlReaderSettingsFactory)
+ {
+ }
}
}
diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
index 8b7deebf23..57238ef87c 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
@@ -6,15 +6,13 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using System;
using System.Xml;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.XbmcMetadata.Parsers
{
public class SeriesNfoParser : BaseNfoParser<Series>
{
- public SeriesNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(logger, config, providerManager)
- {
- }
-
/// <summary>
/// Fetches the data from XML node.
/// </summary>
@@ -108,5 +106,9 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
}
+
+ public SeriesNfoParser(ILogger logger, IConfigurationManager config, IProviderManager providerManager, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(logger, config, providerManager, fileSystem, xmlReaderSettingsFactory)
+ {
+ }
}
}
diff --git a/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs
index a90cb20fdc..c80ebda45a 100644
--- a/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/AlbumNfoProvider.cs
@@ -5,7 +5,10 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.XbmcMetadata.Parsers;
using System.IO;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.XbmcMetadata.Providers
{
@@ -14,18 +17,20 @@ namespace MediaBrowser.XbmcMetadata.Providers
private readonly ILogger _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
+ protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; }
- public AlbumNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager)
+ public AlbumNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
: base(fileSystem)
{
_logger = logger;
_config = config;
_providerManager = providerManager;
+ XmlReaderSettingsFactory = xmlReaderSettingsFactory;
}
protected override void Fetch(MetadataResult<MusicAlbum> result, string path, CancellationToken cancellationToken)
{
- new BaseNfoParser<MusicAlbum>(_logger, _config, _providerManager).Fetch(result, path, cancellationToken);
+ new BaseNfoParser<MusicAlbum>(_logger, _config, _providerManager, FileSystem, XmlReaderSettingsFactory).Fetch(result, path, cancellationToken);
}
protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)
diff --git a/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs
index 391eb459ac..119dcf2f33 100644
--- a/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/ArtistNfoProvider.cs
@@ -5,7 +5,10 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.XbmcMetadata.Parsers;
using System.IO;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.XbmcMetadata.Providers
{
@@ -14,18 +17,20 @@ namespace MediaBrowser.XbmcMetadata.Providers
private readonly ILogger _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
+ protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; }
- public ArtistNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager)
+ public ArtistNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
: base(fileSystem)
{
_logger = logger;
_config = config;
_providerManager = providerManager;
+ XmlReaderSettingsFactory = xmlReaderSettingsFactory;
}
protected override void Fetch(MetadataResult<MusicArtist> result, string path, CancellationToken cancellationToken)
{
- new BaseNfoParser<MusicArtist>(_logger, _config, _providerManager).Fetch(result, path, cancellationToken);
+ new BaseNfoParser<MusicArtist>(_logger, _config, _providerManager, FileSystem, XmlReaderSettingsFactory).Fetch(result, path, cancellationToken);
}
protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)
diff --git a/MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs
index f3c40a1696..7e37d61df4 100644
--- a/MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/BaseNfoProvider.cs
@@ -4,7 +4,9 @@ using MediaBrowser.XbmcMetadata.Savers;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.XbmcMetadata.Providers
{
@@ -39,7 +41,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
{
result.HasMetadata = false;
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
result.HasMetadata = false;
}
diff --git a/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs
index 0a268e8e27..ff798c0f29 100644
--- a/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs
@@ -6,7 +6,10 @@ using MediaBrowser.XbmcMetadata.Parsers;
using MediaBrowser.XbmcMetadata.Savers;
using System.Linq;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.XbmcMetadata.Providers
{
@@ -16,13 +19,15 @@ namespace MediaBrowser.XbmcMetadata.Providers
private readonly ILogger _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
+ protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; }
- public BaseVideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager)
+ public BaseVideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
: base(fileSystem)
{
_logger = logger;
_config = config;
_providerManager = providerManager;
+ XmlReaderSettingsFactory = xmlReaderSettingsFactory;
}
protected override void Fetch(MetadataResult<T> result, string path, CancellationToken cancellationToken)
@@ -31,7 +36,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
{
Item = result.Item
};
- new MovieNfoParser(_logger, _config, _providerManager).Fetch(tmpItem, path, cancellationToken);
+ new MovieNfoParser(_logger, _config, _providerManager, FileSystem, XmlReaderSettingsFactory).Fetch(tmpItem, path, cancellationToken);
result.Item = (T)tmpItem.Item;
result.People = tmpItem.People;
diff --git a/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs
index d8f3f1a21e..9deaa9745d 100644
--- a/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/EpisodeNfoProvider.cs
@@ -6,7 +6,10 @@ using MediaBrowser.XbmcMetadata.Parsers;
using System.Collections.Generic;
using System.IO;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.XbmcMetadata.Providers
{
@@ -15,20 +18,22 @@ namespace MediaBrowser.XbmcMetadata.Providers
private readonly ILogger _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
+ protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; }
- public EpisodeNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager)
+ public EpisodeNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
: base(fileSystem)
{
_logger = logger;
_config = config;
_providerManager = providerManager;
+ XmlReaderSettingsFactory = xmlReaderSettingsFactory;
}
protected override void Fetch(MetadataResult<Episode> result, string path, CancellationToken cancellationToken)
{
var images = new List<LocalImageInfo>();
- new EpisodeNfoParser(_logger, _config, _providerManager).Fetch(result, images, path, cancellationToken);
+ new EpisodeNfoParser(_logger, _config, _providerManager, FileSystem, XmlReaderSettingsFactory).Fetch(result, images, path, cancellationToken);
result.Images = images;
}
diff --git a/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs
index 5a759f81e5..75d85b2956 100644
--- a/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/MovieNfoProvider.cs
@@ -1,29 +1,32 @@
-using CommonIO;
-using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.XbmcMetadata.Providers
{
public class MovieNfoProvider : BaseVideoNfoProvider<Movie>
{
- public MovieNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(fileSystem, logger, config, providerManager)
+ public MovieNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(fileSystem, logger, config, providerManager, xmlReaderSettingsFactory)
{
}
}
public class MusicVideoNfoProvider : BaseVideoNfoProvider<MusicVideo>
{
- public MusicVideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(fileSystem, logger, config, providerManager)
+ public MusicVideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(fileSystem, logger, config, providerManager, xmlReaderSettingsFactory)
{
}
}
public class VideoNfoProvider : BaseVideoNfoProvider<Video>
{
- public VideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager) : base(fileSystem, logger, config, providerManager)
+ public VideoNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(fileSystem, logger, config, providerManager, xmlReaderSettingsFactory)
{
}
}
diff --git a/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs
index 7e116b2c5d..ccccca98ef 100644
--- a/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/SeasonNfoProvider.cs
@@ -5,7 +5,10 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.XbmcMetadata.Parsers;
using System.IO;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.XbmcMetadata.Providers
{
@@ -14,18 +17,20 @@ namespace MediaBrowser.XbmcMetadata.Providers
private readonly ILogger _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
+ protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; }
- public SeasonNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager)
+ public SeasonNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
: base(fileSystem)
{
_logger = logger;
_config = config;
_providerManager = providerManager;
+ XmlReaderSettingsFactory = xmlReaderSettingsFactory;
}
protected override void Fetch(MetadataResult<Season> result, string path, CancellationToken cancellationToken)
{
- new SeasonNfoParser(_logger, _config, _providerManager).Fetch(result, path, cancellationToken);
+ new SeasonNfoParser(_logger, _config, _providerManager, FileSystem, XmlReaderSettingsFactory).Fetch(result, path, cancellationToken);
}
protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)
diff --git a/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs
index f03c209545..e144142aa8 100644
--- a/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs
+++ b/MediaBrowser.XbmcMetadata/Providers/SeriesNfoProvider.cs
@@ -5,7 +5,10 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.XbmcMetadata.Parsers;
using System.IO;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.XbmcMetadata.Providers
{
@@ -14,18 +17,22 @@ namespace MediaBrowser.XbmcMetadata.Providers
private readonly ILogger _logger;
private readonly IConfigurationManager _config;
private readonly IProviderManager _providerManager;
+ private readonly IFileSystem _fileSystem;
+ protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; }
- public SeriesNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager)
+ public SeriesNfoProvider(IFileSystem fileSystem, ILogger logger, IConfigurationManager config, IProviderManager providerManager, IFileSystem fileSystem1, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
: base(fileSystem)
{
_logger = logger;
_config = config;
_providerManager = providerManager;
+ _fileSystem = fileSystem1;
+ XmlReaderSettingsFactory = xmlReaderSettingsFactory;
}
protected override void Fetch(MetadataResult<Series> result, string path, CancellationToken cancellationToken)
{
- new SeriesNfoParser(_logger, _config, _providerManager).Fetch(result, path, cancellationToken);
+ new SeriesNfoParser(_logger, _config, _providerManager, _fileSystem, XmlReaderSettingsFactory).Fetch(result, path, cancellationToken);
}
protected override FileSystemMetadata GetXmlFile(ItemInfo info, IDirectoryService directoryService)
diff --git a/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs
index 7f7606f8eb..83539bbf44 100644
--- a/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs
@@ -9,16 +9,15 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.XbmcMetadata.Savers
{
public class AlbumNfoSaver : BaseNfoSaver
{
- public AlbumNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
- {
- }
-
protected override string GetLocalSavePath(IHasMetadata item)
{
return Path.Combine(item.Path, "album.nfo");
@@ -97,5 +96,9 @@ namespace MediaBrowser.XbmcMetadata.Savers
return list;
}
+
+ public AlbumNfoSaver(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.XbmcMetadata/Savers/ArtistNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs
index f0f9df4348..14a56c33a5 100644
--- a/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs
@@ -9,16 +9,15 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.XbmcMetadata.Savers
{
public class ArtistNfoSaver : BaseNfoSaver
{
- public ArtistNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
- {
- }
-
protected override string GetLocalSavePath(IHasMetadata item)
{
return Path.Combine(item.Path, "artist.nfo");
@@ -90,5 +89,9 @@ namespace MediaBrowser.XbmcMetadata.Savers
return list;
}
+
+ public ArtistNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger, xmlReaderSettingsFactory)
+ {
+ }
}
} \ No newline at end of file
diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
index ab71f321c0..18936df013 100644
--- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
@@ -18,8 +18,11 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Xml;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.XbmcMetadata.Savers
{
@@ -105,9 +108,10 @@ namespace MediaBrowser.XbmcMetadata.Savers
}.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
- protected BaseNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger)
+ protected BaseNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
{
Logger = logger;
+ XmlReaderSettingsFactory = xmlReaderSettingsFactory;
UserDataManager = userDataManager;
UserManager = userManager;
LibraryManager = libraryManager;
@@ -121,6 +125,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
protected IUserManager UserManager { get; private set; }
protected IUserDataManager UserDataManager { get; private set; }
protected ILogger Logger { get; private set; }
+ protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; }
protected ItemUpdateType MinimumUpdateType
{
@@ -203,32 +208,33 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
FileSystem.CreateDirectory(Path.GetDirectoryName(path));
- var file = new FileInfo(path);
+ var file = FileSystem.GetFileInfo(path);
var wasHidden = false;
// This will fail if the file is hidden
if (file.Exists)
{
- if ((file.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
+ if (file.IsHidden)
{
- file.Attributes &= ~FileAttributes.Hidden;
+ FileSystem.SetHidden(path, false);
wasHidden = true;
}
+ if (file.IsReadOnly)
+ {
+ FileSystem.SetReadOnly(path, false);
+ }
}
- using (var filestream = FileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
+ using (var filestream = FileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
{
stream.CopyTo(filestream);
}
if (wasHidden || ConfigurationManager.Configuration.SaveMetadataHidden)
{
- file.Refresh();
-
- // Add back the attribute
- file.Attributes |= FileAttributes.Hidden;
+ FileSystem.SetHidden(path, true);
}
}
@@ -269,13 +275,13 @@ namespace MediaBrowser.XbmcMetadata.Savers
try
{
- AddCustomTags(xmlPath, tagsUsed, writer, Logger);
+ AddCustomTags(xmlPath, tagsUsed, writer, Logger, FileSystem);
}
catch (FileNotFoundException)
{
}
- catch (DirectoryNotFoundException)
+ catch (IOException)
{
}
@@ -428,7 +434,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
/// <returns>Task.</returns>
public static void AddCommonNodes(BaseItem item, XmlWriter writer, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataRepo, IFileSystem fileSystem, IServerConfigurationManager config)
{
- var writtenProviderIds = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
+ var writtenProviderIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var overview = (item.Overview ?? string.Empty)
.StripHtml()
@@ -483,13 +489,9 @@ namespace MediaBrowser.XbmcMetadata.Savers
writer.WriteElementString("title", item.Name ?? string.Empty);
- var hasOriginalTitle = item as IHasOriginalTitle;
- if (hasOriginalTitle != null)
+ if (!string.IsNullOrWhiteSpace(item.OriginalTitle))
{
- if (!string.IsNullOrEmpty(hasOriginalTitle.OriginalTitle))
- {
- writer.WriteElementString("originaltitle", hasOriginalTitle.OriginalTitle ?? string.Empty);
- }
+ writer.WriteElementString("originaltitle", item.OriginalTitle);
}
var people = libraryManager.GetPeople(item);
@@ -909,17 +911,14 @@ namespace MediaBrowser.XbmcMetadata.Savers
var image = item.GetImageInfo(ImageType.Primary, 0);
- if (image != null && image.IsLocalFile)
+ if (image != null)
{
- writer.WriteElementString("poster", GetPathToSave(image.Path, libraryManager, config));
+ writer.WriteElementString("poster", GetImagePathToSave(image, libraryManager, config));
}
foreach (var backdrop in item.GetImages(ImageType.Backdrop))
{
- if (backdrop.IsLocalFile)
- {
- writer.WriteElementString("fanart", GetPathToSave(backdrop.Path, libraryManager, config));
- }
+ writer.WriteElementString("fanart", GetImagePathToSave(backdrop, libraryManager, config));
}
writer.WriteEndElement();
@@ -1010,9 +1009,9 @@ namespace MediaBrowser.XbmcMetadata.Savers
var personEntity = libraryManager.GetPerson(person.Name);
var image = personEntity.GetImageInfo(ImageType.Primary, 0);
- if (image != null && image.IsLocalFile)
+ if (image != null)
{
- writer.WriteElementString("thumb", GetPathToSave(image.Path, libraryManager, config));
+ writer.WriteElementString("thumb", GetImagePathToSave(image, libraryManager, config));
}
}
catch (Exception)
@@ -1024,9 +1023,14 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
}
- private static string GetPathToSave(string path, ILibraryManager libraryManager, IServerConfigurationManager config)
+ private static string GetImagePathToSave(ItemImageInfo image, ILibraryManager libraryManager, IServerConfigurationManager config)
{
- return libraryManager.GetPathAfterNetworkSubstitution(path);
+ if (!image.IsLocalFile)
+ {
+ return image.Path;
+ }
+
+ return libraryManager.GetPathAfterNetworkSubstitution(image.Path);
}
private static bool IsPersonType(PersonInfo person, string type)
@@ -1034,56 +1038,57 @@ namespace MediaBrowser.XbmcMetadata.Savers
return string.Equals(person.Type, type, StringComparison.OrdinalIgnoreCase) || string.Equals(person.Role, type, StringComparison.OrdinalIgnoreCase);
}
- private static void AddCustomTags(string path, List<string> xmlTagsUsed, XmlWriter writer, ILogger logger)
+ private void AddCustomTags(string path, List<string> xmlTagsUsed, XmlWriter writer, ILogger logger, IFileSystem fileSystem)
{
- var settings = new XmlReaderSettings
- {
- CheckCharacters = false,
- IgnoreProcessingInstructions = true,
- IgnoreComments = true,
- ValidationType = ValidationType.None
- };
+ var settings = XmlReaderSettingsFactory.Create(false);
+
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
- using (var streamReader = GetStreamReader(path))
+ using (var fileStream = fileSystem.OpenRead(path))
{
- // Use XmlReader for best performance
- using (var reader = XmlReader.Create(streamReader, settings))
+ using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
{
- try
+ // Use XmlReader for best performance
+ using (var reader = XmlReader.Create(streamReader, settings))
{
- reader.MoveToContent();
- }
- catch (Exception ex)
- {
- logger.ErrorException("Error reading existing xml tags from {0}.", ex, path);
- return;
- }
-
- // Loop through each element
- while (reader.Read())
- {
- if (reader.NodeType == XmlNodeType.Element)
+ try
{
- var name = reader.Name;
+ reader.MoveToContent();
+ }
+ catch (Exception ex)
+ {
+ logger.ErrorException("Error reading existing xml tags from {0}.", ex, path);
+ return;
+ }
- if (!CommonTags.ContainsKey(name) && !xmlTagsUsed.Contains(name, StringComparer.OrdinalIgnoreCase))
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ {
+ if (reader.NodeType == XmlNodeType.Element)
{
- writer.WriteNode(reader, false);
+ var name = reader.Name;
+
+ if (!CommonTags.ContainsKey(name) && !xmlTagsUsed.Contains(name, StringComparer.OrdinalIgnoreCase))
+ {
+ writer.WriteNode(reader, false);
+ }
+ else
+ {
+ reader.Skip();
+ }
}
else
{
- reader.Skip();
+ reader.Read();
}
}
}
}
}
-
- }
-
- public static StreamReader GetStreamReader(string path)
- {
- return new StreamReader(path, Encoding.UTF8);
}
}
}
diff --git a/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs
index 0dfcd9ce5b..3346b43d34 100644
--- a/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs
@@ -8,16 +8,15 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Xml;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.XbmcMetadata.Savers
{
public class EpisodeNfoSaver : BaseNfoSaver
{
- public EpisodeNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
- {
- }
-
protected override string GetLocalSavePath(IHasMetadata item)
{
return Path.ChangeExtension(item.Path, ".nfo");
@@ -129,5 +128,9 @@ namespace MediaBrowser.XbmcMetadata.Savers
return list;
}
+
+ public EpisodeNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger, xmlReaderSettingsFactory)
+ {
+ }
}
} \ No newline at end of file
diff --git a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
index 63445b9c45..3bd3a15551 100644
--- a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs
@@ -10,16 +10,15 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.XbmcMetadata.Savers
{
public class MovieNfoSaver : BaseNfoSaver
{
- public MovieNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
- {
- }
-
protected override string GetLocalSavePath(IHasMetadata item)
{
return GetMovieSavePaths(new ItemInfo(item), FileSystem).FirstOrDefault();
@@ -36,7 +35,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
list.Add(Path.Combine(path, "VIDEO_TS", "VIDEO_TS.nfo"));
}
- if (item.VideoType == VideoType.Dvd || item.VideoType == VideoType.BluRay || item.VideoType == VideoType.HdDvd)
+ if (!item.IsPlaceHolder && (item.VideoType == VideoType.Dvd || item.VideoType == VideoType.BluRay || item.VideoType == VideoType.HdDvd))
{
var path = item.ContainingFolderPath;
@@ -126,5 +125,9 @@ namespace MediaBrowser.XbmcMetadata.Savers
return list;
}
+
+ public MovieNfoSaver(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.XbmcMetadata/Savers/SeasonNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs
index 9d9b2e94c0..bbf4b5900d 100644
--- a/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs
@@ -7,16 +7,15 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Xml;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.XbmcMetadata.Savers
{
public class SeasonNfoSaver : BaseNfoSaver
{
- public SeasonNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
- {
- }
-
protected override string GetLocalSavePath(IHasMetadata item)
{
return Path.Combine(item.Path, "season.nfo");
@@ -60,5 +59,9 @@ namespace MediaBrowser.XbmcMetadata.Savers
return list;
}
+
+ public SeasonNfoSaver(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.XbmcMetadata/Savers/SeriesNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs
index 3fb3a8e9d9..b512939a7f 100644
--- a/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs
@@ -8,16 +8,15 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Xml;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Xml;
namespace MediaBrowser.XbmcMetadata.Savers
{
public class SeriesNfoSaver : BaseNfoSaver
{
- public SeriesNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger) : base(fileSystem, configurationManager, libraryManager, userManager, userDataManager, logger)
- {
- }
-
protected override string GetLocalSavePath(IHasMetadata item)
{
return Path.Combine(item.Path, "tvshow.nfo");
@@ -91,8 +90,6 @@ namespace MediaBrowser.XbmcMetadata.Savers
}
}
- private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
protected override List<string> GetTagsUsed()
{
var list = new List<string>
@@ -109,5 +106,9 @@ namespace MediaBrowser.XbmcMetadata.Savers
return list;
}
+
+ public SeriesNfoSaver(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.XbmcMetadata/packages.config b/MediaBrowser.XbmcMetadata/packages.config
index ccef6d6862..6b8deb9c96 100644
--- a/MediaBrowser.XbmcMetadata/packages.config
+++ b/MediaBrowser.XbmcMetadata/packages.config
@@ -1,5 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="CommonIO" version="1.0.0.9" targetFramework="net45" />
- <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
</packages> \ No newline at end of file
diff --git a/MediaBrowser.XbmcMetadata/project.json b/MediaBrowser.XbmcMetadata/project.json
new file mode 100644
index 0000000000..fbbe9eaf32
--- /dev/null
+++ b/MediaBrowser.XbmcMetadata/project.json
@@ -0,0 +1,17 @@
+{
+ "frameworks":{
+ "netstandard1.6":{
+ "dependencies":{
+ "NETStandard.Library":"1.6.0",
+ }
+ },
+ ".NETPortable,Version=v4.5,Profile=Profile7":{
+ "buildOptions": {
+ "define": [ ]
+ },
+ "frameworkAssemblies":{
+
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.sln b/MediaBrowser.sln
index 22e990e8b9..292d0345cf 100644
--- a/MediaBrowser.sln
+++ b/MediaBrowser.sln
@@ -34,22 +34,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Model", "Media
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.WebDashboard", "MediaBrowser.WebDashboard\MediaBrowser.WebDashboard.csproj", "{5624B7B5-B5A7-41D8-9F10-CC5611109619}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Common.Implementations", "MediaBrowser.Common.Implementations\MediaBrowser.Common.Implementations.csproj", "{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Server.Implementations", "MediaBrowser.Server.Implementations\MediaBrowser.Server.Implementations.csproj", "{2E781478-814D-4A48-9D80-BFF206441A65}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Model.net35", "MediaBrowser.Model.net35\MediaBrowser.Model.net35.csproj", "{657B5410-7C3B-4806-9753-D254102CE537}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Tests", "MediaBrowser.Tests\MediaBrowser.Tests.csproj", "{E22BFD35-0FCD-4A85-978A-C22DCD73A081}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Providers", "MediaBrowser.Providers\MediaBrowser.Providers.csproj", "{442B5058-DCAF-4263-BB6A-F21E31120A1B}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Model.Portable", "MediaBrowser.Model.Portable\MediaBrowser.Model.Portable.csproj", "{D729ADB1-1C01-428D-B680-8EFACD687B2A}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.ServerApplication", "MediaBrowser.ServerApplication\MediaBrowser.ServerApplication.csproj", "{94ADE4D3-B7EC-45CD-A200-CC469433072B}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Dlna", "MediaBrowser.Dlna\MediaBrowser.Dlna.csproj", "{734098EB-6DC1-4DD0-A1CA-3140DCD2737C}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.MediaEncoding", "MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj", "{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenSubtitlesHandler", "OpenSubtitlesHandler\OpenSubtitlesHandler.csproj", "{4A4402D4-E910-443B-B8FC-2C18286A2CA0}"
@@ -64,7 +56,31 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Server.Startup
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing", "Emby.Drawing\Emby.Drawing.csproj", "{08FFF49B-F175-4807-A2B5-73B0EBD9F716}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Nat", "Mono.Nat\Mono.Nat.csproj", "{D7453B88-2266-4805-B39B-2B5A2A33E1BA}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Photos", "Emby.Photos\Emby.Photos.csproj", "{89AB4548-770D-41FD-A891-8DAFF44F452C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DvdLib", "DvdLib\DvdLib.csproj", "{713F42B5-878E-499D-A878-E4C652B1D5E8}"
+EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Emby.Common.Implementations", "Emby.Common.Implementations\Emby.Common.Implementations.xproj", "{5A27010A-09C6-4E86-93EA-437484C10917}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BDInfo", "BDInfo\BDInfo.csproj", "{88AE38DF-19D7-406F-A6A9-09527719A21E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Server.Implementations", "Emby.Server.Implementations\Emby.Server.Implementations.csproj", "{E383961B-9356-4D5D-8233-9A1079D03055}"
+EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Mono.Nat", "Mono.Nat\Mono.Nat.xproj", "{4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RSSDP", "RSSDP\RSSDP.csproj", "{21002819-C39A-4D3E-BE83-2A276A77FB1F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Dlna", "Emby.Dlna\Emby.Dlna.csproj", "{805844AB-E92F-45E6-9D99-4F6D48D129A5}"
+EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Emby.Server.Core", "Emby.Server.Core\Emby.Server.Core.xproj", "{65AA7D67-8059-40CD-91F1-16D02687226C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing.ImageMagick", "Emby.Drawing.ImageMagick\Emby.Drawing.ImageMagick.csproj", "{6CFEE013-6E7C-432B-AC37-CABF0880C69A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing.Net", "Emby.Drawing.Net\Emby.Drawing.Net.csproj", "{C97A239E-A96C-4D64-A844-CCF8CC30AECB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceStack", "ServiceStack\ServiceStack.csproj", "{680A1709-25EB-4D52-A87F-EE03FFD94BAA}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SocketHttpListener.Portable", "SocketHttpListener.Portable\SocketHttpListener.Portable.csproj", "{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -83,6 +99,11 @@ Global
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
@@ -108,6 +129,16 @@ 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 Mono|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Signed|Any CPU.Build.0 = Release Mono|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Signed|Mixed Platforms.ActiveCfg = Release Mono|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Signed|Mixed Platforms.Build.0 = Release Mono|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Signed|Win32.ActiveCfg = Release Mono|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Signed|Win32.Build.0 = Release Mono|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Signed|x64.ActiveCfg = Release Mono|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Signed|x64.Build.0 = Release Mono|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Signed|x86.ActiveCfg = Release Mono|Any CPU
+ {17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Signed|x86.Build.0 = Release Mono|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
@@ -131,6 +162,16 @@ 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 Mono|Any CPU
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|Any CPU.Build.0 = Release Mono|Any CPU
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|Mixed Platforms.ActiveCfg = Release Mono|Any CPU
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|Mixed Platforms.Build.0 = Release Mono|Any CPU
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|Win32.ActiveCfg = Release Mono|Any CPU
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|Win32.Build.0 = Release Mono|Any CPU
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|x64.ActiveCfg = Release Mono|Any CPU
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|x64.Build.0 = Release Mono|Any CPU
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|x86.ActiveCfg = Release Mono|Any CPU
+ {4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Signed|x86.Build.0 = Release Mono|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
@@ -154,6 +195,16 @@ 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 Mono|Any CPU
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|Any CPU.Build.0 = Release Mono|Any CPU
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|Mixed Platforms.ActiveCfg = Release Mono|Any CPU
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|Mixed Platforms.Build.0 = Release Mono|Any CPU
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|Win32.ActiveCfg = Release Mono|Any CPU
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|Win32.Build.0 = Release Mono|Any CPU
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|x64.ActiveCfg = Release Mono|Any CPU
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|x64.Build.0 = Release Mono|Any CPU
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|x86.ActiveCfg = Release Mono|Any CPU
+ {9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Signed|x86.Build.0 = Release Mono|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
@@ -164,11 +215,14 @@ Global
{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 Mono|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release Mono|Mixed Platforms.Build.0 = Release Mono|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release Mono|Win32.ActiveCfg = Release Mono|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release Mono|x64.ActiveCfg = Release Mono|Any CPU
- {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release Mono|x86.ActiveCfg = Release Mono|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|Win32.Build.0 = Release|Any CPU
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release Mono|x64.ActiveCfg = Release|Any CPU
+ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release Mono|x64.Build.0 = 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
@@ -177,6 +231,16 @@ 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
@@ -200,27 +264,16 @@ 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
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|Win32.ActiveCfg = Debug|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|x64.ActiveCfg = Debug|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|x86.ActiveCfg = Debug|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release Mono|Mixed Platforms.ActiveCfg = Release Mono|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release Mono|Mixed Platforms.Build.0 = Release Mono|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release Mono|Win32.ActiveCfg = Release Mono|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release Mono|x64.ActiveCfg = Release Mono|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release Mono|x86.ActiveCfg = Release Mono|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|Any CPU.Build.0 = Release|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|Win32.ActiveCfg = Release|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|x64.ActiveCfg = Release|Any CPU
- {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|x86.ActiveCfg = Release|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|Any CPU.ActiveCfg = Release Mono|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|Any CPU.Build.0 = Release Mono|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|Mixed Platforms.ActiveCfg = Release Mono|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|Mixed Platforms.Build.0 = Release Mono|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|Win32.ActiveCfg = Release Mono|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|Win32.Build.0 = Release Mono|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|x64.ActiveCfg = Release Mono|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|x64.Build.0 = Release Mono|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|x86.ActiveCfg = Release Mono|Any CPU
+ {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Signed|x86.Build.0 = Release Mono|Any CPU
{2E781478-814D-4A48-9D80-BFF206441A65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2E781478-814D-4A48-9D80-BFF206441A65}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2E781478-814D-4A48-9D80-BFF206441A65}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -242,27 +295,16 @@ Global
{2E781478-814D-4A48-9D80-BFF206441A65}.Release|Win32.ActiveCfg = Release|Any CPU
{2E781478-814D-4A48-9D80-BFF206441A65}.Release|x64.ActiveCfg = Release|Any CPU
{2E781478-814D-4A48-9D80-BFF206441A65}.Release|x86.ActiveCfg = Release|Any CPU
- {657B5410-7C3B-4806-9753-D254102CE537}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {657B5410-7C3B-4806-9753-D254102CE537}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {657B5410-7C3B-4806-9753-D254102CE537}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {657B5410-7C3B-4806-9753-D254102CE537}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {657B5410-7C3B-4806-9753-D254102CE537}.Debug|Win32.ActiveCfg = Debug|Any CPU
- {657B5410-7C3B-4806-9753-D254102CE537}.Debug|x64.ActiveCfg = Debug|Any CPU
- {657B5410-7C3B-4806-9753-D254102CE537}.Debug|x86.ActiveCfg = Debug|Any CPU
- {657B5410-7C3B-4806-9753-D254102CE537}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {657B5410-7C3B-4806-9753-D254102CE537}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {657B5410-7C3B-4806-9753-D254102CE537}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {657B5410-7C3B-4806-9753-D254102CE537}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {657B5410-7C3B-4806-9753-D254102CE537}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {657B5410-7C3B-4806-9753-D254102CE537}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {657B5410-7C3B-4806-9753-D254102CE537}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {657B5410-7C3B-4806-9753-D254102CE537}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {657B5410-7C3B-4806-9753-D254102CE537}.Release|Any CPU.Build.0 = Release|Any CPU
- {657B5410-7C3B-4806-9753-D254102CE537}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {657B5410-7C3B-4806-9753-D254102CE537}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {657B5410-7C3B-4806-9753-D254102CE537}.Release|Win32.ActiveCfg = Release|Any CPU
- {657B5410-7C3B-4806-9753-D254102CE537}.Release|x64.ActiveCfg = Release|Any CPU
- {657B5410-7C3B-4806-9753-D254102CE537}.Release|x86.ActiveCfg = Release|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Signed|Any CPU.ActiveCfg = Release Mono|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Signed|Any CPU.Build.0 = Release Mono|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Signed|Mixed Platforms.ActiveCfg = Release Mono|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Signed|Mixed Platforms.Build.0 = Release Mono|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Signed|Win32.ActiveCfg = Release Mono|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Signed|Win32.Build.0 = Release Mono|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Signed|x64.ActiveCfg = Release Mono|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Signed|x64.Build.0 = Release Mono|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Signed|x86.ActiveCfg = Release Mono|Any CPU
+ {2E781478-814D-4A48-9D80-BFF206441A65}.Signed|x86.Build.0 = Release Mono|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
@@ -284,6 +326,16 @@ Global
{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
@@ -305,27 +357,16 @@ 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
- {D729ADB1-1C01-428D-B680-8EFACD687B2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D729ADB1-1C01-428D-B680-8EFACD687B2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D729ADB1-1C01-428D-B680-8EFACD687B2A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {D729ADB1-1C01-428D-B680-8EFACD687B2A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {D729ADB1-1C01-428D-B680-8EFACD687B2A}.Debug|Win32.ActiveCfg = Debug|Any CPU
- {D729ADB1-1C01-428D-B680-8EFACD687B2A}.Debug|x64.ActiveCfg = Debug|Any CPU
- {D729ADB1-1C01-428D-B680-8EFACD687B2A}.Debug|x86.ActiveCfg = Debug|Any CPU
- {D729ADB1-1C01-428D-B680-8EFACD687B2A}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {D729ADB1-1C01-428D-B680-8EFACD687B2A}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {D729ADB1-1C01-428D-B680-8EFACD687B2A}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {D729ADB1-1C01-428D-B680-8EFACD687B2A}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {D729ADB1-1C01-428D-B680-8EFACD687B2A}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {D729ADB1-1C01-428D-B680-8EFACD687B2A}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {D729ADB1-1C01-428D-B680-8EFACD687B2A}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {D729ADB1-1C01-428D-B680-8EFACD687B2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D729ADB1-1C01-428D-B680-8EFACD687B2A}.Release|Any CPU.Build.0 = Release|Any CPU
- {D729ADB1-1C01-428D-B680-8EFACD687B2A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {D729ADB1-1C01-428D-B680-8EFACD687B2A}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {D729ADB1-1C01-428D-B680-8EFACD687B2A}.Release|Win32.ActiveCfg = Release|Any CPU
- {D729ADB1-1C01-428D-B680-8EFACD687B2A}.Release|x64.ActiveCfg = Release|Any CPU
- {D729ADB1-1C01-428D-B680-8EFACD687B2A}.Release|x86.ActiveCfg = Release|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|Any CPU.ActiveCfg = Release Mono|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|Any CPU.Build.0 = Release Mono|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|Mixed Platforms.ActiveCfg = Release Mono|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|Mixed Platforms.Build.0 = Release Mono|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|Win32.ActiveCfg = Release Mono|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|Win32.Build.0 = Release Mono|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|x64.ActiveCfg = Release Mono|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|x64.Build.0 = Release Mono|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|x86.ActiveCfg = Release Mono|Any CPU
+ {442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|x86.Build.0 = Release Mono|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
@@ -348,27 +389,16 @@ Global
{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
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Debug|Win32.ActiveCfg = Debug|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Debug|x64.ActiveCfg = Debug|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Debug|x86.ActiveCfg = Debug|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release Mono|Mixed Platforms.ActiveCfg = Release Mono|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release Mono|Mixed Platforms.Build.0 = Release Mono|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release Mono|Win32.ActiveCfg = Release Mono|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release Mono|x64.ActiveCfg = Release Mono|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release Mono|x86.ActiveCfg = Release Mono|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release|Any CPU.Build.0 = Release|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release|Win32.ActiveCfg = Release|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.Release|x64.ActiveCfg = Release|Any CPU
- {734098EB-6DC1-4DD0-A1CA-3140DCD2737C}.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
{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -390,6 +420,16 @@ Global
{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|Win32.ActiveCfg = Release|Any CPU
{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|x64.ActiveCfg = Release|Any CPU
{0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Release|x86.ActiveCfg = Release|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|Any CPU.ActiveCfg = Release Mono|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|Any CPU.Build.0 = Release Mono|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|Mixed Platforms.ActiveCfg = Release Mono|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|Mixed Platforms.Build.0 = Release Mono|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|Win32.ActiveCfg = Release Mono|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|Win32.Build.0 = Release Mono|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|x64.ActiveCfg = Release Mono|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|x64.Build.0 = Release Mono|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|x86.ActiveCfg = Release Mono|Any CPU
+ {0BD82FA6-EB8A-4452-8AF5-74F9C3849451}.Signed|x86.Build.0 = Release Mono|Any CPU
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -411,6 +451,16 @@ 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 Mono|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|Any CPU.Build.0 = Release Mono|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|Mixed Platforms.ActiveCfg = Release Mono|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|Mixed Platforms.Build.0 = Release Mono|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|Win32.ActiveCfg = Release Mono|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|Win32.Build.0 = Release Mono|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|x64.ActiveCfg = Release Mono|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|x64.Build.0 = Release Mono|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|x86.ActiveCfg = Release Mono|Any CPU
+ {4A4402D4-E910-443B-B8FC-2C18286A2CA0}.Signed|x86.Build.0 = Release Mono|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
@@ -432,6 +482,16 @@ 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
@@ -453,6 +513,16 @@ 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|x86
{175A9388-F352-4586-A6B4-070DED62B644}.Debug|Any CPU.Build.0 = Debug|x86
{175A9388-F352-4586-A6B4-070DED62B644}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
@@ -479,6 +549,16 @@ Global
{175A9388-F352-4586-A6B4-070DED62B644}.Release|x64.ActiveCfg = Release|Any CPU
{175A9388-F352-4586-A6B4-070DED62B644}.Release|x86.ActiveCfg = Release|x86
{175A9388-F352-4586-A6B4-070DED62B644}.Release|x86.Build.0 = Release|x86
+ {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|x86
+ {175A9388-F352-4586-A6B4-070DED62B644}.Signed|Mixed Platforms.Build.0 = Release|x86
+ {175A9388-F352-4586-A6B4-070DED62B644}.Signed|Win32.ActiveCfg = Release|x86
+ {175A9388-F352-4586-A6B4-070DED62B644}.Signed|Win32.Build.0 = Release|x86
+ {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|x86
+ {175A9388-F352-4586-A6B4-070DED62B644}.Signed|x86.Build.0 = Release|x86
{B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@@ -500,6 +580,16 @@ Global
{B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Release|Win32.ActiveCfg = Release|Any CPU
{B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Release|x64.ActiveCfg = Release|Any CPU
{B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Release|x86.ActiveCfg = Release|Any CPU
+ {B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Signed|Any CPU.Build.0 = Release|Any CPU
+ {B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
+ {B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Signed|Win32.ActiveCfg = Release|Any CPU
+ {B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Signed|Win32.Build.0 = Release|Any CPU
+ {B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Signed|x64.ActiveCfg = Release|Any CPU
+ {B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Signed|x64.Build.0 = Release|Any CPU
+ {B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.Signed|x86.ActiveCfg = Release|Any CPU
+ {B90AB8F2-1BFF-4568-A3FD-2A338A435A75}.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
@@ -521,36 +611,536 @@ 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
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|Win32.ActiveCfg = Debug|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|Win32.Build.0 = Debug|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|x64.ActiveCfg = Debug|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|x64.Build.0 = Debug|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|x86.ActiveCfg = Debug|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Debug|x86.Build.0 = Debug|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|Win32.Build.0 = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|x64.Build.0 = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release Mono|x86.Build.0 = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|Any CPU.Build.0 = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|Win32.ActiveCfg = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|Win32.Build.0 = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|x64.ActiveCfg = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|x64.Build.0 = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|x86.ActiveCfg = Release|Any CPU
- {D7453B88-2266-4805-B39B-2B5A2A33E1BA}.Release|x86.Build.0 = 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
+ {89AB4548-770D-41FD-A891-8DAFF44F452C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {89AB4548-770D-41FD-A891-8DAFF44F452C}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {89AB4548-770D-41FD-A891-8DAFF44F452C}.Debug|Win32.Build.0 = Debug|Any CPU
+ {89AB4548-770D-41FD-A891-8DAFF44F452C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {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
+ {89AB4548-770D-41FD-A891-8DAFF44F452C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {89AB4548-770D-41FD-A891-8DAFF44F452C}.Release|Win32.ActiveCfg = Release|Any CPU
+ {89AB4548-770D-41FD-A891-8DAFF44F452C}.Release|Win32.Build.0 = Release|Any CPU
+ {89AB4548-770D-41FD-A891-8DAFF44F452C}.Release|x64.ActiveCfg = Release|Any CPU
+ {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
+ {713F42B5-878E-499D-A878-E4C652B1D5E8}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {713F42B5-878E-499D-A878-E4C652B1D5E8}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {713F42B5-878E-499D-A878-E4C652B1D5E8}.Debug|Win32.Build.0 = Debug|Any CPU
+ {713F42B5-878E-499D-A878-E4C652B1D5E8}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {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
+ {713F42B5-878E-499D-A878-E4C652B1D5E8}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {713F42B5-878E-499D-A878-E4C652B1D5E8}.Release|Win32.ActiveCfg = Release|Any CPU
+ {713F42B5-878E-499D-A878-E4C652B1D5E8}.Release|Win32.Build.0 = Release|Any CPU
+ {713F42B5-878E-499D-A878-E4C652B1D5E8}.Release|x64.ActiveCfg = Release|Any CPU
+ {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
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Debug|Win32.Build.0 = Debug|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Debug|x64.Build.0 = Debug|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Debug|x86.Build.0 = Debug|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|Any CPU.Build.0 = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|Win32.ActiveCfg = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|Win32.Build.0 = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|x64.ActiveCfg = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|x64.Build.0 = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|x86.ActiveCfg = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release Mono|x86.Build.0 = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release|Win32.ActiveCfg = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release|Win32.Build.0 = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release|x64.ActiveCfg = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release|x64.Build.0 = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release|x86.ActiveCfg = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Release|x86.Build.0 = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Signed|Any CPU.Build.0 = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Signed|Win32.ActiveCfg = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Signed|Win32.Build.0 = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Signed|x64.ActiveCfg = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Signed|x64.Build.0 = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.Signed|x86.ActiveCfg = Release|Any CPU
+ {5A27010A-09C6-4E86-93EA-437484C10917}.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
+ {88AE38DF-19D7-406F-A6A9-09527719A21E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {88AE38DF-19D7-406F-A6A9-09527719A21E}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {88AE38DF-19D7-406F-A6A9-09527719A21E}.Debug|Win32.Build.0 = Debug|Any CPU
+ {88AE38DF-19D7-406F-A6A9-09527719A21E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {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
+ {88AE38DF-19D7-406F-A6A9-09527719A21E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {88AE38DF-19D7-406F-A6A9-09527719A21E}.Release|Win32.ActiveCfg = Release|Any CPU
+ {88AE38DF-19D7-406F-A6A9-09527719A21E}.Release|Win32.Build.0 = Release|Any CPU
+ {88AE38DF-19D7-406F-A6A9-09527719A21E}.Release|x64.ActiveCfg = Release|Any CPU
+ {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
+ {E383961B-9356-4D5D-8233-9A1079D03055}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {E383961B-9356-4D5D-8233-9A1079D03055}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {E383961B-9356-4D5D-8233-9A1079D03055}.Debug|Win32.Build.0 = Debug|Any CPU
+ {E383961B-9356-4D5D-8233-9A1079D03055}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {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
+ {E383961B-9356-4D5D-8233-9A1079D03055}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {E383961B-9356-4D5D-8233-9A1079D03055}.Release|Win32.ActiveCfg = Release|Any CPU
+ {E383961B-9356-4D5D-8233-9A1079D03055}.Release|Win32.Build.0 = Release|Any CPU
+ {E383961B-9356-4D5D-8233-9A1079D03055}.Release|x64.ActiveCfg = Release|Any CPU
+ {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
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|Win32.Build.0 = Debug|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|x64.Build.0 = Debug|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Debug|x86.Build.0 = Debug|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release Mono|Any CPU.Build.0 = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release Mono|Win32.ActiveCfg = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release Mono|Win32.Build.0 = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release Mono|x64.ActiveCfg = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release Mono|x64.Build.0 = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release Mono|x86.ActiveCfg = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release Mono|x86.Build.0 = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release|Win32.ActiveCfg = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release|Win32.Build.0 = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release|x64.ActiveCfg = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release|x64.Build.0 = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release|x86.ActiveCfg = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Release|x86.Build.0 = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Signed|Any CPU.Build.0 = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Signed|Win32.ActiveCfg = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Signed|Win32.Build.0 = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Signed|x64.ActiveCfg = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Signed|x64.Build.0 = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.Signed|x86.ActiveCfg = Release|Any CPU
+ {4ACAB6A2-AC9A-4B50-BAEC-1FE4A1F3B8BC}.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
+ {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|Win32.Build.0 = Debug|Any CPU
+ {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {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
+ {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|Win32.ActiveCfg = Release|Any CPU
+ {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|Win32.Build.0 = Release|Any CPU
+ {21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|x64.ActiveCfg = Release|Any CPU
+ {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
+ {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|Win32.Build.0 = Debug|Any CPU
+ {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {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
+ {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|Win32.ActiveCfg = Release|Any CPU
+ {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|Win32.Build.0 = Release|Any CPU
+ {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|x64.ActiveCfg = Release|Any CPU
+ {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
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Debug|Win32.Build.0 = Debug|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Debug|x64.Build.0 = Debug|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Debug|x86.Build.0 = Debug|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release Mono|Any CPU.Build.0 = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release Mono|Win32.ActiveCfg = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release Mono|Win32.Build.0 = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release Mono|x64.ActiveCfg = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release Mono|x64.Build.0 = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release Mono|x86.ActiveCfg = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release Mono|x86.Build.0 = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release|Win32.ActiveCfg = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release|Win32.Build.0 = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release|x64.ActiveCfg = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release|x64.Build.0 = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release|x86.ActiveCfg = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Release|x86.Build.0 = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Signed|Any CPU.Build.0 = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Signed|Win32.ActiveCfg = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Signed|Win32.Build.0 = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Signed|x64.ActiveCfg = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Signed|x64.Build.0 = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.Signed|x86.ActiveCfg = Release|Any CPU
+ {65AA7D67-8059-40CD-91F1-16D02687226C}.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
+ {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Debug|Win32.Build.0 = Debug|Any CPU
+ {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {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
+ {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release|Win32.ActiveCfg = Release|Any CPU
+ {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release|Win32.Build.0 = Release|Any CPU
+ {6CFEE013-6E7C-432B-AC37-CABF0880C69A}.Release|x64.ActiveCfg = Release|Any CPU
+ {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
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Debug|Win32.Build.0 = Debug|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Debug|x64.Build.0 = Debug|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Debug|x86.Build.0 = Debug|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release Mono|Any CPU.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release Mono|Win32.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release Mono|Win32.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release Mono|x64.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release Mono|x64.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release Mono|x86.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release Mono|x86.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release|Win32.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release|Win32.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release|x64.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release|x64.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release|x86.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Release|x86.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|Any CPU.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|Win32.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|Win32.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|x64.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|x64.Build.0 = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|x86.ActiveCfg = Release|Any CPU
+ {C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|x86.Build.0 = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Win32.Build.0 = Debug|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|x64.Build.0 = Debug|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|x86.Build.0 = Debug|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Any CPU.Build.0 = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Win32.ActiveCfg = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Win32.Build.0 = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|x64.ActiveCfg = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|x64.Build.0 = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|x86.ActiveCfg = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|x86.Build.0 = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Win32.ActiveCfg = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Win32.Build.0 = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|x64.ActiveCfg = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|x64.Build.0 = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|x86.ActiveCfg = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|x86.Build.0 = Release|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Any CPU.ActiveCfg = Signed|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Any CPU.Build.0 = Signed|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Mixed Platforms.ActiveCfg = Signed|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Mixed Platforms.Build.0 = Signed|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Win32.ActiveCfg = Signed|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Win32.Build.0 = Signed|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|x64.ActiveCfg = Signed|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|x64.Build.0 = Signed|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|x86.ActiveCfg = Signed|Any CPU
+ {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|x86.Build.0 = Signed|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|Win32.Build.0 = Debug|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|x64.Build.0 = Debug|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|x86.Build.0 = Debug|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release Mono|Any CPU.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release Mono|Win32.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release Mono|Win32.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release Mono|x64.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release Mono|x64.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release Mono|x86.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release Mono|x86.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release|Win32.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release|Win32.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release|x64.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release|x64.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release|x86.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Release|x86.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|Any CPU.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|Any CPU.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|Mixed Platforms.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|Win32.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|Win32.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|x64.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|x64.Build.0 = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|x86.ActiveCfg = Release|Any CPU
+ {4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Mono.Nat/AbstractNatDevice.cs b/Mono.Nat/AbstractNatDevice.cs
index 046cfc10f3..1b42160025 100644
--- a/Mono.Nat/AbstractNatDevice.cs
+++ b/Mono.Nat/AbstractNatDevice.cs
@@ -30,6 +30,7 @@ using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
+using System.Threading.Tasks;
namespace Mono.Nat
{
@@ -50,48 +51,6 @@ namespace Mono.Nat
set { lastSeen = value; }
}
- public virtual void CreatePortMap (Mapping mapping)
- {
- IAsyncResult result = BeginCreatePortMap (mapping, null, null);
- EndCreatePortMap(result);
- }
-
- public virtual void DeletePortMap (Mapping mapping)
- {
- IAsyncResult result = BeginDeletePortMap (mapping, null, mapping);
- EndDeletePortMap(result);
- }
-
- public virtual Mapping[] GetAllMappings ()
- {
- IAsyncResult result = BeginGetAllMappings (null, null);
- return EndGetAllMappings (result);
- }
-
- public virtual IPAddress GetExternalIP ()
- {
- IAsyncResult result = BeginGetExternalIP(null, null);
- return EndGetExternalIP(result);
- }
-
- public virtual Mapping GetSpecificMapping (Protocol protocol, int port)
- {
- IAsyncResult result = this.BeginGetSpecificMapping (protocol, port, null, null);
- return this.EndGetSpecificMapping(result);
- }
-
- public abstract IAsyncResult BeginCreatePortMap(Mapping mapping, AsyncCallback callback, object asyncState);
- public abstract IAsyncResult BeginDeletePortMap (Mapping mapping, AsyncCallback callback, object asyncState);
-
- public abstract IAsyncResult BeginGetAllMappings (AsyncCallback callback, object asyncState);
- public abstract IAsyncResult BeginGetExternalIP (AsyncCallback callback, object asyncState);
- public abstract IAsyncResult BeginGetSpecificMapping(Protocol protocol, int externalPort, AsyncCallback callback, object asyncState);
-
- public abstract void EndCreatePortMap (IAsyncResult result);
- public abstract void EndDeletePortMap (IAsyncResult result);
-
- public abstract Mapping[] EndGetAllMappings (IAsyncResult result);
- public abstract IPAddress EndGetExternalIP (IAsyncResult result);
- public abstract Mapping EndGetSpecificMapping (IAsyncResult result);
+ public abstract Task CreatePortMap(Mapping mapping);
}
}
diff --git a/Mono.Nat/Exceptions/MappingException.cs b/Mono.Nat/Exceptions/MappingException.cs
index bb2e6a69d3..9c0c4f1225 100644
--- a/Mono.Nat/Exceptions/MappingException.cs
+++ b/Mono.Nat/Exceptions/MappingException.cs
@@ -25,11 +25,9 @@
//
using System;
-using System.Security.Permissions;
namespace Mono.Nat
{
- [Serializable]
public class MappingException : Exception
{
private int errorCode;
@@ -45,7 +43,6 @@ namespace Mono.Nat
get { return this.errorText; }
}
- #region Constructors
public MappingException()
: base()
{
@@ -67,21 +64,5 @@ namespace Mono.Nat
: base(message, innerException)
{
}
-
- protected MappingException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
- : base(info, context)
- {
- }
- #endregion
-
- [SecurityPermission(SecurityAction.Demand, SerializationFormatter=true)]
- public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
- {
- if(info==null) throw new ArgumentNullException("info");
-
- this.errorCode = info.GetInt32("errorCode");
- this.errorText = info.GetString("errorText");
- base.GetObjectData(info, context);
- }
}
}
diff --git a/Mono.Nat/IMapper.cs b/Mono.Nat/IMapper.cs
deleted file mode 100644
index b18e6cff20..0000000000
--- a/Mono.Nat/IMapper.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-//
-// Authors:
-// Nicholas Terry <nick.i.terry@gmail.com>
-//
-// 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.Collections.Generic;
-using System.Linq;
-using System.Net;
-using System.Net.Sockets;
-using System.Text;
-
-namespace Mono.Nat
-{
- public enum MapperType
- {
- Pmp,
- Upnp
- }
-
- internal interface IMapper
- {
- event EventHandler<DeviceEventArgs> DeviceFound;
-
- void Map(IPAddress gatewayAddress);
-
- void Handle(IPAddress localAddres, byte[] response);
- }
-}
diff --git a/Mono.Nat/INatDevice.cs b/Mono.Nat/INatDevice.cs
index c9f27055b8..b0401627ba 100644
--- a/Mono.Nat/INatDevice.cs
+++ b/Mono.Nat/INatDevice.cs
@@ -30,32 +30,15 @@ using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
+using System.Threading.Tasks;
namespace Mono.Nat
{
public interface INatDevice
{
- void CreatePortMap (Mapping mapping);
- void DeletePortMap (Mapping mapping);
+ Task CreatePortMap (Mapping mapping);
IPAddress LocalAddress { get; }
- Mapping[] GetAllMappings ();
- IPAddress GetExternalIP ();
- Mapping GetSpecificMapping (Protocol protocol, int port);
-
- IAsyncResult BeginCreatePortMap (Mapping mapping, AsyncCallback callback, object asyncState);
- IAsyncResult BeginDeletePortMap (Mapping mapping, AsyncCallback callback, object asyncState);
-
- IAsyncResult BeginGetAllMappings (AsyncCallback callback, object asyncState);
- IAsyncResult BeginGetExternalIP (AsyncCallback callback, object asyncState);
- IAsyncResult BeginGetSpecificMapping (Protocol protocol, int externalPort, AsyncCallback callback, object asyncState);
-
- void EndCreatePortMap (IAsyncResult result);
- void EndDeletePortMap (IAsyncResult result);
-
- Mapping[] EndGetAllMappings (IAsyncResult result);
- IPAddress EndGetExternalIP (IAsyncResult result);
- Mapping EndGetSpecificMapping (IAsyncResult result);
DateTime LastSeen { get; set; }
}
diff --git a/Mono.Nat/Mono.Nat.xproj b/Mono.Nat/Mono.Nat.xproj
new file mode 100644
index 0000000000..3479a2a677
--- /dev/null
+++ b/Mono.Nat/Mono.Nat.xproj
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
+ <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+ </PropertyGroup>
+ <Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>4acab6a2-ac9a-4b50-baec-1fe4a1f3b8bc</ProjectGuid>
+ <RootNamespace>Mono.Nat</RootNamespace>
+ <BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
+ <OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
+ <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
+ </PropertyGroup>
+ <PropertyGroup>
+ <SchemaVersion>2.0</SchemaVersion>
+ </PropertyGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
+ <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
+ </ItemGroup>
+ <Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
+</Project> \ No newline at end of file
diff --git a/Mono.Nat/NatUtility.cs b/Mono.Nat/NatUtility.cs
index 6d91d25137..bcbe5d8d0e 100644
--- a/Mono.Nat/NatUtility.cs
+++ b/Mono.Nat/NatUtility.cs
@@ -34,10 +34,10 @@ using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Net.NetworkInformation;
-using MediaBrowser.Controller.Dlna;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Logging;
-using Mono.Nat.Pmp.Mappers;
-using Mono.Nat.Upnp.Mappers;
namespace Mono.Nat
{
@@ -47,16 +47,15 @@ namespace Mono.Nat
public static event EventHandler<DeviceEventArgs> DeviceFound;
public static event EventHandler<DeviceEventArgs> DeviceLost;
- public static event EventHandler<UnhandledExceptionEventArgs> UnhandledException;
-
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
+ public static bool Verbose
{
get { return verbose; }
set { verbose = value; }
@@ -66,14 +65,12 @@ namespace Mono.Nat
{
EnabledProtocols = new List<NatProtocol>
{
- NatProtocol.Upnp,
NatProtocol.Pmp
};
searching = new ManualResetEvent(false);
controllers = new List<ISearcher>();
- controllers.Add(UpnpSearcher.Instance);
controllers.Add(PmpSearcher.Instance);
controllers.ForEach(searcher =>
@@ -89,9 +86,8 @@ namespace Mono.Nat
DeviceLost(sender, args);
};
});
- Thread t = new Thread(SearchAndListen);
- t.IsBackground = true;
- t.Start();
+
+ Task.Factory.StartNew(SearchAndListen, TaskCreationOptions.LongRunning);
}
internal static void Log(string format, params object[] args)
@@ -101,7 +97,7 @@ namespace Mono.Nat
logger.Debug(format, args);
}
- private static void SearchAndListen()
+ private static async Task SearchAndListen()
{
while (true)
{
@@ -111,58 +107,42 @@ namespace Mono.Nat
{
var enabledProtocols = EnabledProtocols.ToList();
- if (enabledProtocols.Contains(UpnpSearcher.Instance.Protocol))
- {
- Receive(UpnpSearcher.Instance, UpnpSearcher.sockets);
- }
if (enabledProtocols.Contains(PmpSearcher.Instance.Protocol))
{
- Receive(PmpSearcher.Instance, PmpSearcher.sockets);
+ 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();
+ s.Search();
}
+ }
}
catch (Exception e)
{
- if (UnhandledException != null)
- UnhandledException(typeof(NatUtility), new UnhandledExceptionEventArgs(e, false));
+
}
- Thread.Sleep(10);
+ await Task.Delay(100).ConfigureAwait(false);
}
}
- static void Receive (ISearcher searcher, List<UdpClient> clients)
+ static async Task Receive (ISearcher searcher, List<UdpClient> clients)
{
- IPEndPoint received = new IPEndPoint(IPAddress.Parse("192.168.0.1"), 5351);
foreach (UdpClient client in clients)
{
if (client.Available > 0)
{
IPAddress localAddress = ((IPEndPoint)client.Client.LocalEndPoint).Address;
- byte[] data = client.Receive(ref received);
+ var result = await client.ReceiveAsync().ConfigureAwait(false);
+ var data = result.Buffer;
+ var received = result.RemoteEndPoint;
searcher.Handle(localAddress, data, received);
}
}
}
-
- static void Receive(IMapper mapper, List<UdpClient> clients)
- {
- IPEndPoint received = new IPEndPoint(IPAddress.Parse("192.168.0.1"), 5351);
- foreach (UdpClient client in clients)
- {
- if (client.Available > 0)
- {
- IPAddress localAddress = ((IPEndPoint)client.Client.LocalEndPoint).Address;
- byte[] data = client.Receive(ref received);
- mapper.Handle(localAddress, data);
- }
- }
- }
public static void StartDiscovery ()
{
@@ -173,49 +153,6 @@ namespace Mono.Nat
{
searching.Reset();
}
-
- //This is for when you know the Gateway IP and want to skip the costly search...
- public static void DirectMap(IPAddress gatewayAddress, MapperType type)
- {
- IMapper mapper;
- switch (type)
- {
- case MapperType.Pmp:
- mapper = new PmpMapper();
- break;
- case MapperType.Upnp:
- mapper = new UpnpMapper();
- mapper.DeviceFound += (sender, args) =>
- {
- if (DeviceFound != null)
- DeviceFound(sender, args);
- };
- mapper.Map(gatewayAddress);
- break;
- default:
- throw new InvalidOperationException("Unsuported type given");
-
- }
- searching.Reset();
-
- }
-
- //So then why is it here? -Nick
- [Obsolete ("This method serves no purpose and shouldn't be used")]
- public static IPAddress[] GetLocalAddresses (bool includeIPv6)
- {
- List<IPAddress> addresses = new List<IPAddress> ();
-
- IPHostEntry hostInfo = Dns.GetHostEntry (Dns.GetHostName ());
- foreach (IPAddress address in hostInfo.AddressList) {
- if (address.AddressFamily == AddressFamily.InterNetwork ||
- (includeIPv6 && address.AddressFamily == AddressFamily.InterNetworkV6)) {
- addresses.Add (address);
- }
- }
-
- return addresses.ToArray ();
- }
//checks if an IP address is a private address space as defined by RFC 1918
public static bool IsPrivateAddressSpace (IPAddress address)
@@ -239,7 +176,7 @@ namespace Mono.Nat
switch (protocol)
{
case NatProtocol.Upnp:
- UpnpSearcher.Instance.Handle(localAddress, response, endpoint);
+ //UpnpSearcher.Instance.Handle(localAddress, response, endpoint);
break;
case NatProtocol.Pmp:
PmpSearcher.Instance.Handle(localAddress, response, endpoint);
@@ -254,11 +191,21 @@ namespace Mono.Nat
switch (protocol)
{
case NatProtocol.Upnp:
- UpnpSearcher.Instance.Handle(localAddress, deviceInfo, endpoint);
+ var searcher = new UpnpSearcher(Logger, HttpClient);
+ searcher.DeviceFound += Searcher_DeviceFound;
+ searcher.Handle(localAddress, deviceInfo, endpoint);
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/Mappers/PmpMapper.cs b/Mono.Nat/Pmp/Mappers/PmpMapper.cs
deleted file mode 100644
index f33ca44c30..0000000000
--- a/Mono.Nat/Pmp/Mappers/PmpMapper.cs
+++ /dev/null
@@ -1,83 +0,0 @@
-//
-// Authors:
-// Nicholas Terry <nick.i.terry@gmail.com>
-//
-// 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.Collections.Generic;
-using System.Linq;
-using System.Net;
-using System.Net.Sockets;
-using System.Text;
-using Mono.Nat.Pmp;
-
-namespace Mono.Nat.Pmp.Mappers
-{
- internal class PmpMapper : Pmp, IMapper
- {
- public event EventHandler<DeviceEventArgs> DeviceFound;
-
- static PmpMapper()
- {
- CreateSocketsAndAddGateways();
- }
-
- public void Map(IPAddress gatewayAddress)
- {
- sockets.ForEach(x => Map(x, gatewayAddress));
- }
-
- void Map(UdpClient client, IPAddress gatewayAddress)
- {
- // The nat-pmp search message. Must be sent to GatewayIP:53531
- byte[] buffer = new byte[] { PmpConstants.Version, PmpConstants.OperationCode };
-
- client.Send(buffer, buffer.Length, new IPEndPoint(gatewayAddress, PmpConstants.ServerPort));
- }
-
- public void Handle(IPAddress localAddres, byte[] response)
- {
- //if (!IsSearchAddress(endpoint.Address))
- // return;
- if (response.Length != 12)
- return;
- if (response[0] != PmpConstants.Version)
- return;
- if (response[1] != PmpConstants.ServerNoop)
- return;
- int errorcode = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(response, 2));
- if (errorcode != 0)
- NatUtility.Log("Non zero error: {0}", errorcode);
-
- IPAddress publicIp = new IPAddress(new byte[] { response[8], response[9], response[10], response[11] });
- OnDeviceFound(new DeviceEventArgs(new PmpNatDevice(localAddres, publicIp)));
- }
-
- private void OnDeviceFound(DeviceEventArgs args)
- {
- if (DeviceFound != null)
- DeviceFound(this, args);
- }
- }
-}
diff --git a/Mono.Nat/Pmp/Pmp.cs b/Mono.Nat/Pmp/Pmp.cs
deleted file mode 100644
index 6795561b15..0000000000
--- a/Mono.Nat/Pmp/Pmp.cs
+++ /dev/null
@@ -1,118 +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.Collections.Generic;
-using System.Linq;
-using System.Net;
-using System.Net.NetworkInformation;
-using System.Net.Sockets;
-using System.Text;
-
-namespace Mono.Nat.Pmp
-{
- internal abstract class Pmp
- {
- public static List<UdpClient> sockets;
- protected static Dictionary<UdpClient, List<IPEndPoint>> gatewayLists;
-
- internal static void CreateSocketsAndAddGateways()
- {
- sockets = new List<UdpClient>();
- gatewayLists = new Dictionary<UdpClient, List<IPEndPoint>>();
-
- try
- {
- foreach (NetworkInterface n in NetworkInterface.GetAllNetworkInterfaces())
- {
- if (n.OperationalStatus != OperationalStatus.Up && n.OperationalStatus != OperationalStatus.Unknown)
- continue;
- IPInterfaceProperties properties = n.GetIPProperties();
- List<IPEndPoint> gatewayList = new List<IPEndPoint>();
-
- foreach (GatewayIPAddressInformation gateway in properties.GatewayAddresses)
- {
- if (gateway.Address.AddressFamily == AddressFamily.InterNetwork)
- {
- gatewayList.Add(new IPEndPoint(gateway.Address, PmpConstants.ServerPort));
- }
- }
- if (gatewayList.Count == 0)
- {
- /* Mono on OSX doesn't give any gateway addresses, so check DNS entries */
- foreach (var gw2 in properties.DnsAddresses)
- {
- if (gw2.AddressFamily == AddressFamily.InterNetwork)
- {
- gatewayList.Add(new IPEndPoint(gw2, PmpConstants.ServerPort));
- }
- }
- foreach (var unicast in properties.UnicastAddresses)
- {
- if (/*unicast.DuplicateAddressDetectionState == DuplicateAddressDetectionState.Preferred
- && unicast.AddressPreferredLifetime != UInt32.MaxValue
- && */unicast.Address.AddressFamily == AddressFamily.InterNetwork)
- {
- var bytes = unicast.Address.GetAddressBytes();
- bytes[3] = 1;
- gatewayList.Add(new IPEndPoint(new IPAddress(bytes), PmpConstants.ServerPort));
- }
- }
- }
-
- if (gatewayList.Count > 0)
- {
- foreach (UnicastIPAddressInformation address in properties.UnicastAddresses)
- {
- if (address.Address.AddressFamily == AddressFamily.InterNetwork)
- {
- UdpClient client;
-
- try
- {
- client = new UdpClient(new IPEndPoint(address.Address, 0));
- }
- catch (SocketException)
- {
- continue; // Move on to the next address.
- }
-
- gatewayLists.Add(client, gatewayList);
- sockets.Add(client);
- }
- }
- }
- }
- }
- catch (Exception)
- {
- // NAT-PMP does not use multicast, so there isn't really a good fallback.
- }
- }
- }
-}
diff --git a/Mono.Nat/Pmp/PmpNatDevice.cs b/Mono.Nat/Pmp/PmpNatDevice.cs
index 9a2962c4d5..93007cb8af 100644
--- a/Mono.Nat/Pmp/PmpNatDevice.cs
+++ b/Mono.Nat/Pmp/PmpNatDevice.cs
@@ -30,318 +30,173 @@ using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections.Generic;
+using System.Threading.Tasks;
namespace Mono.Nat.Pmp
{
- internal sealed class PmpNatDevice : AbstractNatDevice, IEquatable<PmpNatDevice>
- {
- private AsyncResult externalIpResult;
- private bool pendingOp;
- private IPAddress localAddress;
- private IPAddress publicAddress;
-
- internal PmpNatDevice (IPAddress localAddress, IPAddress publicAddress)
- {
- this.localAddress = localAddress;
- this.publicAddress = publicAddress;
- }
-
- public override IPAddress LocalAddress
- {
- get { return localAddress; }
- }
+ internal sealed class PmpNatDevice : AbstractNatDevice, IEquatable<PmpNatDevice>
+ {
+ private IPAddress localAddress;
+ private IPAddress publicAddress;
- public override IPAddress GetExternalIP ()
- {
- return publicAddress;
- }
-
- public override IAsyncResult BeginCreatePortMap(Mapping mapping, AsyncCallback callback, object asyncState)
- {
- PortMapAsyncResult pmar = new PortMapAsyncResult (mapping.Protocol, mapping.PublicPort, PmpConstants.DefaultLeaseTime, callback, asyncState);
- ThreadPool.QueueUserWorkItem (delegate
- {
- try
- {
- CreatePortMap(pmar.Mapping, true);
- pmar.Complete();
- }
- catch (Exception e)
- {
- pmar.Complete(e);
- }
- });
- return pmar;
- }
-
- public override IAsyncResult BeginDeletePortMap (Mapping mapping, AsyncCallback callback, object asyncState)
- {
- PortMapAsyncResult pmar = new PortMapAsyncResult (mapping, callback, asyncState);
- ThreadPool.QueueUserWorkItem (delegate {
- try {
- CreatePortMap(pmar.Mapping, false);
- pmar.Complete();
- } catch (Exception e) {
- pmar.Complete(e);
- }
- });
- return pmar;
- }
-
- public override void EndCreatePortMap (IAsyncResult result)
- {
- PortMapAsyncResult pmar = result as PortMapAsyncResult;
- pmar.AsyncWaitHandle.WaitOne ();
- }
-
- public override void EndDeletePortMap (IAsyncResult result)
- {
- PortMapAsyncResult pmar = result as PortMapAsyncResult;
- pmar.AsyncWaitHandle.WaitOne ();
- }
-
- public override IAsyncResult BeginGetAllMappings (AsyncCallback callback, object asyncState)
- {
- //NAT-PMP does not specify a way to get all port mappings
- throw new NotSupportedException ();
- }
+ internal PmpNatDevice(IPAddress localAddress, IPAddress publicAddress)
+ {
+ this.localAddress = localAddress;
+ this.publicAddress = publicAddress;
+ }
- public override IAsyncResult BeginGetExternalIP (AsyncCallback callback, object asyncState)
- {
- StartOp(ref externalIpResult, callback, asyncState);
- AsyncResult result = externalIpResult;
- result.Complete();
- return result;
- }
+ public override IPAddress LocalAddress
+ {
+ get { return localAddress; }
+ }
- public override IAsyncResult BeginGetSpecificMapping (Protocol protocol, int port, AsyncCallback callback, object asyncState)
- {
- //NAT-PMP does not specify a way to get a specific port map
- throw new NotSupportedException ();
- }
-
- public override Mapping[] EndGetAllMappings (IAsyncResult result)
- {
- //NAT-PMP does not specify a way to get all port mappings
- throw new NotSupportedException ();
- }
+ public override Task CreatePortMap(Mapping mapping)
+ {
+ return InternalCreatePortMapAsync(mapping, true);
+ }
- public override IPAddress EndGetExternalIP (IAsyncResult result)
- {
- EndOp(result, ref externalIpResult);
- return publicAddress;
- }
+ public override bool Equals(object obj)
+ {
+ PmpNatDevice device = obj as PmpNatDevice;
+ return (device == null) ? false : this.Equals(device);
+ }
- private void StartOp(ref AsyncResult result, AsyncCallback callback, object asyncState)
+ public override int GetHashCode()
{
- if (pendingOp == true)
- throw new InvalidOperationException("Can only have one simultaenous async operation");
+ return this.publicAddress.GetHashCode();
+ }
- pendingOp = true;
- result = new AsyncResult(callback, asyncState);
+ public bool Equals(PmpNatDevice other)
+ {
+ return (other == null) ? false : this.publicAddress.Equals(other.publicAddress);
}
- private void EndOp(IAsyncResult supplied, ref AsyncResult actual)
+ private async Task<Mapping> InternalCreatePortMapAsync(Mapping mapping, bool create)
{
- if (supplied == null)
- throw new ArgumentNullException("result");
+ var package = new List<byte>();
+
+ package.Add(PmpConstants.Version);
+ package.Add(mapping.Protocol == Protocol.Tcp ? PmpConstants.OperationCodeTcp : PmpConstants.OperationCodeUdp);
+ package.Add(0); //reserved
+ package.Add(0); //reserved
+ package.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)mapping.PrivatePort)));
+ package.AddRange(
+ BitConverter.GetBytes(create ? IPAddress.HostToNetworkOrder((short)mapping.PublicPort) : (short)0));
+ package.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(mapping.Lifetime)));
+
+ try
+ {
+ byte[] buffer = package.ToArray();
+ int attempt = 0;
+ int delay = PmpConstants.RetryDelay;
- if (supplied != actual)
- throw new ArgumentException("Supplied IAsyncResult does not match the stored result");
+ using (var udpClient = new UdpClient())
+ {
+ var cancellationTokenSource = new CancellationTokenSource();
- if (!supplied.IsCompleted)
- supplied.AsyncWaitHandle.WaitOne();
+ while (attempt < PmpConstants.RetryAttempts)
+ {
+ await udpClient.SendAsync(buffer, buffer.Length,
+ new IPEndPoint(LocalAddress, PmpConstants.ServerPort));
- if (actual.StoredException != null)
- throw actual.StoredException;
+ if (attempt == 0)
+ {
+ Task.Run(() => CreatePortMapListen(udpClient, mapping, cancellationTokenSource.Token));
+ }
- pendingOp = false;
- actual = null;
- }
+ attempt++;
+ delay *= 2;
+ await Task.Delay(delay).ConfigureAwait(false);
+ }
+
+ cancellationTokenSource.Cancel();
+ }
+ }
+ catch (OperationCanceledException)
+ {
- public override Mapping EndGetSpecificMapping (IAsyncResult result)
- {
- //NAT-PMP does not specify a way to get a specific port map
- throw new NotSupportedException ();
- }
-
- public override bool Equals(object obj)
- {
- PmpNatDevice device = obj as PmpNatDevice;
- return (device == null) ? false : this.Equals(device);
- }
-
- public override int GetHashCode ()
- {
- return this.publicAddress.GetHashCode();
- }
+ }
+ catch (Exception e)
+ {
+ string type = create ? "create" : "delete";
+ string message = String.Format("Failed to {0} portmap (protocol={1}, private port={2}) {3}",
+ type,
+ mapping.Protocol,
+ mapping.PrivatePort,
+ e.Message);
+ NatUtility.Log(message);
+ var pmpException = e as MappingException;
+ throw new MappingException(message, pmpException);
+ }
+
+ return mapping;
+ }
- public bool Equals (PmpNatDevice other)
- {
- return (other == null) ? false : this.publicAddress.Equals(other.publicAddress);
- }
+ private async void CreatePortMapListen(UdpClient udpClient, Mapping mapping, CancellationToken cancellationToken)
+ {
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ var result = await udpClient.ReceiveAsync().ConfigureAwait(false);
+ var endPoint = result.RemoteEndPoint;
+ byte[] data = data = result.Buffer;
- private Mapping CreatePortMap (Mapping mapping, bool create)
- {
- List<byte> package = new List<byte> ();
-
- package.Add (PmpConstants.Version);
- package.Add (mapping.Protocol == Protocol.Tcp ? PmpConstants.OperationCodeTcp : PmpConstants.OperationCodeUdp);
- package.Add ((byte)0); //reserved
- package.Add ((byte)0); //reserved
- package.AddRange (BitConverter.GetBytes (IPAddress.HostToNetworkOrder((short)mapping.PrivatePort)));
- package.AddRange (BitConverter.GetBytes (create ? IPAddress.HostToNetworkOrder((short)mapping.PublicPort) : (short)0));
- package.AddRange (BitConverter.GetBytes (IPAddress.HostToNetworkOrder(mapping.Lifetime)));
+ if (data.Length < 16)
+ continue;
- CreatePortMapAsyncState state = new CreatePortMapAsyncState ();
- state.Buffer = package.ToArray ();
- state.Mapping = mapping;
+ if (data[0] != PmpConstants.Version)
+ continue;
- ThreadPool.QueueUserWorkItem (new WaitCallback (CreatePortMapAsync), state);
- WaitHandle.WaitAll (new WaitHandle[] {state.ResetEvent});
-
- if (!state.Success) {
- string type = create ? "create" : "delete";
- throw new MappingException (String.Format ("Failed to {0} portmap (protocol={1}, private port={2}", type, mapping.Protocol, mapping.PrivatePort));
- }
-
- return state.Mapping;
- }
-
- private void CreatePortMapAsync (object obj)
- {
- CreatePortMapAsyncState state = obj as CreatePortMapAsyncState;
-
- UdpClient udpClient = new UdpClient ();
- CreatePortMapListenState listenState = new CreatePortMapListenState (state, udpClient);
+ var opCode = (byte)(data[1] & 127);
- int attempt = 0;
- int delay = PmpConstants.RetryDelay;
-
- ThreadPool.QueueUserWorkItem (new WaitCallback (CreatePortMapListen), listenState);
+ var protocol = Protocol.Tcp;
+ if (opCode == PmpConstants.OperationCodeUdp)
+ protocol = Protocol.Udp;
- while (attempt < PmpConstants.RetryAttempts && !listenState.Success) {
- udpClient.Send (state.Buffer, state.Buffer.Length, new IPEndPoint (localAddress, PmpConstants.ServerPort));
- listenState.UdpClientReady.Set();
+ short resultCode = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 2));
+ int epoch = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(data, 4));
- attempt++;
- delay *= 2;
- Thread.Sleep (delay);
- }
-
- state.Success = listenState.Success;
-
- udpClient.Close ();
- state.ResetEvent.Set ();
- }
-
- private void CreatePortMapListen (object obj)
- {
- CreatePortMapListenState state = obj as CreatePortMapListenState;
+ short privatePort = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 8));
+ short publicPort = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 10));
- UdpClient udpClient = state.UdpClient;
- state.UdpClientReady.WaitOne(); // Evidently UdpClient has some lazy-init Send/Receive race?
- IPEndPoint endPoint = new IPEndPoint (localAddress, PmpConstants.ServerPort);
-
- while (!state.Success)
- {
- byte[] data;
- try
- {
- data = udpClient.Receive(ref endPoint);
- }
- catch (SocketException)
- {
- state.Success = false;
- return;
- }
+ var lifetime = (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(data, 12));
- catch (ObjectDisposedException)
+ if (privatePort < 0 || publicPort < 0 || resultCode != PmpConstants.ResultCodeSuccess)
{
- state.Success = false;
+ var errors = new[]
+ {
+ "Success",
+ "Unsupported Version",
+ "Not Authorized/Refused (e.g. box supports mapping, but user has turned feature off)"
+ ,
+ "Network Failure (e.g. NAT box itself has not obtained a DHCP lease)",
+ "Out of resources (NAT box cannot create any more mappings at this time)",
+ "Unsupported opcode"
+ };
+
+ var errorMsg = errors[resultCode];
+ NatUtility.Log("Error in CreatePortMapListen: " + errorMsg);
return;
}
-
- if (data.Length < 16)
- continue;
- if (data[0] != PmpConstants.Version)
- continue;
-
- byte opCode = (byte)(data[1] & (byte)127);
-
- Protocol protocol = Protocol.Tcp;
- if (opCode == PmpConstants.OperationCodeUdp)
- protocol = Protocol.Udp;
-
- short resultCode = IPAddress.NetworkToHostOrder (BitConverter.ToInt16 (data, 2));
- uint epoch = (uint)IPAddress.NetworkToHostOrder (BitConverter.ToInt32 (data, 4));
-
- int privatePort = IPAddress.NetworkToHostOrder (BitConverter.ToInt16 (data, 8));
- int publicPort = IPAddress.NetworkToHostOrder (BitConverter.ToInt16 (data, 10));
-
- uint lifetime = (uint)IPAddress.NetworkToHostOrder (BitConverter.ToInt32 (data, 12));
-
- if (publicPort < 0 || privatePort < 0 || resultCode != PmpConstants.ResultCodeSuccess)
- {
- state.Success = false;
- return;
- }
-
- if (lifetime == 0)
- {
- //mapping was deleted
- state.Success = true;
- state.Mapping = null;
- return;
- }
- else
- {
- //mapping was created
- //TODO: verify that the private port+protocol are a match
- Mapping mapping = state.Mapping;
- mapping.PublicPort = publicPort;
- mapping.Protocol = protocol;
- mapping.Expiration = DateTime.Now.AddSeconds (lifetime);
-
- state.Success = true;
- }
- }
- }
+ if (lifetime == 0) return; //mapping was deleted
+ //mapping was created
+ //TODO: verify that the private port+protocol are a match
+ mapping.PublicPort = publicPort;
+ mapping.Protocol = protocol;
+ mapping.Expiration = DateTime.Now.AddSeconds(lifetime);
+ return;
+ }
+ }
/// <summary>
/// Overridden.
/// </summary>
/// <returns></returns>
- public override string ToString( )
+ public override string ToString()
{
- return String.Format( "PmpNatDevice - Local Address: {0}, Public IP: {1}, Last Seen: {2}",
- this.localAddress, this.publicAddress, this.LastSeen );
+ return String.Format("PmpNatDevice - Local Address: {0}, Public IP: {1}, Last Seen: {2}",
+ this.localAddress, this.publicAddress, this.LastSeen);
}
-
-
- private class CreatePortMapAsyncState
- {
- internal byte[] Buffer;
- internal ManualResetEvent ResetEvent = new ManualResetEvent (false);
- internal Mapping Mapping;
-
- internal bool Success;
- }
-
- private class CreatePortMapListenState
- {
- internal volatile bool Success;
- internal Mapping Mapping;
- internal UdpClient UdpClient;
- internal ManualResetEvent UdpClientReady;
-
- internal CreatePortMapListenState (CreatePortMapAsyncState state, UdpClient client)
- {
- Mapping = state.Mapping;
- UdpClient = client; UdpClientReady = new ManualResetEvent(false);
- }
- }
- }
+ }
} \ No newline at end of file
diff --git a/Mono.Nat/Pmp/Searchers/PmpSearcher.cs b/Mono.Nat/Pmp/Searchers/PmpSearcher.cs
index df0273ccb8..4a8a904120 100644
--- a/Mono.Nat/Pmp/Searchers/PmpSearcher.cs
+++ b/Mono.Nat/Pmp/Searchers/PmpSearcher.cs
@@ -37,10 +37,11 @@ using Mono.Nat.Pmp;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Linq;
+using System.Threading.Tasks;
namespace Mono.Nat
{
- internal class PmpSearcher : Pmp.Pmp, ISearcher
+ internal class PmpSearcher : ISearcher
{
static PmpSearcher instance = new PmpSearcher();
@@ -60,18 +61,95 @@ namespace Mono.Nat
CreateSocketsAndAddGateways();
}
+ public static List<UdpClient> sockets;
+ protected static Dictionary<UdpClient, List<IPEndPoint>> gatewayLists;
+
+ internal static void CreateSocketsAndAddGateways()
+ {
+ sockets = new List<UdpClient>();
+ gatewayLists = new Dictionary<UdpClient, List<IPEndPoint>>();
+
+ try
+ {
+ foreach (NetworkInterface n in NetworkInterface.GetAllNetworkInterfaces())
+ {
+ if (n.OperationalStatus != OperationalStatus.Up && n.OperationalStatus != OperationalStatus.Unknown)
+ continue;
+ IPInterfaceProperties properties = n.GetIPProperties();
+ List<IPEndPoint> gatewayList = new List<IPEndPoint>();
+
+ foreach (GatewayIPAddressInformation gateway in properties.GatewayAddresses)
+ {
+ if (gateway.Address.AddressFamily == AddressFamily.InterNetwork)
+ {
+ gatewayList.Add(new IPEndPoint(gateway.Address, PmpConstants.ServerPort));
+ }
+ }
+ if (gatewayList.Count == 0)
+ {
+ /* Mono on OSX doesn't give any gateway addresses, so check DNS entries */
+ foreach (var gw2 in properties.DnsAddresses)
+ {
+ if (gw2.AddressFamily == AddressFamily.InterNetwork)
+ {
+ gatewayList.Add(new IPEndPoint(gw2, PmpConstants.ServerPort));
+ }
+ }
+ foreach (var unicast in properties.UnicastAddresses)
+ {
+ if (/*unicast.DuplicateAddressDetectionState == DuplicateAddressDetectionState.Preferred
+ && unicast.AddressPreferredLifetime != UInt32.MaxValue
+ && */unicast.Address.AddressFamily == AddressFamily.InterNetwork)
+ {
+ var bytes = unicast.Address.GetAddressBytes();
+ bytes[3] = 1;
+ gatewayList.Add(new IPEndPoint(new IPAddress(bytes), PmpConstants.ServerPort));
+ }
+ }
+ }
+
+ if (gatewayList.Count > 0)
+ {
+ foreach (UnicastIPAddressInformation address in properties.UnicastAddresses)
+ {
+ if (address.Address.AddressFamily == AddressFamily.InterNetwork)
+ {
+ UdpClient client;
+
+ try
+ {
+ client = new UdpClient(new IPEndPoint(address.Address, 0));
+ }
+ catch (SocketException)
+ {
+ continue; // Move on to the next address.
+ }
+
+ gatewayLists.Add(client, gatewayList);
+ sockets.Add(client);
+ }
+ }
+ }
+ }
+ }
+ catch (Exception)
+ {
+ // NAT-PMP does not use multicast, so there isn't really a good fallback.
+ }
+ }
+
PmpSearcher()
{
timeout = 250;
}
- public void Search()
+ public async void Search()
{
foreach (UdpClient s in sockets)
{
try
{
- Search(s);
+ await Search(s).ConfigureAwait(false);
}
catch
{
@@ -80,7 +158,7 @@ namespace Mono.Nat
}
}
- void Search (UdpClient client)
+ async Task Search (UdpClient client)
{
// Sort out the time for the next search first. The spec says the
// timeout should double after each attempt. Once it reaches 64 seconds
@@ -98,8 +176,10 @@ namespace Mono.Nat
// The nat-pmp search message. Must be sent to GatewayIP:53531
byte[] buffer = new byte[] { PmpConstants.Version, PmpConstants.OperationCode };
- foreach (IPEndPoint gatewayEndpoint in gatewayLists[client])
- client.Send(buffer, buffer.Length, gatewayEndpoint);
+ foreach (IPEndPoint gatewayEndpoint in gatewayLists[client])
+ {
+ await client.SendAsync(buffer, buffer.Length, gatewayEndpoint).ConfigureAwait(false);
+ }
}
bool IsSearchAddress(IPAddress address)
diff --git a/Mono.Nat/Properties/AssemblyInfo.cs b/Mono.Nat/Properties/AssemblyInfo.cs
index c3c3101de0..2a4e75c210 100644
--- a/Mono.Nat/Properties/AssemblyInfo.cs
+++ b/Mono.Nat/Properties/AssemblyInfo.cs
@@ -2,30 +2,15 @@
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("Mono.Nat")]
-[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Mono.Nat")]
-[assembly: AssemblyCopyright("Copyright © 2016")]
[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("d7453b88-2266-4805-b39b-2b5a2a33e1ba")]
-
-// 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/Mono.Nat/Upnp/AsyncResults/GetAllMappingsAsyncResult.cs b/Mono.Nat/Upnp/AsyncResults/GetAllMappingsAsyncResult.cs
deleted file mode 100644
index 51ecfbaf09..0000000000
--- a/Mono.Nat/Upnp/AsyncResults/GetAllMappingsAsyncResult.cs
+++ /dev/null
@@ -1,56 +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 System.Collections.Generic;
-using System.Text;
-using System.Net;
-
-namespace Mono.Nat.Upnp
-{
- internal class GetAllMappingsAsyncResult : PortMapAsyncResult
- {
- private List<Mapping> mappings;
- private Mapping specificMapping;
-
- public GetAllMappingsAsyncResult(WebRequest request, AsyncCallback callback, object asyncState)
- : base(request, callback, asyncState)
- {
- mappings = new List<Mapping>();
- }
-
- public List<Mapping> Mappings
- {
- get { return this.mappings; }
- }
-
- public Mapping SpecificMapping
- {
- get { return this.specificMapping; }
- set { this.specificMapping = value; }
- }
- }
-}
diff --git a/Mono.Nat/Upnp/AsyncResults/PortMapAsyncResult.cs b/Mono.Nat/Upnp/AsyncResults/PortMapAsyncResult.cs
deleted file mode 100644
index d8ac3fe612..0000000000
--- a/Mono.Nat/Upnp/AsyncResults/PortMapAsyncResult.cs
+++ /dev/null
@@ -1,75 +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 System.Net;
-using System.Threading;
-
-namespace Mono.Nat.Upnp
-{
- internal class PortMapAsyncResult : AsyncResult
- {
- private WebRequest request;
- private MessageBase savedMessage;
-
- protected PortMapAsyncResult(WebRequest request, AsyncCallback callback, object asyncState)
- : base (callback, asyncState)
- {
- this.request = request;
- }
-
- internal WebRequest Request
- {
- get { return this.request; }
- set { this.request = value; }
- }
-
- internal MessageBase SavedMessage
- {
- get { return this.savedMessage; }
- set { this.savedMessage = value; }
- }
-
- internal static PortMapAsyncResult Create (MessageBase message, WebRequest request, AsyncCallback storedCallback, object asyncState)
- {
- if (message is GetGenericPortMappingEntry)
- return new GetAllMappingsAsyncResult(request, storedCallback, asyncState);
-
- if (message is GetSpecificPortMappingEntryMessage)
- {
- GetSpecificPortMappingEntryMessage mapMessage = (GetSpecificPortMappingEntryMessage)message;
- GetAllMappingsAsyncResult result = new GetAllMappingsAsyncResult(request, storedCallback, asyncState);
-
- result.SpecificMapping = new Mapping(mapMessage.protocol, 0, mapMessage.externalPort, 0);
- return result;
- }
-
- return new PortMapAsyncResult(request, storedCallback, asyncState);
- }
- }
-}
diff --git a/Mono.Nat/Upnp/Mappers/UpnpMapper.cs b/Mono.Nat/Upnp/Mappers/UpnpMapper.cs
deleted file mode 100644
index 6f27168055..0000000000
--- a/Mono.Nat/Upnp/Mappers/UpnpMapper.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-//
-// Authors:
-// Nicholas Terry <nick.i.terry@gmail.com>
-//
-// 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.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using System.Net;
-using System.Net.Sockets;
-using System.Text;
-using System.Threading;
-
-namespace Mono.Nat.Upnp.Mappers
-{
- internal class UpnpMapper : Upnp, IMapper
- {
-
- public event EventHandler<DeviceEventArgs> DeviceFound;
-
- public UdpClient Client { get; set; }
-
- public UpnpMapper()
- {
- //Bind to local port 1900 for ssdp responses
- Client = new UdpClient(1900);
- }
-
- public void Map(IPAddress gatewayAddress)
- {
- //Get the httpu request payload
- byte[] data = DiscoverDeviceMessage.EncodeUnicast(gatewayAddress);
-
- Client.Send(data, data.Length, new IPEndPoint(gatewayAddress, 1900));
-
- new Thread(Receive).Start();
- }
-
- public void Receive()
- {
- while (true)
- {
- IPEndPoint received = new IPEndPoint(IPAddress.Parse("192.168.0.1"), 5351);
- if (Client.Available > 0)
- {
- IPAddress localAddress = ((IPEndPoint)Client.Client.LocalEndPoint).Address;
- byte[] data = Client.Receive(ref received);
- Handle(localAddress, data, received);
- }
- }
- }
-
- public void Handle(IPAddress localAddres, byte[] response)
- {
- Handle(localAddres, response, null);
- }
-
- public void Handle(IPAddress localAddress, byte[] response, 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.
- try
- {
- UpnpNatDevice d = base.Handle(localAddress, response, endpoint);
- d.GetServicesList(DeviceSetupComplete);
- }
- catch (Exception ex)
- {
- Trace.WriteLine("Unhandled exception when trying to decode a device's response Send me the following data: ");
- Trace.WriteLine("ErrorMessage:");
- Trace.WriteLine(ex.Message);
- Trace.WriteLine("Data string:");
- Trace.WriteLine(Encoding.UTF8.GetString(response));
- }
- }
-
- private void DeviceSetupComplete(INatDevice device)
- {
- OnDeviceFound(new DeviceEventArgs(device));
- }
-
- private void OnDeviceFound(DeviceEventArgs args)
- {
- if (DeviceFound != null)
- DeviceFound(this, args);
- }
- }
-}
diff --git a/Mono.Nat/Upnp/Messages/ErrorMessage.cs b/Mono.Nat/Upnp/Messages/ErrorMessage.cs
index ce5270e9b9..7c0c44d8e8 100644
--- a/Mono.Nat/Upnp/Messages/ErrorMessage.cs
+++ b/Mono.Nat/Upnp/Messages/ErrorMessage.cs
@@ -25,6 +25,7 @@
//
using System;
+using MediaBrowser.Common.Net;
namespace Mono.Nat.Upnp
{
@@ -54,8 +55,7 @@ namespace Mono.Nat.Upnp
}
#endregion
-
- public override System.Net.WebRequest Encode(out byte[] body)
+ public override HttpRequestOptions Encode()
{
throw new NotImplementedException();
}
diff --git a/Mono.Nat/Upnp/Messages/GetServicesMessage.cs b/Mono.Nat/Upnp/Messages/GetServicesMessage.cs
index c5d7bce70c..9d29f98fdf 100644
--- a/Mono.Nat/Upnp/Messages/GetServicesMessage.cs
+++ b/Mono.Nat/Upnp/Messages/GetServicesMessage.cs
@@ -27,6 +27,8 @@
using System;
using System.Diagnostics;
using System.Net;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Logging;
namespace Mono.Nat.Upnp
{
@@ -34,28 +36,37 @@ namespace Mono.Nat.Upnp
{
private string servicesDescriptionUrl;
private EndPoint hostAddress;
+ private readonly ILogger _logger;
- public GetServicesMessage(string description, EndPoint hostAddress)
- :base(null)
+ public GetServicesMessage(string description, EndPoint hostAddress, ILogger logger)
+ : base(null)
{
if (string.IsNullOrEmpty(description))
- Trace.WriteLine("Description is null");
+ _logger.Warn("Description is null");
if (hostAddress == null)
- Trace.WriteLine("hostaddress is null");
+ _logger.Warn("hostaddress is null");
this.servicesDescriptionUrl = description;
this.hostAddress = hostAddress;
+ _logger = logger;
}
+ public override string Method
+ {
+ get
+ {
+ return "GET";
+ }
+ }
- public override WebRequest Encode(out byte[] body)
+ public override HttpRequestOptions Encode()
{
- HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://" + this.hostAddress.ToString() + this.servicesDescriptionUrl);
- req.Headers.Add("ACCEPT-LANGUAGE", "en");
- req.Method = "GET";
+ var req = new HttpRequestOptions();
+
+ req.Url = "http://" + this.hostAddress.ToString() + this.servicesDescriptionUrl;
+ req.RequestHeaders.Add("ACCEPT-LANGUAGE", "en");
- body = new byte[0];
return req;
}
}
diff --git a/Mono.Nat/Upnp/Messages/Requests/CreatePortMappingMessage.cs b/Mono.Nat/Upnp/Messages/Requests/CreatePortMappingMessage.cs
index da650fb418..e9caa916d1 100644
--- a/Mono.Nat/Upnp/Messages/Requests/CreatePortMappingMessage.cs
+++ b/Mono.Nat/Upnp/Messages/Requests/CreatePortMappingMessage.cs
@@ -29,6 +29,7 @@ using System.IO;
using System.Globalization;
using System.Text;
using System.Xml;
+using MediaBrowser.Common.Net;
namespace Mono.Nat.Upnp
{
@@ -51,8 +52,7 @@ namespace Mono.Nat.Upnp
}
#endregion
-
- public override WebRequest Encode(out byte[] body)
+ public override HttpRequestOptions Encode()
{
CultureInfo culture = CultureInfo.InvariantCulture;
@@ -69,7 +69,7 @@ namespace Mono.Nat.Upnp
WriteFullElement(writer, "NewLeaseDuration", mapping.Lifetime.ToString());
writer.Flush();
- return CreateRequest("AddPortMapping", builder.ToString(), out body);
+ return CreateRequest("AddPortMapping", builder.ToString());
}
}
}
diff --git a/Mono.Nat/Upnp/Messages/Requests/DeletePortMappingMessage.cs b/Mono.Nat/Upnp/Messages/Requests/DeletePortMappingMessage.cs
deleted file mode 100644
index d9be89a693..0000000000
--- a/Mono.Nat/Upnp/Messages/Requests/DeletePortMappingMessage.cs
+++ /dev/null
@@ -1,57 +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.IO;
-using System.Text;
-using System.Xml;
-
-namespace Mono.Nat.Upnp
-{
- internal class DeletePortMappingMessage : MessageBase
- {
- private Mapping mapping;
-
- public DeletePortMappingMessage(Mapping mapping, UpnpNatDevice device)
- : base(device)
- {
- this.mapping = mapping;
- }
-
- public override WebRequest Encode(out byte[] body)
- {
- StringBuilder builder = new StringBuilder(256);
- XmlWriter writer = CreateWriter(builder);
-
- WriteFullElement(writer, "NewRemoteHost", string.Empty);
- WriteFullElement(writer, "NewExternalPort", mapping.PublicPort.ToString(MessageBase.Culture));
- WriteFullElement(writer, "NewProtocol", mapping.Protocol == Protocol.Tcp ? "TCP" : "UDP");
-
- writer.Flush();
- return CreateRequest("DeletePortMapping", builder.ToString(), out body);
- }
- }
-}
diff --git a/Mono.Nat/Upnp/Messages/Requests/GetExternalIPAddressMessage.cs b/Mono.Nat/Upnp/Messages/Requests/GetExternalIPAddressMessage.cs
deleted file mode 100644
index 8f97002ea3..0000000000
--- a/Mono.Nat/Upnp/Messages/Requests/GetExternalIPAddressMessage.cs
+++ /dev/null
@@ -1,51 +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 System.Collections.Generic;
-using System.Text;
-using System.Net;
-using System.IO;
-
-namespace Mono.Nat.Upnp
-{
- internal class GetExternalIPAddressMessage : MessageBase
- {
-
- #region Constructors
- public GetExternalIPAddressMessage(UpnpNatDevice device)
- :base(device)
- {
- }
- #endregion
-
-
- public override WebRequest Encode(out byte[] body)
- {
- return CreateRequest("GetExternalIPAddress", string.Empty, out body);
- }
- }
-}
diff --git a/Mono.Nat/Upnp/Messages/Requests/GetGenericPortMappingEntry.cs b/Mono.Nat/Upnp/Messages/Requests/GetGenericPortMappingEntry.cs
deleted file mode 100644
index c0c555881b..0000000000
--- a/Mono.Nat/Upnp/Messages/Requests/GetGenericPortMappingEntry.cs
+++ /dev/null
@@ -1,55 +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 System.Collections.Generic;
-using System.Text;
-using System.Xml;
-
-namespace Mono.Nat.Upnp
-{
- internal class GetGenericPortMappingEntry : MessageBase
- {
- private int index;
-
- public GetGenericPortMappingEntry(int index, UpnpNatDevice device)
- :base(device)
- {
- this.index = index;
- }
-
- public override System.Net.WebRequest Encode(out byte[] body)
- {
- StringBuilder sb = new StringBuilder(128);
- XmlWriter writer = CreateWriter(sb);
-
- WriteFullElement(writer, "NewPortMappingIndex", index.ToString());
-
- writer.Flush();
- return CreateRequest("GetGenericPortMappingEntry", sb.ToString(), out body);
- }
- }
-}
diff --git a/Mono.Nat/Upnp/Messages/Requests/GetSpecificPortMappingEntryMessage.cs b/Mono.Nat/Upnp/Messages/Requests/GetSpecificPortMappingEntryMessage.cs
deleted file mode 100644
index 314468ece1..0000000000
--- a/Mono.Nat/Upnp/Messages/Requests/GetSpecificPortMappingEntryMessage.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;
-using System.Collections.Generic;
-using System.Text;
-using System.Xml;
-using System.Net;
-
-namespace Mono.Nat.Upnp
-{
- internal class GetSpecificPortMappingEntryMessage : MessageBase
- {
- internal Protocol protocol;
- internal int externalPort;
-
- public GetSpecificPortMappingEntryMessage(Protocol protocol, int externalPort, UpnpNatDevice device)
- : base(device)
- {
- this.protocol = protocol;
- this.externalPort = externalPort;
- }
-
- public override WebRequest Encode(out byte[] body)
- {
- StringBuilder sb = new StringBuilder(64);
- XmlWriter writer = CreateWriter(sb);
-
- WriteFullElement(writer, "NewRemoteHost", string.Empty);
- WriteFullElement(writer, "NewExternalPort", externalPort.ToString());
- WriteFullElement(writer, "NewProtocol", protocol == Protocol.Tcp ? "TCP" : "UDP");
- writer.Flush();
-
- return CreateRequest("GetSpecificPortMappingEntry", sb.ToString(), out body);
- }
- }
-}
diff --git a/Mono.Nat/Upnp/Messages/Responses/CreatePortMappingResponseMessage.cs b/Mono.Nat/Upnp/Messages/Responses/CreatePortMappingResponseMessage.cs
index e75926b090..48776dd6f9 100644
--- a/Mono.Nat/Upnp/Messages/Responses/CreatePortMappingResponseMessage.cs
+++ b/Mono.Nat/Upnp/Messages/Responses/CreatePortMappingResponseMessage.cs
@@ -27,6 +27,8 @@
using System;
+using MediaBrowser.Common.Net;
+
namespace Mono.Nat.Upnp
{
internal class CreatePortMappingResponseMessage : MessageBase
@@ -38,7 +40,7 @@ namespace Mono.Nat.Upnp
}
#endregion
- public override System.Net.WebRequest Encode(out byte[] body)
+ public override HttpRequestOptions Encode()
{
throw new NotImplementedException();
}
diff --git a/Mono.Nat/Upnp/Messages/Responses/DeletePortMappingResponseMessage.cs b/Mono.Nat/Upnp/Messages/Responses/DeletePortMappingResponseMessage.cs
deleted file mode 100644
index 1fce4eb044..0000000000
--- a/Mono.Nat/Upnp/Messages/Responses/DeletePortMappingResponseMessage.cs
+++ /dev/null
@@ -1,44 +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.Upnp
-{
- internal class DeletePortMapResponseMessage : MessageBase
- {
- public DeletePortMapResponseMessage()
- :base(null)
- {
- }
-
- public override System.Net.WebRequest Encode(out byte[] body)
- {
- throw new NotSupportedException();
- }
- }
-}
diff --git a/Mono.Nat/Upnp/Messages/Responses/GetExternalIPAddressResponseMessage.cs b/Mono.Nat/Upnp/Messages/Responses/GetExternalIPAddressResponseMessage.cs
deleted file mode 100644
index ee4b18cd10..0000000000
--- a/Mono.Nat/Upnp/Messages/Responses/GetExternalIPAddressResponseMessage.cs
+++ /dev/null
@@ -1,53 +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 System.Collections.Generic;
-using System.Text;
-using System.Net;
-
-namespace Mono.Nat.Upnp
-{
- internal class GetExternalIPAddressResponseMessage : MessageBase
- {
- public IPAddress ExternalIPAddress
- {
- get { return this.externalIPAddress; }
- }
- private IPAddress externalIPAddress;
-
- public GetExternalIPAddressResponseMessage(string ip)
- :base(null)
- {
- this.externalIPAddress = IPAddress.Parse(ip);
- }
-
- public override WebRequest Encode(out byte[] body)
- {
- throw new NotImplementedException();
- }
- }
-}
diff --git a/Mono.Nat/Upnp/Messages/Responses/GetGenericPortMappingEntryResponseMessage.cs b/Mono.Nat/Upnp/Messages/Responses/GetGenericPortMappingEntryResponseMessage.cs
deleted file mode 100644
index b11bfa0278..0000000000
--- a/Mono.Nat/Upnp/Messages/Responses/GetGenericPortMappingEntryResponseMessage.cs
+++ /dev/null
@@ -1,108 +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 System.Collections.Generic;
-using System.Text;
-using System.Xml;
-
-namespace Mono.Nat.Upnp
-{
- internal class GetGenericPortMappingEntryResponseMessage : MessageBase
- {
- private string remoteHost;
- private int externalPort;
- private Protocol protocol;
- private int internalPort;
- private string internalClient;
- private bool enabled;
- private string portMappingDescription;
- private int leaseDuration;
-
- public string RemoteHost
- {
- get { return this.remoteHost; }
- }
-
- public int ExternalPort
- {
- get { return this.externalPort; }
- }
-
- public Protocol Protocol
- {
- get { return this.protocol; }
- }
-
- public int InternalPort
- {
- get { return this.internalPort; }
- }
-
- public string InternalClient
- {
- get { return this.internalClient; }
- }
-
- public bool Enabled
- {
- get { return this.enabled; }
- }
-
- public string PortMappingDescription
- {
- get { return this.portMappingDescription; }
- }
-
- public int LeaseDuration
- {
- get { return this.leaseDuration; }
- }
-
-
- public GetGenericPortMappingEntryResponseMessage(XmlNode data, bool genericMapping)
- : base(null)
- {
- remoteHost = (genericMapping) ? data["NewRemoteHost"].InnerText : string.Empty;
- externalPort = (genericMapping) ? Convert.ToInt32(data["NewExternalPort"].InnerText) : -1;
- if (genericMapping)
- protocol = data["NewProtocol"].InnerText.Equals("TCP", StringComparison.InvariantCultureIgnoreCase) ? Protocol.Tcp : Protocol.Udp;
- else
- protocol = Protocol.Udp;
-
- internalPort = Convert.ToInt32(data["NewInternalPort"].InnerText);
- internalClient = data["NewInternalClient"].InnerText;
- enabled = data["NewEnabled"].InnerText == "1" ? true : false;
- portMappingDescription = data["NewPortMappingDescription"].InnerText;
- leaseDuration = Convert.ToInt32(data["NewLeaseDuration"].InnerText);
- }
-
- public override System.Net.WebRequest Encode(out byte[] body)
- {
- throw new NotImplementedException();
- }
- }
-}
diff --git a/Mono.Nat/Upnp/Messages/UpnpMessage.cs b/Mono.Nat/Upnp/Messages/UpnpMessage.cs
index 44c16eec60..54cca44947 100644
--- a/Mono.Nat/Upnp/Messages/UpnpMessage.cs
+++ b/Mono.Nat/Upnp/Messages/UpnpMessage.cs
@@ -31,6 +31,7 @@ using System.Net;
using System.IO;
using System.Text;
using System.Globalization;
+using MediaBrowser.Common.Net;
namespace Mono.Nat.Upnp
{
@@ -44,17 +45,16 @@ namespace Mono.Nat.Upnp
this.device = device;
}
- protected WebRequest CreateRequest(string upnpMethod, string methodParameters, out byte[] body)
+ protected HttpRequestOptions CreateRequest(string upnpMethod, string methodParameters)
{
string ss = "http://" + this.device.HostEndPoint.ToString() + this.device.ControlUrl;
NatUtility.Log("Initiating request to: {0}", ss);
- Uri location = new Uri(ss);
- HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(location);
- req.KeepAlive = false;
- req.Method = "POST";
- req.ContentType = "text/xml; charset=\"utf-8\"";
- req.Headers.Add("SOAPACTION", "\"" + device.ServiceType + "#" + upnpMethod + "\"");
+ var req = new HttpRequestOptions();
+ req.Url = ss;
+ req.EnableKeepAlive = false;
+ req.RequestContentType = "text/xml; charset=\"utf-8\"";
+ req.RequestHeaders.Add("SOAPACTION", "\"" + device.ServiceType + "#" + upnpMethod + "\"");
string bodyString = "<s:Envelope "
+ "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
@@ -67,54 +67,17 @@ namespace Mono.Nat.Upnp
+ "</s:Body>"
+ "</s:Envelope>\r\n\r\n";
- body = System.Text.Encoding.UTF8.GetBytes(bodyString);
+ req.RequestContentBytes = System.Text.Encoding.UTF8.GetBytes(bodyString);
return req;
}
- public static MessageBase Decode(UpnpNatDevice device, string message)
- {
- XmlNode node;
- XmlDocument doc = new XmlDocument();
- doc.LoadXml(message);
-
- XmlNamespaceManager nsm = new XmlNamespaceManager(doc.NameTable);
-
- // Error messages should be found under this namespace
- nsm.AddNamespace("errorNs", "urn:schemas-upnp-org:control-1-0");
- nsm.AddNamespace("responseNs", device.ServiceType);
-
- // Check to see if we have a fault code message.
- if ((node = doc.SelectSingleNode("//errorNs:UPnPError", nsm)) != null) {
- string errorCode = node["errorCode"] != null ? node["errorCode"].InnerText : "";
- string errorDescription = node["errorDescription"] != null ? node["errorDescription"].InnerText : "";
-
- return new ErrorMessage(Convert.ToInt32(errorCode, CultureInfo.InvariantCulture), errorDescription);
- }
-
- if ((doc.SelectSingleNode("//responseNs:AddPortMappingResponse", nsm)) != null)
- return new CreatePortMappingResponseMessage();
+ public abstract HttpRequestOptions Encode();
- if ((doc.SelectSingleNode("//responseNs:DeletePortMappingResponse", nsm)) != null)
- return new DeletePortMapResponseMessage();
-
- if ((node = doc.SelectSingleNode("//responseNs:GetExternalIPAddressResponse", nsm)) != null) {
- string newExternalIPAddress = node["NewExternalIPAddress"] != null ? node["NewExternalIPAddress"].InnerText : "";
- return new GetExternalIPAddressResponseMessage(newExternalIPAddress);
- }
-
- if ((node = doc.SelectSingleNode("//responseNs:GetGenericPortMappingEntryResponse", nsm)) != null)
- return new GetGenericPortMappingEntryResponseMessage(node, true);
-
- if ((node = doc.SelectSingleNode("//responseNs:GetSpecificPortMappingEntryResponse", nsm)) != null)
- return new GetGenericPortMappingEntryResponseMessage(node, false);
-
- NatUtility.Log("Unknown message returned. Please send me back the following XML:");
- NatUtility.Log(message);
- return null;
+ public virtual string Method
+ {
+ get { return "POST"; }
}
- public abstract WebRequest Encode(out byte[] body);
-
internal static void WriteFullElement(XmlWriter writer, string element, string value)
{
writer.WriteStartElement(element);
diff --git a/Mono.Nat/Upnp/Searchers/UpnpSearcher.cs b/Mono.Nat/Upnp/Searchers/UpnpSearcher.cs
index edc5a5d76a..5e36410c58 100644
--- a/Mono.Nat/Upnp/Searchers/UpnpSearcher.cs
+++ b/Mono.Nat/Upnp/Searchers/UpnpSearcher.cs
@@ -36,97 +36,31 @@ using Mono.Nat.Upnp;
using System.Diagnostics;
using System.Net.Sockets;
using System.Net.NetworkInformation;
-using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Dlna;
namespace Mono.Nat
{
internal class UpnpSearcher : ISearcher
{
- private const int SearchPeriod = 5 * 60; // The time in seconds between each search
- static UpnpSearcher instance = new UpnpSearcher();
- public static List<UdpClient> sockets = CreateSockets();
-
- public static UpnpSearcher Instance
- {
- get { return instance; }
- }
-
public event EventHandler<DeviceEventArgs> DeviceFound;
public event EventHandler<DeviceEventArgs> DeviceLost;
- private List<INatDevice> devices;
- private Dictionary<IPAddress, DateTime> lastFetched;
private DateTime nextSearch;
- private IPEndPoint searchEndpoint;
+ private readonly ILogger _logger;
+ private readonly IHttpClient _httpClient;
- UpnpSearcher()
+ public UpnpSearcher(ILogger logger, IHttpClient httpClient)
{
- devices = new List<INatDevice>();
- lastFetched = new Dictionary<IPAddress, DateTime>();
- //searchEndpoint = new IPEndPoint(IPAddress.Parse("239.255.255.250"), 1900);
- searchEndpoint = new IPEndPoint(IPAddress.Parse("239.255.255.250"), 1900);
+ _logger = logger;
+ _httpClient = httpClient;
}
- static List<UdpClient> CreateSockets()
- {
- List<UdpClient> clients = new List<UdpClient>();
- try
- {
- foreach (NetworkInterface n in NetworkInterface.GetAllNetworkInterfaces())
- {
- foreach (UnicastIPAddressInformation address in n.GetIPProperties().UnicastAddresses)
- {
- if (address.Address.AddressFamily == AddressFamily.InterNetwork)
- {
- try
- {
- clients.Add(new UdpClient(new IPEndPoint(address.Address, 0)));
- }
- catch
- {
- continue; // Move on to the next address.
- }
- }
- }
- }
- }
- catch (Exception)
- {
- clients.Add(new UdpClient(0));
- }
- return clients;
- }
-
public void Search()
{
- foreach (UdpClient s in sockets)
- {
- try
- {
- Search(s);
- }
- catch
- {
- // Ignore any search errors
- }
- }
}
- void Search(UdpClient client)
- {
- nextSearch = DateTime.Now.AddSeconds(SearchPeriod);
- byte[] data = DiscoverDeviceMessage.EncodeSSDP();
-
- // UDP is unreliable, so send 3 requests at a time (per Upnp spec, sec 1.1.2)
- for (int i = 0; i < 3; i++)
- client.Send(data, data.Length, searchEndpoint);
- }
-
- public IPEndPoint SearchEndpoint
- {
- get { return searchEndpoint; }
- }
-
public void Handle(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint endpoint)
{
// No matter what, this method should never throw an exception. If something goes wrong
@@ -145,113 +79,19 @@ namespace Mono.Nat
prefix. */
// We have an internet gateway device now
- UpnpNatDevice d = new UpnpNatDevice(localAddress, deviceInfo, endpoint, string.Empty);
+ UpnpNatDevice d = new UpnpNatDevice(localAddress, deviceInfo, endpoint, string.Empty, _logger, _httpClient);
- if (devices.Contains(d))
- {
- // We already have found this device, so we just refresh it to let people know it's
- // Still alive. If a device doesn't respond to a search, we dump it.
- devices[devices.IndexOf(d)].LastSeen = DateTime.Now;
- }
- else
- {
-
- // If we send 3 requests at a time, ensure we only fetch the services list once
- // even if three responses are received
- if (lastFetched.ContainsKey(endpoint.Address))
- {
- DateTime last = lastFetched[endpoint.Address];
- if ((DateTime.Now - last) < TimeSpan.FromSeconds(20))
- return;
- }
- lastFetched[endpoint.Address] = DateTime.Now;
-
- // Once we've parsed the information we need, we tell the device to retrieve it's service list
- // Once we successfully receive the service list, the callback provided will be invoked.
- NatUtility.Log("Fetching service list: {0}", d.HostEndPoint);
- d.GetServicesList(DeviceSetupComplete);
- }
+ NatUtility.Log("Fetching service list: {0}", d.HostEndPoint);
+ OnDeviceFound(new DeviceEventArgs(d));
}
catch (Exception ex)
{
- NatUtility.Log("Unhandled exception when trying to decode a device's response Send me the following data: ");
- NatUtility.Log("ErrorMessage:");
- NatUtility.Log(ex.Message);
+ _logger.ErrorException("Error decoding device response", ex);
}
}
public void Handle(IPAddress localAddress, byte[] response, IPEndPoint endpoint)
{
- // Convert it to a string for easy parsing
- string dataString = null;
-
- // 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.
- try {
- string urn;
- dataString = Encoding.UTF8.GetString(response);
-
- if (NatUtility.Verbose)
- NatUtility.Log("UPnP Response: {0}", dataString);
-
- /* For UPnP Port Mapping we need ot find either WANPPPConnection or WANIPConnection.
- Any other device type is no good to us for this purpose. See the IGP overview paper
- page 5 for an overview of device types and their hierarchy.
- http://upnp.org/specs/gw/UPnP-gw-InternetGatewayDevice-v1-Device.pdf */
-
- /* TODO: Currently we are assuming version 1 of the protocol. We should figure out which
- version it is and apply the correct URN. */
-
- /* Some routers don't correctly implement the version ID on the URN, so we only search for the type
- prefix. */
-
- string log = "UPnP Response: Router advertised a '{0}' service";
- StringComparison c = StringComparison.OrdinalIgnoreCase;
- if (dataString.IndexOf("urn:schemas-upnp-org:service:WANIPConnection:", c) != -1) {
- urn = "urn:schemas-upnp-org:service:WANIPConnection:1";
- NatUtility.Log(log, "urn:schemas-upnp-org:service:WANIPConnection:1");
- } else if (dataString.IndexOf("urn:schemas-upnp-org:service:WANPPPConnection:", c) != -1) {
- urn = "urn:schemas-upnp-org:service:WANPPPConnection:1";
- NatUtility.Log(log, "urn:schemas-upnp-org:service:WANPPPConnection:");
- } else
- return;
-
- // We have an internet gateway device now
- UpnpNatDevice d = new UpnpNatDevice(localAddress, dataString, urn);
-
- if (devices.Contains(d))
- {
- // We already have found this device, so we just refresh it to let people know it's
- // Still alive. If a device doesn't respond to a search, we dump it.
- devices[devices.IndexOf(d)].LastSeen = DateTime.Now;
- }
- else
- {
-
- // If we send 3 requests at a time, ensure we only fetch the services list once
- // even if three responses are received
- if (lastFetched.ContainsKey(endpoint.Address))
- {
- DateTime last = lastFetched[endpoint.Address];
- if ((DateTime.Now - last) < TimeSpan.FromSeconds(20))
- return;
- }
- lastFetched[endpoint.Address] = DateTime.Now;
-
- // Once we've parsed the information we need, we tell the device to retrieve it's service list
- // Once we successfully receive the service list, the callback provided will be invoked.
- NatUtility.Log("Fetching service list: {0}", d.HostEndPoint);
- d.GetServicesList(DeviceSetupComplete);
- }
- }
- catch (Exception ex)
- {
- Trace.WriteLine("Unhandled exception when trying to decode a device's response Send me the following data: ");
- Trace.WriteLine("ErrorMessage:");
- Trace.WriteLine(ex.Message);
- Trace.WriteLine("Data string:");
- Trace.WriteLine(dataString);
- }
}
public DateTime NextSearch
@@ -259,20 +99,6 @@ namespace Mono.Nat
get { return nextSearch; }
}
- private void DeviceSetupComplete(INatDevice device)
- {
- lock (this.devices)
- {
- // We don't want the same device in there twice
- if (devices.Contains(device))
- return;
-
- devices.Add(device);
- }
-
- OnDeviceFound(new DeviceEventArgs(device));
- }
-
private void OnDeviceFound(DeviceEventArgs args)
{
if (DeviceFound != null)
diff --git a/Mono.Nat/Upnp/Upnp.cs b/Mono.Nat/Upnp/Upnp.cs
index e44a51c24d..38d949250f 100644
--- a/Mono.Nat/Upnp/Upnp.cs
+++ b/Mono.Nat/Upnp/Upnp.cs
@@ -33,12 +33,24 @@ using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Logging;
namespace Mono.Nat.Upnp
{
internal class Upnp
{
- public UpnpNatDevice Handle(IPAddress localAddress, byte[] response, IPEndPoint endpoint)
+ protected readonly ILogger Logger;
+ protected readonly IHttpClient HttpClient;
+
+ public Upnp(ILogger logger, IHttpClient httpClient)
+ {
+ Logger = logger;
+ HttpClient = httpClient;
+ }
+
+ public virtual Task<UpnpNatDevice> Handle(IPAddress localAddress, byte[] response, IPEndPoint endpoint)
{
// Convert it to a string for easy parsing
string dataString = null;
@@ -77,7 +89,8 @@ namespace Mono.Nat.Upnp
throw new NotSupportedException("Received non-supported device type");
// We have an internet gateway device now
- return new UpnpNatDevice(localAddress, dataString, urn);
+ var device = new UpnpNatDevice(localAddress, dataString, urn, Logger, HttpClient);
+ return Task.FromResult(device);
}
}
}
diff --git a/Mono.Nat/Upnp/UpnpNatDevice.cs b/Mono.Nat/Upnp/UpnpNatDevice.cs
index 1160d3ac22..ebb1426d1a 100644
--- a/Mono.Nat/Upnp/UpnpNatDevice.cs
+++ b/Mono.Nat/Upnp/UpnpNatDevice.cs
@@ -32,29 +32,29 @@ using System.Net;
using System.Xml;
using System.Text;
using System.Diagnostics;
-using MediaBrowser.Controller.Dlna;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Dlna;
namespace Mono.Nat.Upnp
{
- public sealed class UpnpNatDevice : AbstractNatDevice, IEquatable<UpnpNatDevice>
- {
- private EndPoint hostEndPoint;
- private IPAddress localAddress;
- private string serviceDescriptionUrl;
- private string controlUrl;
- private string serviceType;
-
- public override IPAddress LocalAddress
- {
- get { return localAddress; }
- }
-
- /// <summary>
- /// The callback to invoke when we are finished setting up the device
- /// </summary>
- private NatDeviceCallback callback;
+ public sealed class UpnpNatDevice : AbstractNatDevice, IEquatable<UpnpNatDevice>
+ {
+ private EndPoint hostEndPoint;
+ private IPAddress localAddress;
+ private string serviceDescriptionUrl;
+ private string controlUrl;
+ private string serviceType;
+ private readonly ILogger _logger;
+ private readonly IHttpClient _httpClient;
+
+ public override IPAddress LocalAddress
+ {
+ get { return localAddress; }
+ }
- internal UpnpNatDevice(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint hostEndPoint, string serviceType)
+ internal UpnpNatDevice(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint hostEndPoint, string serviceType, ILogger logger, IHttpClient httpClient)
{
this.LastSeen = DateTime.Now;
this.localAddress = localAddress;
@@ -62,13 +62,15 @@ namespace Mono.Nat.Upnp
// Split the string at the "location" section so i can extract the ipaddress and service description url
string locationDetails = deviceInfo.Location.ToString();
this.serviceType = serviceType;
+ _logger = logger;
+ _httpClient = httpClient;
// Make sure we have no excess whitespace
locationDetails = locationDetails.Trim();
// FIXME: Is this reliable enough. What if we get a hostname as opposed to a proper http address
// Are we going to get addresses with the "http://" attached?
- if (locationDetails.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase))
+ if (locationDetails.StartsWith("http://", StringComparison.OrdinalIgnoreCase))
{
NatUtility.Log("Found device at: {0}", locationDetails);
// This bit strings out the "http://" from the string
@@ -88,30 +90,32 @@ namespace Mono.Nat.Upnp
}
}
- internal UpnpNatDevice (IPAddress localAddress, string deviceDetails, string serviceType)
- {
- this.LastSeen = DateTime.Now;
- this.localAddress = localAddress;
+ internal UpnpNatDevice(IPAddress localAddress, string deviceDetails, string serviceType, ILogger logger, IHttpClient httpClient)
+ {
+ _logger = logger;
+ _httpClient = httpClient;
+ this.LastSeen = DateTime.Now;
+ this.localAddress = localAddress;
- // Split the string at the "location" section so i can extract the ipaddress and service description url
- string locationDetails = deviceDetails.Substring(deviceDetails.IndexOf("Location", StringComparison.InvariantCultureIgnoreCase) + 9).Split('\r')[0];
+ // Split the string at the "location" section so i can extract the ipaddress and service description url
+ string locationDetails = deviceDetails.Substring(deviceDetails.IndexOf("Location", StringComparison.OrdinalIgnoreCase) + 9).Split('\r')[0];
this.serviceType = serviceType;
- // Make sure we have no excess whitespace
- locationDetails = locationDetails.Trim();
+ // Make sure we have no excess whitespace
+ locationDetails = locationDetails.Trim();
- // FIXME: Is this reliable enough. What if we get a hostname as opposed to a proper http address
- // Are we going to get addresses with the "http://" attached?
- if (locationDetails.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase))
- {
- NatUtility.Log("Found device at: {0}", locationDetails);
- // This bit strings out the "http://" from the string
- locationDetails = locationDetails.Substring(7);
+ // FIXME: Is this reliable enough. What if we get a hostname as opposed to a proper http address
+ // Are we going to get addresses with the "http://" attached?
+ if (locationDetails.StartsWith("http://", StringComparison.OrdinalIgnoreCase))
+ {
+ NatUtility.Log("Found device at: {0}", locationDetails);
+ // This bit strings out the "http://" from the string
+ locationDetails = locationDetails.Substring(7);
- // We then split off the end of the string to get something like: 192.168.0.3:241 in our string
- string hostAddressAndPort = locationDetails.Remove(locationDetails.IndexOf('/'));
+ // We then split off the end of the string to get something like: 192.168.0.3:241 in our string
+ string hostAddressAndPort = locationDetails.Remove(locationDetails.IndexOf('/'));
- // From this we parse out the IP address and Port
+ // From this we parse out the IP address and Port
if (hostAddressAndPort.IndexOf(':') > 0)
{
this.hostEndPoint = new IPEndPoint(IPAddress.Parse(hostAddressAndPort.Remove(hostAddressAndPort.IndexOf(':'))),
@@ -123,529 +127,85 @@ namespace Mono.Nat.Upnp
this.hostEndPoint = new IPEndPoint(IPAddress.Parse(hostAddressAndPort), 80);
}
- 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
- {
- Trace.WriteLine("Couldn't decode address. Please send following string to the developer: ");
- Trace.WriteLine(deviceDetails);
- }
- }
+ NatUtility.Log("Parsed device as: {0}", this.hostEndPoint.ToString());
- /// <summary>
- /// The EndPoint that the device is at
- /// </summary>
- internal EndPoint HostEndPoint
- {
- get { return this.hostEndPoint; }
- }
+ // 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
+ {
+ logger.Warn("Couldn't decode address: " + deviceDetails);
+ }
+ }
- /// <summary>
- /// The relative url of the xml file that describes the list of services is at
- /// </summary>
- internal string ServiceDescriptionUrl
- {
- get { return this.serviceDescriptionUrl; }
- }
+ /// <summary>
+ /// The EndPoint that the device is at
+ /// </summary>
+ internal EndPoint HostEndPoint
+ {
+ get { return this.hostEndPoint; }
+ }
- /// <summary>
- /// The relative url that we can use to control the port forwarding
- /// </summary>
- internal string ControlUrl
- {
- get { return this.controlUrl; }
- }
+ /// <summary>
+ /// The relative url of the xml file that describes the list of services is at
+ /// </summary>
+ internal string ServiceDescriptionUrl
+ {
+ get { return this.serviceDescriptionUrl; }
+ }
- /// <summary>
- /// The service type we're using on the device
- /// </summary>
- public string ServiceType
- {
- get { return serviceType; }
- }
+ /// <summary>
+ /// The relative url that we can use to control the port forwarding
+ /// </summary>
+ internal string ControlUrl
+ {
+ get { return this.controlUrl; }
+ }
- /// <summary>
- /// Begins an async call to get the external ip address of the router
- /// </summary>
- public override IAsyncResult BeginGetExternalIP(AsyncCallback callback, object asyncState)
- {
- // Create the port map message
- GetExternalIPAddressMessage message = new GetExternalIPAddressMessage(this);
- return BeginMessageInternal(message, callback, asyncState, EndGetExternalIPInternal);
- }
+ /// <summary>
+ /// The service type we're using on the device
+ /// </summary>
+ public string ServiceType
+ {
+ get { return serviceType; }
+ }
- /// <summary>
- /// Maps the specified port to this computer
- /// </summary>
- public override IAsyncResult BeginCreatePortMap(Mapping mapping, AsyncCallback callback, object asyncState)
- {
+ public override Task CreatePortMap(Mapping mapping)
+ {
CreatePortMappingMessage message = new CreatePortMappingMessage(mapping, localAddress, this);
- return BeginMessageInternal(message, callback, asyncState, EndCreatePortMapInternal);
- }
-
- /// <summary>
- /// Removes a port mapping from this computer
- /// </summary>
- public override IAsyncResult BeginDeletePortMap(Mapping mapping, AsyncCallback callback, object asyncState)
- {
- DeletePortMappingMessage message = new DeletePortMappingMessage(mapping, this);
- return BeginMessageInternal(message, callback, asyncState, EndDeletePortMapInternal);
- }
-
-
- public override IAsyncResult BeginGetAllMappings(AsyncCallback callback, object asyncState)
- {
- GetGenericPortMappingEntry message = new GetGenericPortMappingEntry(0, this);
- return BeginMessageInternal(message, callback, asyncState, EndGetAllMappingsInternal);
- }
-
-
- public override IAsyncResult BeginGetSpecificMapping (Protocol protocol, int port, AsyncCallback callback, object asyncState)
- {
- GetSpecificPortMappingEntryMessage message = new GetSpecificPortMappingEntryMessage(protocol, port, this);
- return this.BeginMessageInternal(message, callback, asyncState, new AsyncCallback(this.EndGetSpecificMappingInternal));
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="result"></param>
- public override void EndCreatePortMap(IAsyncResult result)
- {
- if (result == null) throw new ArgumentNullException("result");
-
- PortMapAsyncResult mappingResult = result as PortMapAsyncResult;
- if (mappingResult == null)
- throw new ArgumentException("Invalid AsyncResult", "result");
-
- // Check if we need to wait for the operation to finish
- if (!result.IsCompleted)
- result.AsyncWaitHandle.WaitOne();
-
- // If we have a saved exception, it means something went wrong during the mapping
- // so we just rethrow the exception and let the user figure out what they should do.
- if (mappingResult.SavedMessage is ErrorMessage)
- {
- ErrorMessage msg = mappingResult.SavedMessage as ErrorMessage;
- throw new MappingException(msg.ErrorCode, msg.Description);
- }
-
- //return result.AsyncState as Mapping;
- }
-
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="result"></param>
- public override void EndDeletePortMap(IAsyncResult result)
- {
- if (result == null)
- throw new ArgumentNullException("result");
-
- PortMapAsyncResult mappingResult = result as PortMapAsyncResult;
- if (mappingResult == null)
- throw new ArgumentException("Invalid AsyncResult", "result");
-
- // Check if we need to wait for the operation to finish
- if (!mappingResult.IsCompleted)
- mappingResult.AsyncWaitHandle.WaitOne();
-
- // If we have a saved exception, it means something went wrong during the mapping
- // so we just rethrow the exception and let the user figure out what they should do.
- if (mappingResult.SavedMessage is ErrorMessage)
- {
- ErrorMessage msg = mappingResult.SavedMessage as ErrorMessage;
- throw new MappingException(msg.ErrorCode, msg.Description);
- }
-
- // If all goes well, we just return
- //return true;
- }
-
-
- public override Mapping[] EndGetAllMappings(IAsyncResult result)
- {
- if (result == null)
- throw new ArgumentNullException("result");
-
- GetAllMappingsAsyncResult mappingResult = result as GetAllMappingsAsyncResult;
- if (mappingResult == null)
- throw new ArgumentException("Invalid AsyncResult", "result");
-
- if (!mappingResult.IsCompleted)
- mappingResult.AsyncWaitHandle.WaitOne();
-
- if (mappingResult.SavedMessage is ErrorMessage)
- {
- ErrorMessage msg = mappingResult.SavedMessage as ErrorMessage;
- if (msg.ErrorCode != 713)
- throw new MappingException(msg.ErrorCode, msg.Description);
- }
-
- return mappingResult.Mappings.ToArray();
- }
-
-
- /// <summary>
- /// Ends an async request to get the external ip address of the router
- /// </summary>
- public override IPAddress EndGetExternalIP(IAsyncResult result)
- {
- if (result == null) throw new ArgumentNullException("result");
-
- PortMapAsyncResult mappingResult = result as PortMapAsyncResult;
- if (mappingResult == null)
- throw new ArgumentException("Invalid AsyncResult", "result");
-
- if (!result.IsCompleted)
- result.AsyncWaitHandle.WaitOne();
-
- if (mappingResult.SavedMessage is ErrorMessage)
- {
- ErrorMessage msg = mappingResult.SavedMessage as ErrorMessage;
- throw new MappingException(msg.ErrorCode, msg.Description);
- }
-
- if (mappingResult.SavedMessage == null)
- return null;
- else
- return ((GetExternalIPAddressResponseMessage)mappingResult.SavedMessage).ExternalIPAddress;
- }
-
-
- public override Mapping EndGetSpecificMapping(IAsyncResult result)
- {
- if (result == null)
- throw new ArgumentNullException("result");
-
- GetAllMappingsAsyncResult mappingResult = result as GetAllMappingsAsyncResult;
- if (mappingResult == null)
- throw new ArgumentException("Invalid AsyncResult", "result");
-
- if (!mappingResult.IsCompleted)
- mappingResult.AsyncWaitHandle.WaitOne();
-
- if (mappingResult.SavedMessage is ErrorMessage)
- {
- ErrorMessage message = mappingResult.SavedMessage as ErrorMessage;
- if (message.ErrorCode != 0x2ca)
- {
- throw new MappingException(message.ErrorCode, message.Description);
- }
- }
- if (mappingResult.Mappings.Count == 0)
- return new Mapping (Protocol.Tcp, -1, -1);
-
- return mappingResult.Mappings[0];
- }
-
-
- public override bool Equals(object obj)
- {
- UpnpNatDevice device = obj as UpnpNatDevice;
- return (device == null) ? false : this.Equals((device));
- }
-
-
- public bool Equals(UpnpNatDevice other)
- {
- return (other == null) ? false : (this.hostEndPoint.Equals(other.hostEndPoint)
- //&& this.controlUrl == other.controlUrl
- && this.serviceDescriptionUrl == other.serviceDescriptionUrl);
- }
-
- public override int GetHashCode()
- {
- return (this.hostEndPoint.GetHashCode() ^ this.controlUrl.GetHashCode() ^ this.serviceDescriptionUrl.GetHashCode());
- }
-
- private IAsyncResult BeginMessageInternal(MessageBase message, AsyncCallback storedCallback, object asyncState, AsyncCallback callback)
- {
- byte[] body;
- WebRequest request = message.Encode(out body);
- PortMapAsyncResult mappingResult = PortMapAsyncResult.Create(message, request, storedCallback, asyncState);
-
- if (body.Length > 0)
- {
- request.ContentLength = body.Length;
- request.BeginGetRequestStream(delegate(IAsyncResult result) {
- try
- {
- Stream s = request.EndGetRequestStream(result);
- s.Write(body, 0, body.Length);
- request.BeginGetResponse(callback, mappingResult);
- }
- catch (Exception ex)
- {
- mappingResult.Complete(ex);
- }
- }, null);
- }
- else
- {
- request.BeginGetResponse(callback, mappingResult);
- }
- return mappingResult;
- }
-
- private void CompleteMessage(IAsyncResult result)
- {
- PortMapAsyncResult mappingResult = result.AsyncState as PortMapAsyncResult;
- mappingResult.CompletedSynchronously = result.CompletedSynchronously;
- mappingResult.Complete();
- }
-
- private MessageBase DecodeMessageFromResponse(Stream s, long length)
- {
- StringBuilder data = new StringBuilder();
- int bytesRead = 0;
- int totalBytesRead = 0;
- byte[] buffer = new byte[10240];
-
- // Read out the content of the message, hopefully picking everything up in the case where we have no contentlength
- if (length != -1)
- {
- while (totalBytesRead < length)
- {
- bytesRead = s.Read(buffer, 0, buffer.Length);
- data.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead));
- totalBytesRead += bytesRead;
- }
- }
- else
- {
- while ((bytesRead = s.Read(buffer, 0, buffer.Length)) != 0)
- data.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead));
- }
-
- // Once we have our content, we need to see what kind of message it is. It'll either a an error
- // or a response based on the action we performed.
- return MessageBase.Decode(this, data.ToString());
- }
-
- private void EndCreatePortMapInternal(IAsyncResult result)
- {
- EndMessageInternal(result);
- CompleteMessage(result);
- }
-
- private void EndMessageInternal(IAsyncResult result)
- {
- HttpWebResponse response = null;
- PortMapAsyncResult mappingResult = result.AsyncState as PortMapAsyncResult;
-
- try
- {
- try
- {
- response = (HttpWebResponse)mappingResult.Request.EndGetResponse(result);
- }
- catch (WebException ex)
- {
- // Even if the request "failed" i want to continue on to read out the response from the router
- response = ex.Response as HttpWebResponse;
- if (response == null)
- mappingResult.SavedMessage = new ErrorMessage((int)ex.Status, ex.Message);
- }
- if (response != null)
- mappingResult.SavedMessage = DecodeMessageFromResponse(response.GetResponseStream(), response.ContentLength);
- }
-
- finally
- {
- if (response != null)
- response.Close();
- }
- }
-
- private void EndDeletePortMapInternal(IAsyncResult result)
- {
- EndMessageInternal(result);
- CompleteMessage(result);
- }
-
- private void EndGetAllMappingsInternal(IAsyncResult result)
- {
- EndMessageInternal(result);
-
- GetAllMappingsAsyncResult mappingResult = result.AsyncState as GetAllMappingsAsyncResult;
- GetGenericPortMappingEntryResponseMessage message = mappingResult.SavedMessage as GetGenericPortMappingEntryResponseMessage;
- if (message != null)
- {
- Mapping mapping = new Mapping (message.Protocol, message.InternalPort, message.ExternalPort, message.LeaseDuration);
- mapping.Description = message.PortMappingDescription;
- mappingResult.Mappings.Add(mapping);
- GetGenericPortMappingEntry next = new GetGenericPortMappingEntry(mappingResult.Mappings.Count, this);
-
- // It's ok to do this synchronously because we should already be on anther thread
- // and this won't block the user.
- byte[] body;
- WebRequest request = next.Encode(out body);
- if (body.Length > 0)
- {
- request.ContentLength = body.Length;
- request.GetRequestStream().Write(body, 0, body.Length);
- }
- mappingResult.Request = request;
- request.BeginGetResponse(EndGetAllMappingsInternal, mappingResult);
- return;
- }
-
- CompleteMessage(result);
- }
-
- private void EndGetExternalIPInternal(IAsyncResult result)
- {
- EndMessageInternal(result);
- CompleteMessage(result);
- }
-
- private void EndGetSpecificMappingInternal(IAsyncResult result)
- {
- EndMessageInternal(result);
-
- GetAllMappingsAsyncResult mappingResult = result.AsyncState as GetAllMappingsAsyncResult;
- GetGenericPortMappingEntryResponseMessage message = mappingResult.SavedMessage as GetGenericPortMappingEntryResponseMessage;
- if (message != null) {
- Mapping mapping = new Mapping(mappingResult.SpecificMapping.Protocol, message.InternalPort, mappingResult.SpecificMapping.PublicPort, message.LeaseDuration);
- mapping.Description = mappingResult.SpecificMapping.Description;
- mappingResult.Mappings.Add(mapping);
- }
-
- CompleteMessage(result);
- }
-
- internal void GetServicesList(NatDeviceCallback callback)
- {
- // Save the callback so i can use it again later when i've finished parsing the services available
- this.callback = callback;
-
- // Create a HTTPWebRequest to download the list of services the device offers
- byte[] body;
- WebRequest request = new GetServicesMessage(this.serviceDescriptionUrl, this.hostEndPoint).Encode(out body);
- if (body.Length > 0)
- NatUtility.Log("Error: Services Message contained a body");
- request.BeginGetResponse(this.ServicesReceived, request);
- }
-
- private void ServicesReceived(IAsyncResult result)
- {
- HttpWebResponse response = null;
- try
- {
- int abortCount = 0;
- int bytesRead = 0;
- byte[] buffer = new byte[10240];
- StringBuilder servicesXml = new StringBuilder();
- XmlDocument xmldoc = new XmlDocument();
- HttpWebRequest request = result.AsyncState as HttpWebRequest;
- response = request.EndGetResponse(result) as HttpWebResponse;
- Stream s = response.GetResponseStream();
-
- if (response.StatusCode != HttpStatusCode.OK) {
- NatUtility.Log("{0}: Couldn't get services list: {1}", HostEndPoint, response.StatusCode);
- return; // FIXME: This the best thing to do??
- }
+ return _httpClient.SendAsync(message.Encode(), message.Method);
+ }
- while (true)
- {
- bytesRead = s.Read(buffer, 0, buffer.Length);
- servicesXml.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead));
- try
- {
- xmldoc.LoadXml(servicesXml.ToString());
- response.Close();
- break;
- }
- catch (XmlException)
- {
- // If we can't receive the entire XML within 500ms, then drop the connection
- // Unfortunately not all routers supply a valid ContentLength (mine doesn't)
- // so this hack is needed to keep testing our recieved data until it gets successfully
- // parsed by the xmldoc. Without this, the code will never pick up my router.
- if (abortCount++ > 50)
- {
- response.Close();
- return;
- }
- NatUtility.Log("{0}: Couldn't parse services list", HostEndPoint);
- System.Threading.Thread.Sleep(10);
- }
- }
+ public override bool Equals(object obj)
+ {
+ UpnpNatDevice device = obj as UpnpNatDevice;
+ return (device == null) ? false : this.Equals((device));
+ }
- 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);
- foreach (XmlNode node in nodes)
- {
- //Go through each service there
- foreach (XmlNode service in node.ChildNodes)
- {
- //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;
- // 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))
- {
- this.controlUrl = service["controlURL"].InnerText;
- NatUtility.Log("{0}: Found upnp service at: {1}", HostEndPoint, controlUrl);
- try
- {
- 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);
- }
- }
- catch
- {
- NatUtility.Log("{0}: Assuming control Uri is relative: {1}", HostEndPoint, controlUrl);
- }
- NatUtility.Log("{0}: Handshake Complete", HostEndPoint);
- this.callback(this);
- return;
- }
- }
- }
+ public bool Equals(UpnpNatDevice other)
+ {
+ return (other == null) ? false : (this.hostEndPoint.Equals(other.hostEndPoint)
+ //&& this.controlUrl == other.controlUrl
+ && this.serviceDescriptionUrl == other.serviceDescriptionUrl);
+ }
- //If we get here, it means that we didn't get WANIPConnection service, which means no uPnP forwarding
- //So we don't invoke the callback, so this device is never added to our lists
- }
- catch (WebException ex)
- {
- // Just drop the connection, FIXME: Should i retry?
- NatUtility.Log("{0}: Device denied the connection attempt: {1}", HostEndPoint, ex);
- }
- finally
- {
- if (response != null)
- response.Close();
- }
- }
+ public override int GetHashCode()
+ {
+ return (this.hostEndPoint.GetHashCode() ^ this.controlUrl.GetHashCode() ^ this.serviceDescriptionUrl.GetHashCode());
+ }
/// <summary>
/// Overridden.
/// </summary>
/// <returns></returns>
- public override string ToString( )
+ public override string ToString()
{
//GetExternalIP is blocking and can throw exceptions, can't use it here.
- return String.Format(
+ return String.Format(
"UpnpNatDevice - EndPoint: {0}, External IP: {1}, Control Url: {2}, Service Description Url: {3}, Service Type: {4}, Last Seen: {5}",
this.hostEndPoint, "Manually Check" /*this.GetExternalIP()*/, this.controlUrl, this.serviceDescriptionUrl, this.serviceType, this.LastSeen);
}
- }
+ }
} \ No newline at end of file
diff --git a/Mono.Nat/project.json b/Mono.Nat/project.json
new file mode 100644
index 0000000000..3c38a62e13
--- /dev/null
+++ b/Mono.Nat/project.json
@@ -0,0 +1,41 @@
+{
+ "version": "1.0.0-*",
+
+ "dependencies": {
+
+ },
+
+ "frameworks": {
+ "net46": {
+ "frameworkAssemblies": {
+ "System.Collections": "4.0.0.0",
+ "System.Net": "4.0.0.0",
+ "System.Runtime": "4.0.0.0",
+ "System.Threading": "4.0.0.0",
+ "System.Threading.Tasks": "4.0.0.0",
+ "System.Xml": "4.0.0.0"
+ },
+ "dependencies": {
+ "MediaBrowser.Common": {
+ "target": "project"
+ },
+ "MediaBrowser.Model": {
+ "target": "project"
+ }
+ }
+ },
+ "netstandard1.6": {
+ "imports": "dnxcore50",
+ "dependencies": {
+ "NETStandard.Library": "1.6.1",
+ "MediaBrowser.Common": {
+ "target": "project"
+ },
+ "MediaBrowser.Model": {
+ "target": "project"
+ },
+ "System.Net.NetworkInformation": "4.3.0"
+ }
+ }
+ }
+}
diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec
index aba4913f33..292c80a7c4 100644
--- a/Nuget/MediaBrowser.Common.Internal.nuspec
+++ b/Nuget/MediaBrowser.Common.Internal.nuspec
@@ -2,17 +2,17 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common.Internal</id>
- <version>3.0.665</version>
- <title>MediaBrowser.Common.Internal</title>
+ <version>3.0.680</version>
+ <title>Emby.Common.Internal</title>
<authors>Luke</authors>
<owners>ebr,Luke,scottisafool</owners>
- <projectUrl>https://github.com/MediaBrowser/MediaBrowser</projectUrl>
+ <projectUrl>https://github.com/MediaBrowser/Emby</projectUrl>
<iconUrl>http://www.mb3admin.com/images/mb3icons1-1.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.</description>
<copyright>Copyright © Emby 2013</copyright>
<dependencies>
- <dependency id="MediaBrowser.Common" version="3.0.665" />
+ <dependency id="MediaBrowser.Common" version="3.0.680" />
<dependency id="NLog" version="4.3.8" />
<dependency id="SimpleInjector" version="3.2.2" />
</dependencies>
diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec
index cd79701275..daeb754bac 100644
--- a/Nuget/MediaBrowser.Common.nuspec
+++ b/Nuget/MediaBrowser.Common.nuspec
@@ -2,20 +2,18 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common</id>
- <version>3.0.665</version>
- <title>MediaBrowser.Common</title>
+ <version>3.0.691</version>
+ <title>Emby.Common</title>
<authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners>
- <projectUrl>https://github.com/MediaBrowser/MediaBrowser</projectUrl>
+ <projectUrl>https://github.com/MediaBrowser/Emby</projectUrl>
<iconUrl>http://www.mb3admin.com/images/mb3icons1-1.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Contains common model objects and interfaces used by all Emby solutions.</description>
<copyright>Copyright © Emby 2013</copyright>
</metadata>
<files>
- <file src="dlls\net35\MediaBrowser.Model.dll" target="lib\net35\MediaBrowser.Model.dll" />
- <file src="dlls\MediaBrowser.Common.dll" target="lib\net45\MediaBrowser.Common.dll" />
- <file src="dlls\net45\MediaBrowser.Model.dll" target="lib\net45\MediaBrowser.Model.dll" />
- <file src="dlls\portable\MediaBrowser.Model.dll" target="lib\portable-net4+sl4+wp71+win8+wpa81\MediaBrowser.Model.dll" />
+ <file src="dlls\MediaBrowser.Common.dll" target="lib\portable-net45+win8+wpa81\MediaBrowser.Common.dll" />
+ <file src="dlls\MediaBrowser.Model.dll" target="lib\portable-net45+win8+wpa81\MediaBrowser.Model.dll" />
</files>
</package>
diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec
index b0dd3ae4fe..a34a9bee20 100644
--- a/Nuget/MediaBrowser.Server.Core.nuspec
+++ b/Nuget/MediaBrowser.Server.Core.nuspec
@@ -2,21 +2,21 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MediaBrowser.Server.Core</id>
- <version>3.0.665</version>
- <title>Media Browser.Server.Core</title>
+ <version>3.0.691</version>
+ <title>Emby.Server.Core</title>
<authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners>
- <projectUrl>https://github.com/MediaBrowser/MediaBrowser</projectUrl>
+ <projectUrl>https://github.com/MediaBrowser/Emby</projectUrl>
<iconUrl>http://www.mb3admin.com/images/mb3icons1-1.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Contains core components required to build plugins for Emby Server.</description>
<copyright>Copyright © Emby 2013</copyright>
<dependencies>
- <dependency id="MediaBrowser.Common" version="3.0.665" />
- <dependency id="Interfaces.IO" version="1.0.0.5" />
+ <dependency id="MediaBrowser.Common" version="3.0.691" />
</dependencies>
</metadata>
<files>
<file src="dlls\MediaBrowser.Controller.dll" target="lib\net45\MediaBrowser.Controller.dll" />
+ <file src="dlls\MediaBrowser.Controller.dll" target="lib\portable-net45+win8+wpa81\MediaBrowser.Controller.dll" />
</files>
</package> \ No newline at end of file
diff --git a/OpenSubtitlesHandler/Interfaces/IMethodResponse.cs b/OpenSubtitlesHandler/Interfaces/IMethodResponse.cs
index b8e24f12bd..9c05e357b9 100644
--- a/OpenSubtitlesHandler/Interfaces/IMethodResponse.cs
+++ b/OpenSubtitlesHandler/Interfaces/IMethodResponse.cs
@@ -37,17 +37,17 @@ namespace OpenSubtitlesHandler
protected double seconds;
protected string status;
- protected virtual void LoadAttributes()
+ protected void LoadAttributes()
{
- foreach (Attribute attr in Attribute.GetCustomAttributes(this.GetType()))
- {
- if (attr.GetType() == typeof(MethodResponseDescription))
- {
- this.name = ((MethodResponseDescription)attr).Name;
- this.message = ((MethodResponseDescription)attr).Message;
- break;
- }
- }
+ //foreach (Attribute attr in Attribute.GetCustomAttributes(this.GetType()))
+ //{
+ // if (attr.GetType() == typeof(MethodResponseDescription))
+ // {
+ // this.name = ((MethodResponseDescription)attr).Name;
+ // this.message = ((MethodResponseDescription)attr).Message;
+ // break;
+ // }
+ //}
}
[Description("The name of this response"), Category("MethodResponse")]
@@ -59,4 +59,14 @@ namespace OpenSubtitlesHandler
[Description("The status"), Category("MethodResponse")]
public string Status { get { return status; } set { status = value; } }
}
+
+ public class DescriptionAttribute : Attribute
+ {
+ public DescriptionAttribute(string text) { }
+ }
+
+ public class CategoryAttribute : Attribute
+ {
+ public CategoryAttribute(string text) { }
+ }
}
diff --git a/OpenSubtitlesHandler/MovieHasher.cs b/OpenSubtitlesHandler/MovieHasher.cs
index 89301191fe..57be9ab28e 100644
--- a/OpenSubtitlesHandler/MovieHasher.cs
+++ b/OpenSubtitlesHandler/MovieHasher.cs
@@ -8,29 +8,31 @@ namespace OpenSubtitlesHandler
{
public static byte[] ComputeMovieHash(Stream input)
{
- long lhash, streamsize;
- streamsize = input.Length;
- lhash = streamsize;
-
- long i = 0;
- byte[] buffer = new byte[sizeof(long)];
- while (i < 65536 / sizeof(long) && (input.Read(buffer, 0, sizeof(long)) > 0))
+ using (input)
{
- i++;
- lhash += BitConverter.ToInt64(buffer, 0);
- }
+ long lhash, streamsize;
+ streamsize = input.Length;
+ lhash = streamsize;
- input.Position = Math.Max(0, streamsize - 65536);
- i = 0;
- while (i < 65536 / sizeof(long) && (input.Read(buffer, 0, sizeof(long)) > 0))
- {
- i++;
- lhash += BitConverter.ToInt64(buffer, 0);
+ long i = 0;
+ byte[] buffer = new byte[sizeof(long)];
+ while (i < 65536 / sizeof(long) && (input.Read(buffer, 0, sizeof(long)) > 0))
+ {
+ i++;
+ lhash += BitConverter.ToInt64(buffer, 0);
+ }
+
+ input.Position = Math.Max(0, streamsize - 65536);
+ i = 0;
+ while (i < 65536 / sizeof(long) && (input.Read(buffer, 0, sizeof(long)) > 0))
+ {
+ i++;
+ lhash += BitConverter.ToInt64(buffer, 0);
+ }
+ byte[] result = BitConverter.GetBytes(lhash);
+ Array.Reverse(result);
+ return result;
}
- input.Close();
- byte[] result = BitConverter.GetBytes(lhash);
- Array.Reverse(result);
- return result;
}
public static string ToHexadecimal(byte[] bytes)
diff --git a/OpenSubtitlesHandler/OpenSubtitlesHandler.csproj b/OpenSubtitlesHandler/OpenSubtitlesHandler.csproj
index 683c166dc6..54e14e3e3a 100644
--- a/OpenSubtitlesHandler/OpenSubtitlesHandler.csproj
+++ b/OpenSubtitlesHandler/OpenSubtitlesHandler.csproj
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<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>
@@ -9,9 +9,12 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>OpenSubtitlesHandler</RootNamespace>
<AssemblyName>OpenSubtitlesHandler</AssemblyName>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<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>
@@ -36,15 +39,6 @@
<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.Xml" />
- </ItemGroup>
- <ItemGroup>
<Compile Include="Console\OSHConsole.cs" />
<Compile Include="Interfaces\IMethodResponse.cs" />
<Compile Include="Interfaces\MethodResponseAttr.cs" />
@@ -118,7 +112,10 @@
<ItemGroup>
<Content Include="XML-RPC\Docs\XML-RPC.txt" />
</ItemGroup>
- <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <ItemGroup>
+ <None Include="project.json" />
+ </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">
@@ -126,4 +123,4 @@
<Target Name="AfterBuild">
</Target>
-->
-</Project>
+</Project> \ No newline at end of file
diff --git a/OpenSubtitlesHandler/OpenSubtitlesHandler.nuget.targets b/OpenSubtitlesHandler/OpenSubtitlesHandler.nuget.targets
new file mode 100644
index 0000000000..e69ce0e64f
--- /dev/null
+++ b/OpenSubtitlesHandler/OpenSubtitlesHandler.nuget.targets
@@ -0,0 +1,6 @@
+<?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/OpenSubtitlesHandler/Utilities.cs b/OpenSubtitlesHandler/Utilities.cs
index 9a90462f66..3fe606c782 100644
--- a/OpenSubtitlesHandler/Utilities.cs
+++ b/OpenSubtitlesHandler/Utilities.cs
@@ -20,10 +20,11 @@ using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
-using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Cryptography;
+using MediaBrowser.Model.Text;
namespace OpenSubtitlesHandler
{
@@ -32,50 +33,36 @@ namespace OpenSubtitlesHandler
/// </summary>
public sealed class Utilities
{
+ public static ICryptoProvider CryptographyProvider { get; set; }
+ public static IHttpClient HttpClient { get; set; }
+ public static ITextEncoding EncodingHelper { get; set; }
+
private const string XML_RPC_SERVER = "https://api.opensubtitles.org/xml-rpc";
/// <summary>
/// Compute movie hash
/// </summary>
- /// <param name="fileName">The complete media file path</param>
/// <returns>The hash as Hexadecimal string</returns>
- public static string ComputeHash(string fileName)
+ public static string ComputeHash(Stream stream)
{
- byte[] hash = MovieHasher.ComputeMovieHash(File.OpenRead(fileName));
+ byte[] hash = MovieHasher.ComputeMovieHash(stream);
return MovieHasher.ToHexadecimal(hash);
}
/// <summary>
- /// Compute md5 for a file
- /// </summary>
- /// <param name="filename">The complete file path</param>
- /// <returns>MD5 of the file</returns>
- public static string ComputeMd5(string filename)
- {
- var md5 = MD5.Create();
- var sb = new StringBuilder();
- Stream str = new FileStream(filename, FileMode.Open, FileAccess.Read);
-
- foreach (var b in md5.ComputeHash(str))
- sb.Append(b.ToString("x2").ToLower());
- str.Close();
- return sb.ToString();
- }
- /// <summary>
/// Decompress data using GZip
/// </summary>
/// <param name="dataToDecompress">The stream that hold the data</param>
/// <returns>Bytes array of decompressed data</returns>
public static byte[] Decompress(Stream dataToDecompress)
{
- MemoryStream target = new MemoryStream();
-
- using (System.IO.Compression.GZipStream decompressionStream = new System.IO.Compression.GZipStream(dataToDecompress,
- System.IO.Compression.CompressionMode.Decompress))
+ using (MemoryStream target = new MemoryStream())
{
- decompressionStream.CopyTo(target);
+ using (System.IO.Compression.GZipStream decompressionStream = new System.IO.Compression.GZipStream(dataToDecompress, System.IO.Compression.CompressionMode.Decompress))
+ {
+ decompressionStream.CopyTo(target);
+ }
+ return target.ToArray();
}
- return target.GetBuffer();
-
}
/// <summary>
@@ -122,35 +109,30 @@ namespace OpenSubtitlesHandler
/// <summary>
/// Handle server response stream and decode it as given encoding string.
/// </summary>
- /// <param name="responseStream">The response stream. Expects a stream that doesn't support seek.</param>
- /// <param name="encoding">The encoding that should be used to decode buffer</param>
/// <returns>The string of the stream after decode using given encoding</returns>
- public static string GetStreamString(Stream responseStream, Encoding encoding)
+ public static string GetStreamString(Stream responseStream)
{
- // Handle response, should be XML text.
- List<byte> data = new List<byte>();
- while (true)
+ using (responseStream)
{
- int r = responseStream.ReadByte();
- if (r < 0)
- break;
- data.Add((byte)r);
+ // Handle response, should be XML text.
+ List<byte> data = new List<byte>();
+ while (true)
+ {
+ int r = responseStream.ReadByte();
+ if (r < 0)
+ break;
+ data.Add((byte)r);
+ }
+ var bytes = data.ToArray();
+ return EncodingHelper.GetASCIIEncoding().GetString(bytes, 0, bytes.Length);
}
- responseStream.Close();
- return encoding.GetString(data.ToArray());
}
- /// <summary>
- /// Handle server response stream and decode it as ASCII encoding string.
- /// </summary>
- /// <param name="responseStream">The response stream. Expects a stream that doesn't support seek.</param>
- /// <returns>The string of the stream after decode using ASCII encoding</returns>
- public static string GetStreamString(Stream responseStream)
+
+ public static byte[] GetASCIIBytes(string text)
{
- return GetStreamString(responseStream, Encoding.ASCII);
+ return EncodingHelper.GetASCIIEncoding().GetBytes(text);
}
- public static IHttpClient HttpClient { get; set; }
-
/// <summary>
/// Send a request to the server
/// </summary>
diff --git a/OpenSubtitlesHandler/XML-RPC/XmlRpcGenerator.cs b/OpenSubtitlesHandler/XML-RPC/XmlRpcGenerator.cs
index c39917e298..06fc945a8e 100644
--- a/OpenSubtitlesHandler/XML-RPC/XmlRpcGenerator.cs
+++ b/OpenSubtitlesHandler/XML-RPC/XmlRpcGenerator.cs
@@ -22,6 +22,8 @@ using System.Text;
using System.Collections.Generic;
using System.IO;
using System.Xml;
+using OpenSubtitlesHandler;
+
namespace XmlRpcHandler
{
/// <summary>
@@ -55,40 +57,41 @@ namespace XmlRpcHandler
using (var ms = new MemoryStream())
{
- XmlWriter XMLwrt = XmlWriter.Create(ms, sett);
- // Let's write the methods
- foreach (XmlRpcMethodCall method in methods)
+ using (XmlWriter XMLwrt = XmlWriter.Create(ms, sett))
{
- XMLwrt.WriteStartElement("methodCall");//methodCall
- XMLwrt.WriteStartElement("methodName");//methodName
- XMLwrt.WriteString(method.Name);
- XMLwrt.WriteEndElement();//methodName
- XMLwrt.WriteStartElement("params");//params
- // Write values
- foreach (IXmlRpcValue p in method.Parameters)
+ // Let's write the methods
+ foreach (XmlRpcMethodCall method in methods)
{
- XMLwrt.WriteStartElement("param");//param
- if (p is XmlRpcValueBasic)
- {
- WriteBasicValue(XMLwrt, (XmlRpcValueBasic)p);
- }
- else if (p is XmlRpcValueStruct)
- {
- WriteStructValue(XMLwrt, (XmlRpcValueStruct)p);
- }
- else if (p is XmlRpcValueArray)
+ XMLwrt.WriteStartElement("methodCall");//methodCall
+ XMLwrt.WriteStartElement("methodName");//methodName
+ XMLwrt.WriteString(method.Name);
+ XMLwrt.WriteEndElement();//methodName
+ XMLwrt.WriteStartElement("params");//params
+ // Write values
+ foreach (IXmlRpcValue p in method.Parameters)
{
- WriteArrayValue(XMLwrt, (XmlRpcValueArray)p);
+ XMLwrt.WriteStartElement("param");//param
+ if (p is XmlRpcValueBasic)
+ {
+ WriteBasicValue(XMLwrt, (XmlRpcValueBasic)p);
+ }
+ else if (p is XmlRpcValueStruct)
+ {
+ WriteStructValue(XMLwrt, (XmlRpcValueStruct)p);
+ }
+ else if (p is XmlRpcValueArray)
+ {
+ WriteArrayValue(XMLwrt, (XmlRpcValueArray)p);
+ }
+ XMLwrt.WriteEndElement();//param
}
- XMLwrt.WriteEndElement();//param
- }
- XMLwrt.WriteEndElement();//params
- XMLwrt.WriteEndElement();//methodCall
+ XMLwrt.WriteEndElement();//params
+ XMLwrt.WriteEndElement();//methodCall
+ }
+ XMLwrt.Flush();
+ return ms.ToArray();
}
- XMLwrt.Flush();
- XMLwrt.Close();
- return ms.ToArray();
}
}
/// <summary>
@@ -102,27 +105,34 @@ namespace XmlRpcHandler
XmlReaderSettings sett = new XmlReaderSettings();
sett.DtdProcessing = DtdProcessing.Ignore;
sett.IgnoreWhitespace = true;
- MemoryStream str = new MemoryStream(Encoding.ASCII.GetBytes(xmlResponse));
+ MemoryStream str;
if (xmlResponse.Contains(@"encoding=""utf-8"""))
{
str = new MemoryStream(Encoding.UTF8.GetBytes(xmlResponse));
}
- XmlReader XMLread = XmlReader.Create(str, sett);
-
- XmlRpcMethodCall call = new XmlRpcMethodCall("methodResponse");
- // Read parameters
- while (XMLread.Read())
+ else
{
- if (XMLread.Name == "param" && XMLread.IsStartElement())
+ str = new MemoryStream(Utilities.GetASCIIBytes(xmlResponse));
+ }
+ using (str)
+ {
+ using (XmlReader XMLread = XmlReader.Create(str, sett))
{
- IXmlRpcValue val = ReadValue(XMLread);
- if (val != null)
- call.Parameters.Add(val);
+ XmlRpcMethodCall call = new XmlRpcMethodCall("methodResponse");
+ // Read parameters
+ while (XMLread.Read())
+ {
+ if (XMLread.Name == "param" && XMLread.IsStartElement())
+ {
+ IXmlRpcValue val = ReadValue(XMLread);
+ if (val != null)
+ call.Parameters.Add(val);
+ }
+ }
+ methods.Add(call);
+ return methods.ToArray();
}
}
- methods.Add(call);
- XMLread.Close();
- return methods.ToArray();
}
private static void WriteBasicValue(XmlWriter XMLwrt, XmlRpcValueBasic val)
@@ -231,33 +241,42 @@ namespace XmlRpcHandler
XMLwrt.WriteEndElement();//value
}
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
+
+ private static string ReadString(XmlReader reader)
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ return reader.ReadElementContentAsString();
+ }
+ return reader.ReadContentAsString();
+ }
- private static IXmlRpcValue ReadValue(XmlReader xmlReader)
+ private static IXmlRpcValue ReadValue(XmlReader xmlReader, bool skipRead = false)
{
- while (xmlReader.Read())
+ while (skipRead || xmlReader.Read())
{
if (xmlReader.Name == "value" && xmlReader.IsStartElement())
{
xmlReader.Read();
if (xmlReader.Name == "string" && xmlReader.IsStartElement())
{
- return new XmlRpcValueBasic(xmlReader.ReadString(), XmlRpcBasicValueType.String);
+ return new XmlRpcValueBasic(ReadString(xmlReader), XmlRpcBasicValueType.String);
}
else if (xmlReader.Name == "int" && xmlReader.IsStartElement())
{
- return new XmlRpcValueBasic(int.Parse(xmlReader.ReadString(), UsCulture), XmlRpcBasicValueType.Int);
+ return new XmlRpcValueBasic(int.Parse(ReadString(xmlReader), UsCulture), XmlRpcBasicValueType.Int);
}
else if (xmlReader.Name == "boolean" && xmlReader.IsStartElement())
{
- return new XmlRpcValueBasic(xmlReader.ReadString() == "1", XmlRpcBasicValueType.Boolean);
+ return new XmlRpcValueBasic(ReadString(xmlReader) == "1", XmlRpcBasicValueType.Boolean);
}
else if (xmlReader.Name == "double" && xmlReader.IsStartElement())
{
- return new XmlRpcValueBasic(double.Parse(xmlReader.ReadString(), UsCulture), XmlRpcBasicValueType.Double);
+ return new XmlRpcValueBasic(double.Parse(ReadString(xmlReader), UsCulture), XmlRpcBasicValueType.Double);
}
else if (xmlReader.Name == "dateTime.iso8601" && xmlReader.IsStartElement())
{
- string date = xmlReader.ReadString();
+ string date = ReadString(xmlReader);
int year = int.Parse(date.Substring(0, 4), UsCulture);
int month = int.Parse(date.Substring(4, 2), UsCulture);
int day = int.Parse(date.Substring(6, 2), UsCulture);
@@ -269,7 +288,7 @@ namespace XmlRpcHandler
}
else if (xmlReader.Name == "base64" && xmlReader.IsStartElement())
{
- return new XmlRpcValueBasic(BitConverter.ToInt64(Convert.FromBase64String(xmlReader.ReadString()), 0)
+ return new XmlRpcValueBasic(BitConverter.ToInt64(Convert.FromBase64String(ReadString(xmlReader)), 0)
, XmlRpcBasicValueType.Double);
}
else if (xmlReader.Name == "struct" && xmlReader.IsStartElement())
@@ -282,9 +301,9 @@ namespace XmlRpcHandler
{
XmlRpcStructMember member = new XmlRpcStructMember("", null);
xmlReader.Read();// read name
- member.Name = xmlReader.ReadString();
+ member.Name = ReadString(xmlReader);
- IXmlRpcValue val = ReadValue(xmlReader);
+ IXmlRpcValue val = ReadValue(xmlReader, true);
if (val != null)
{
member.Data = val;
@@ -319,6 +338,11 @@ namespace XmlRpcHandler
}
}
else break;
+
+ if (skipRead)
+ {
+ return null;
+ }
}
return null;
}
diff --git a/OpenSubtitlesHandler/project.json b/OpenSubtitlesHandler/project.json
new file mode 100644
index 0000000000..fbbe9eaf32
--- /dev/null
+++ b/OpenSubtitlesHandler/project.json
@@ -0,0 +1,17 @@
+{
+ "frameworks":{
+ "netstandard1.6":{
+ "dependencies":{
+ "NETStandard.Library":"1.6.0",
+ }
+ },
+ ".NETPortable,Version=v4.5,Profile=Profile7":{
+ "buildOptions": {
+ "define": [ ]
+ },
+ "frameworkAssemblies":{
+
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/RSSDP/CustomHttpHeaders.cs b/RSSDP/CustomHttpHeaders.cs
new file mode 100644
index 0000000000..9250d612fe
--- /dev/null
+++ b/RSSDP/CustomHttpHeaders.cs
@@ -0,0 +1,295 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+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/DeviceAvailableEventArgs.cs b/RSSDP/DeviceAvailableEventArgs.cs
new file mode 100644
index 0000000000..39f07e1d75
--- /dev/null
+++ b/RSSDP/DeviceAvailableEventArgs.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Rssdp
+{
+ /// <summary>
+ /// Event arguments for the <see cref="Rssdp.Infrastructure.SsdpDeviceLocatorBase.DeviceAvailable"/> event.
+ /// </summary>
+ public sealed class DeviceAvailableEventArgs : EventArgs
+ {
+
+ #region Fields
+
+ private readonly DiscoveredSsdpDevice _DiscoveredDevice;
+ private readonly bool _IsNewlyDiscovered;
+
+ #endregion
+
+ #region Constructors
+
+ /// <summary>
+ /// Full constructor.
+ /// </summary>
+ /// <param name="discoveredDevice">A <see cref="DiscoveredSsdpDevice"/> instance representing the available device.</param>
+ /// <param name="isNewlyDiscovered">A boolean value indicating whether or not this device came from the cache. See <see cref="IsNewlyDiscovered"/> for more detail.</param>
+ /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="discoveredDevice"/> parameter is null.</exception>
+ public DeviceAvailableEventArgs(DiscoveredSsdpDevice discoveredDevice, bool isNewlyDiscovered)
+ {
+ if (discoveredDevice == null) throw new ArgumentNullException("discoveredDevice");
+
+ _DiscoveredDevice = discoveredDevice;
+ _IsNewlyDiscovered = isNewlyDiscovered;
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ /// <summary>
+ /// Returns true if the device was discovered due to an alive notification, or a search and was not already in the cache. Returns false if the item came from the cache but matched the current search request.
+ /// </summary>
+ public bool IsNewlyDiscovered
+ {
+ get { return _IsNewlyDiscovered; }
+ }
+
+ /// <summary>
+ /// A reference to a <see cref="Rssdp.DiscoveredSsdpDevice"/> instance containing the discovered details and allowing access to the full device description.
+ /// </summary>
+ public DiscoveredSsdpDevice DiscoveredDevice
+ {
+ get { return _DiscoveredDevice; }
+ }
+
+ #endregion
+
+ }
+} \ No newline at end of file
diff --git a/RSSDP/DeviceEventArgs.cs b/RSSDP/DeviceEventArgs.cs
new file mode 100644
index 0000000000..e0c19c4c47
--- /dev/null
+++ b/RSSDP/DeviceEventArgs.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Rssdp
+{
+ /// <summary>
+ /// Event arguments for the <see cref="SsdpDevice.DeviceAdded"/> and <see cref="SsdpDevice.DeviceRemoved"/> events.
+ /// </summary>
+ public sealed class DeviceEventArgs : EventArgs
+ {
+
+ #region Fields
+
+ private readonly SsdpDevice _Device;
+
+ #endregion
+
+ #region Constructors
+
+ /// <summary>
+ /// Constructs a new instance for the specified <see cref="SsdpDevice"/>.
+ /// </summary>
+ /// <param name="device">The <see cref="SsdpDevice"/> associated with the event this argument class is being used for.</param>
+ /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="device"/> argument is null.</exception>
+ public DeviceEventArgs(SsdpDevice device)
+ {
+ if (device == null) throw new ArgumentNullException("device");
+
+ _Device = device;
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ /// <summary>
+ /// Returns the <see cref="SsdpDevice"/> instance the event being raised for.
+ /// </summary>
+ public SsdpDevice Device
+ {
+ get { return _Device; }
+ }
+
+ #endregion
+
+ }
+} \ No newline at end of file
diff --git a/RSSDP/DeviceUnavailableEventArgs.cs b/RSSDP/DeviceUnavailableEventArgs.cs
new file mode 100644
index 0000000000..5b7c1437ad
--- /dev/null
+++ b/RSSDP/DeviceUnavailableEventArgs.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Rssdp
+{
+ /// <summary>
+ /// Event arguments for the <see cref="Rssdp.Infrastructure.SsdpDeviceLocatorBase.DeviceUnavailable"/> event.
+ /// </summary>
+ public sealed class DeviceUnavailableEventArgs : EventArgs
+ {
+
+ #region Fields
+
+ private readonly DiscoveredSsdpDevice _DiscoveredDevice;
+ private readonly bool _Expired;
+
+ #endregion
+
+ #region Constructors
+
+ /// <summary>
+ /// Full constructor.
+ /// </summary>
+ /// <param name="discoveredDevice">A <see cref="DiscoveredSsdpDevice"/> instance representing the device that has become unavailable.</param>
+ /// <param name="expired">A boolean value indicating whether this device is unavailable because it expired, or because it explicitly sent a byebye notification.. See <see cref="Expired"/> for more detail.</param>
+ /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="discoveredDevice"/> parameter is null.</exception>
+ public DeviceUnavailableEventArgs(DiscoveredSsdpDevice discoveredDevice, bool expired)
+ {
+ if (discoveredDevice == null) throw new ArgumentNullException("discoveredDevice");
+
+ _DiscoveredDevice = discoveredDevice;
+ _Expired = expired;
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ /// <summary>
+ /// Returns true if the device is considered unavailable because it's cached information expired before a new alive notification or search result was received. Returns false if the device is unavailable because it sent an explicit notification of it's unavailability.
+ /// </summary>
+ public bool Expired
+ {
+ get { return _Expired; }
+ }
+
+ /// <summary>
+ /// A reference to a <see cref="Rssdp.DiscoveredSsdpDevice"/> instance containing the discovery details of the removed device.
+ /// </summary>
+ public DiscoveredSsdpDevice DiscoveredDevice
+ {
+ get { return _DiscoveredDevice; }
+ }
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/RSSDP/DiscoveredSsdpDevice.cs b/RSSDP/DiscoveredSsdpDevice.cs
new file mode 100644
index 0000000000..58f0acfa51
--- /dev/null
+++ b/RSSDP/DiscoveredSsdpDevice.cs
@@ -0,0 +1,171 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+using System.Net.Http.Headers;
+
+namespace Rssdp
+{
+ /// <summary>
+ /// Represents a discovered device, containing basic information about the device and the location of it's full device description document. Also provides convenience methods for retrieving the device description document.
+ /// </summary>
+ /// <seealso cref="SsdpDevice"/>
+ /// <seealso cref="Rssdp.Infrastructure.ISsdpDeviceLocator"/>
+ public sealed class DiscoveredSsdpDevice
+ {
+
+ #region Fields
+
+ private SsdpRootDevice _Device;
+ private DateTimeOffset _AsAt;
+
+ private static HttpClient s_DefaultHttpClient;
+
+ #endregion
+
+ #region Public Properties
+
+ /// <summary>
+ /// Sets or returns the type of notification, being either a uuid, device type, service type or upnp:rootdevice.
+ /// </summary>
+ public string NotificationType { get; set; }
+
+ /// <summary>
+ /// Sets or returns the universal service name (USN) of the device.
+ /// </summary>
+ public string Usn { get; set; }
+
+ /// <summary>
+ /// Sets or returns a URL pointing to the device description document for this device.
+ /// </summary>
+ public Uri DescriptionLocation { get; set; }
+
+ /// <summary>
+ /// Sets or returns the length of time this information is valid for (from the <see cref="AsAt"/> time).
+ /// </summary>
+ public TimeSpan CacheLifetime { get; set; }
+
+ /// <summary>
+ /// Sets or returns the date and time this information was received.
+ /// </summary>
+ public DateTimeOffset AsAt
+ {
+ get { return _AsAt; }
+ set
+ {
+ if (_AsAt != value)
+ {
+ _AsAt = value;
+ _Device = null;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns the headers from the SSDP device response message
+ /// </summary>
+ public HttpHeaders ResponseHeaders { get; set; }
+
+ #endregion
+
+ #region Public Methods
+
+ /// <summary>
+ /// Returns true if this device information has expired, based on the current date/time, and the <see cref="CacheLifetime"/> &amp; <see cref="AsAt"/> properties.
+ /// </summary>
+ /// <returns></returns>
+ public bool IsExpired()
+ {
+ 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
+
+ /// <summary>
+ /// Returns the device's <see cref="Usn"/> value.
+ /// </summary>
+ /// <returns>A string containing the device's universal service name.</returns>
+ public override string ToString()
+ {
+ return this.Usn;
+ }
+
+ #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
new file mode 100644
index 0000000000..87f2aa71c2
--- /dev/null
+++ b/RSSDP/DisposableManagedObjectBase.cs
@@ -0,0 +1,76 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Rssdp.Infrastructure
+{
+ /// <summary>
+ /// Correclty implements the <see cref="IDisposable"/> interface and pattern for an object containing only managed resources, and adds a few common niceities not on the interface such as an <see cref="IsDisposed"/> property.
+ /// </summary>
+ public abstract class DisposableManagedObjectBase : IDisposable
+ {
+
+ #region Public Methods
+
+ /// <summary>
+ /// Override this method and dispose any objects you own the lifetime of if disposing is true;
+ /// </summary>
+ /// <param name="disposing">True if managed objects should be disposed, if false, only unmanaged resources should be released.</param>
+ protected abstract void Dispose(bool disposing);
+
+ /// <summary>
+ /// Throws and <see cref="System.ObjectDisposedException"/> if the <see cref="IsDisposed"/> property is true.
+ /// </summary>
+ /// <seealso cref="IsDisposed"/>
+ /// <exception cref="System.ObjectDisposedException">Thrown if the <see cref="IsDisposed"/> property is true.</exception>
+ /// <seealso cref="Dispose()"/>
+ protected virtual void ThrowIfDisposed()
+ {
+ if (this.IsDisposed) throw new ObjectDisposedException(this.GetType().FullName);
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ /// <summary>
+ /// Sets or returns a boolean indicating whether or not this instance has been disposed.
+ /// </summary>
+ /// <seealso cref="Dispose()"/>
+ public bool IsDisposed
+ {
+ get;
+ private set;
+ }
+
+ #endregion
+
+ #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);
+ }
+ }
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/RSSDP/GlobalSuppressions.cs b/RSSDP/GlobalSuppressions.cs
new file mode 100644
index 0000000000..b9aa0c369e
--- /dev/null
+++ b/RSSDP/GlobalSuppressions.cs
Binary files differ
diff --git a/RSSDP/HttpParserBase.cs b/RSSDP/HttpParserBase.cs
new file mode 100644
index 0000000000..7934419b0b
--- /dev/null
+++ b/RSSDP/HttpParserBase.cs
@@ -0,0 +1,244 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Text;
+
+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
+
+ }
+} \ No newline at end of file
diff --git a/RSSDP/HttpRequestParser.cs b/RSSDP/HttpRequestParser.cs
new file mode 100644
index 0000000000..0923f291f7
--- /dev/null
+++ b/RSSDP/HttpRequestParser.cs
@@ -0,0 +1,91 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Rssdp.Infrastructure
+{
+ /// <summary>
+ /// Parses a string into a <see cref="System.Net.Http.HttpRequestMessage"/> or throws an exception.
+ /// </summary>
+ public sealed class HttpRequestParser : HttpParserBase<HttpRequestMessage>
+ {
+
+ #region Fields & Constants
+
+ 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"
+ };
+
+ #endregion
+
+ #region Public Methods
+
+ /// <summary>
+ /// Parses the specified data into a <see cref="System.Net.Http.HttpRequestMessage"/> instance.
+ /// </summary>
+ /// <param name="data">A string containing the data to parse.</param>
+ /// <returns>A <see cref="System.Net.Http.HttpRequestMessage"/> instance containing the parsed data.</returns>
+ public override System.Net.Http.HttpRequestMessage Parse(string data)
+ {
+ System.Net.Http.HttpRequestMessage retVal = null;
+
+ try
+ {
+ retVal = new System.Net.Http.HttpRequestMessage();
+
+ retVal.Content = Parse(retVal, retVal.Headers, data);
+
+ return retVal;
+ }
+ finally
+ {
+ if (retVal != null)
+ retVal.Dispose();
+ }
+ }
+
+ #endregion
+
+ #region Overrides
+
+ /// <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 override void ParseStatusLine(string data, HttpRequestMessage message)
+ {
+ if (data == null) throw new ArgumentNullException("data");
+ if (message == null) throw new ArgumentNullException("message");
+
+ var parts = data.Split(' ');
+ if (parts.Length < 3) throw new ArgumentException("Status line is invalid. Insufficient status parts.", "data");
+
+ message.Method = new HttpMethod(parts[0].Trim());
+ Uri requestUri;
+ if (Uri.TryCreate(parts[1].Trim(), UriKind.RelativeOrAbsolute, out requestUri))
+ message.RequestUri = requestUri;
+ else
+ System.Diagnostics.Debug.WriteLine(parts[1]);
+
+ message.Version = ParseHttpVersion(parts[2].Trim());
+ }
+
+ /// <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 override bool IsContentHeader(string headerName)
+ {
+ return ContentHeaderNames.Contains(headerName, StringComparer.OrdinalIgnoreCase);
+ }
+
+ #endregion
+
+ }
+} \ No newline at end of file
diff --git a/RSSDP/HttpResponseParser.cs b/RSSDP/HttpResponseParser.cs
new file mode 100644
index 0000000000..ba85a16573
--- /dev/null
+++ b/RSSDP/HttpResponseParser.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Rssdp.Infrastructure
+{
+ /// <summary>
+ /// Parses a string into a <see cref="System.Net.Http.HttpResponseMessage"/> or throws an exception.
+ /// </summary>
+ public sealed class HttpResponseParser : HttpParserBase<System.Net.Http.HttpResponseMessage>
+ {
+
+ #region Fields & Constants
+
+ private static 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"
+ };
+
+ #endregion
+
+ #region Public Methods
+
+ /// <summary>
+ /// Parses the specified data into a <see cref="System.Net.Http.HttpResponseMessage"/> instance.
+ /// </summary>
+ /// <param name="data">A string containing the data to parse.</param>
+ /// <returns>A <see cref="System.Net.Http.HttpResponseMessage"/> instance containing the parsed data.</returns>
+ public override HttpResponseMessage Parse(string data)
+ {
+ System.Net.Http.HttpResponseMessage retVal = null;
+ try
+ {
+ retVal = new System.Net.Http.HttpResponseMessage();
+
+ retVal.Content = Parse(retVal, retVal.Headers, data);
+
+ return retVal;
+ }
+ catch
+ {
+ if (retVal != null)
+ retVal.Dispose();
+
+ throw;
+ }
+ }
+
+ #endregion
+
+ #region Overrides Methods
+
+ /// <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>
+ /// <returns>A boolean, true if th specified header relates to HTTP content, otherwise false.</returns>
+ protected override bool IsContentHeader(string headerName)
+ {
+ return ContentHeaderNames.Contains(headerName, StringComparer.OrdinalIgnoreCase);
+ }
+
+ /// <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 override void ParseStatusLine(string data, HttpResponseMessage message)
+ {
+ if (data == null) throw new ArgumentNullException("data");
+ if (message == null) throw new ArgumentNullException("message");
+
+ var parts = data.Split(' ');
+ if (parts.Length < 3) throw new ArgumentException("data status line is invalid. Insufficient status parts.", "data");
+
+ message.Version = ParseHttpVersion(parts[0].Trim());
+
+ int statusCode = -1;
+ if (!Int32.TryParse(parts[1].Trim(), out statusCode))
+ throw new ArgumentException("data status line is invalid. Status code is not a valid integer.", "data");
+
+ message.StatusCode = (HttpStatusCode)statusCode;
+ message.ReasonPhrase = parts[2].Trim();
+ }
+
+ #endregion
+
+ }
+} \ No newline at end of file
diff --git a/RSSDP/IEnumerableExtensions.cs b/RSSDP/IEnumerableExtensions.cs
new file mode 100644
index 0000000000..f72073949d
--- /dev/null
+++ b/RSSDP/IEnumerableExtensions.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Rssdp.Infrastructure
+{
+ internal static class IEnumerableExtensions
+ {
+ public static IEnumerable<T> SelectManyRecursive<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> selector)
+ {
+ if (source == null) throw new ArgumentNullException("source");
+ if (selector == null) throw new ArgumentNullException("selector");
+
+ return !source.Any() ? source :
+ source.Concat(
+ source
+ .SelectMany(i => selector(i).EmptyIfNull())
+ .SelectManyRecursive(selector)
+ );
+ }
+
+ public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> source)
+ {
+ return source ?? Enumerable.Empty<T>();
+ }
+ }
+}
diff --git a/RSSDP/ISsdpCommunicationsServer.cs b/RSSDP/ISsdpCommunicationsServer.cs
new file mode 100644
index 0000000000..eea5e0ed67
--- /dev/null
+++ b/RSSDP/ISsdpCommunicationsServer.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Net;
+
+namespace Rssdp.Infrastructure
+{
+ /// <summary>
+ /// Interface for a component that manages network communication (sending and receiving HTTPU messages) for the SSDP protocol.
+ /// </summary>
+ public interface ISsdpCommunicationsServer : IDisposable
+ {
+
+ #region Events
+
+ /// <summary>
+ /// Raised when a HTTPU request message is received by a socket (unicast or multicast).
+ /// </summary>
+ event EventHandler<RequestReceivedEventArgs> RequestReceived;
+
+ /// <summary>
+ /// Raised when an HTTPU response message is received by a socket (unicast or multicast).
+ /// </summary>
+ event EventHandler<ResponseReceivedEventArgs> ResponseReceived;
+
+ #endregion
+
+ #region Methods
+
+ /// <summary>
+ /// Causes the server to begin listening for multicast messages, being SSDP search requests and notifications.
+ /// </summary>
+ void BeginListeningForBroadcasts();
+
+ /// <summary>
+ /// Causes the server to stop listening for multicast messages, being SSDP search requests and notifications.
+ /// </summary>
+ 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>
+ /// <param name="messageData">A byte array containing the data to send.</param>
+ /// <param name="destination">A <see cref="IpEndPointInfo"/> representing the destination address for the data. Can be either a multicast or unicast destination.</param>
+ /// <param name="fromLocalIpAddress">A <see cref="IpEndPointInfo"/> The local ip address to send from, or .Any if sending from all available</param>
+ Task SendMessage(byte[] messageData, IpEndPointInfo destination, IpAddressInfo fromLocalIpAddress);
+
+ /// <summary>
+ /// Sends a message to the SSDP multicast address and port.
+ /// </summary>
+ Task SendMulticastMessage(string message);
+
+ #endregion
+
+ #region Properties
+
+ /// <summary>
+ /// Gets or sets a boolean value indicating whether or not this instance is shared amongst multiple <see cref="SsdpDeviceLocatorBase"/> and/or <see cref="ISsdpDevicePublisher"/> instances.
+ /// </summary>
+ /// <remarks>
+ /// <para>If true, disposing an instance of a <see cref="SsdpDeviceLocatorBase"/>or a <see cref="ISsdpDevicePublisher"/> will not dispose this comms server instance. The calling code is responsible for managing the lifetime of the server.</para>
+ /// </remarks>
+ bool IsShared { get; set; }
+
+ #endregion
+
+ }
+} \ No newline at end of file
diff --git a/RSSDP/ISsdpDeviceLocator.cs b/RSSDP/ISsdpDeviceLocator.cs
new file mode 100644
index 0000000000..4b7d10796b
--- /dev/null
+++ b/RSSDP/ISsdpDeviceLocator.cs
@@ -0,0 +1,146 @@
+using System;
+
+namespace Rssdp.Infrastructure
+{
+ /// <summary>
+ /// Interface for components that discover the existence of SSDP devices.
+ /// </summary>
+ /// <remarks>
+ /// <para>Discovering devices includes explicit search requests as well as listening for broadcast status notifications.</para>
+ /// </remarks>
+ /// <seealso cref="DiscoveredSsdpDevice"/>
+ /// <seealso cref="SsdpDevice"/>
+ /// <seealso cref="ISsdpDevicePublisher"/>
+ public interface ISsdpDeviceLocator
+ {
+
+ #region Events
+
+ /// <summary>
+ /// Event raised when a device becomes available or is found by a search request.
+ /// </summary>
+ /// <seealso cref="NotificationFilter"/>
+ /// <seealso cref="DeviceUnavailable"/>
+ /// <seealso cref="StartListeningForNotifications"/>
+ /// <seealso cref="StopListeningForNotifications"/>
+ event EventHandler<DeviceAvailableEventArgs> DeviceAvailable;
+
+ /// <summary>
+ /// Event raised when a device explicitly notifies of shutdown or a device expires from the cache.
+ /// </summary>
+ /// <seeseealso cref="NotificationFilter"/>
+ /// <seealso cref="DeviceAvailable"/>
+ /// <seealso cref="StartListeningForNotifications"/>
+ /// <seealso cref="StopListeningForNotifications"/>
+ event EventHandler<DeviceUnavailableEventArgs> DeviceUnavailable;
+
+ #endregion
+
+ #region Properties
+
+ /// <summary>
+ /// Sets or returns a string containing the filter for notifications. Notifications not matching the filter will not raise the <see cref="DeviceAvailable"/> or <see cref="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>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="DeviceAvailable"/>
+ /// <seealso cref="DeviceUnavailable"/>
+ /// <seealso cref="StartListeningForNotifications"/>
+ /// <seealso cref="StopListeningForNotifications"/>
+ string NotificationFilter
+ {
+ get;
+ set;
+ }
+
+ /// <summary>
+ /// Returns a boolean indicating whether or not a search is currently active.
+ /// </summary>
+ bool IsSearching { get; }
+
+ #endregion
+
+ #region Methods
+
+ #region SearchAsync Overloads
+
+ /// <summary>
+ /// Aynchronously performs a search for all devices using the default search timeout, and returns an awaitable task that can be used to retrieve the results.
+ /// </summary>
+ /// <returns>A task whose result is an <see cref="System.Collections.Generic.IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns>
+ System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<DiscoveredSsdpDevice>> SearchAsync();
+
+ /// <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>
+ /// <returns>A task whose result is an <see cref="System.Collections.Generic.IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns>
+ System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<DiscoveredSsdpDevice>> SearchAsync(string searchTarget);
+
+ /// <summary>
+ /// Performs a search for the specified search target (criteria) and 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>A device namespace and type in format of urn:&lt;device namespace&gt;:device:&lt;device type&gt;:&lt;device version&gt; i.e urn:schemas-upnp-org:device:Basic:1</description></item>
+ /// <item><term>Service type</term><description>A service namespace and type in format of urn:&lt;service namespace&gt;:service:&lt;servicetype&gt;:&lt;service version&gt; i.e urn:my-namespace:service:MyCustomService:1</description></item>
+ /// </list>
+ /// </param>
+ /// <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 is recommended by the UPnP 1.1 specification. Specify TimeSpan.Zero to return only devices already in the cache.</param>
+ /// <remarks>
+ /// <para>By design RSSDP does not support 'publishing services' as it is intended for use with non-standard UPnP devices that don't publish UPnP style services. However, it is still possible to use RSSDP to search for devices implemetning these services if you know the service type.</para>
+ /// </remarks>
+ /// <returns>A task whose result is an <see cref="System.Collections.Generic.IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns>
+ System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<DiscoveredSsdpDevice>> SearchAsync(string searchTarget, TimeSpan searchWaitTime);
+
+ /// <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 is recommended by the UPnP 1.1 specification. Specify TimeSpan.Zero to return only devices already in the cache.</param>
+ /// <returns>A task whose result is an <see cref="System.Collections.Generic.IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns>
+ System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<DiscoveredSsdpDevice>> SearchAsync(TimeSpan searchWaitTime);
+
+ #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"/>
+ /// <seealso cref="NotificationFilter"/>
+ void StartListeningForNotifications();
+
+ /// <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>
+ /// <exception cref="System.ObjectDisposedException">Throw if the <see cref="DisposableManagedObjectBase.IsDisposed"/> property is true.</exception>
+ /// <seealso cref="StartListeningForNotifications"/>
+ /// <seealso cref="DeviceAvailable"/>
+ /// <seealso cref="DeviceUnavailable"/>
+ /// <seealso cref="NotificationFilter"/>
+ void StopListeningForNotifications();
+
+ #endregion
+
+ }
+} \ No newline at end of file
diff --git a/RSSDP/ISsdpDevicePublisher.cs b/RSSDP/ISsdpDevicePublisher.cs
new file mode 100644
index 0000000000..b6ebc4176b
--- /dev/null
+++ b/RSSDP/ISsdpDevicePublisher.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Threading.Tasks;
+
+namespace Rssdp.Infrastructure
+{
+ /// <summary>
+ /// Interface for components that publish the existence of SSDP devices.
+ /// </summary>
+ /// <remarks>
+ /// <para>Publishing a device includes sending notifications (alive and byebye) as well as responding to search requests when appropriate.</para>
+ /// </remarks>
+ /// <seealso cref="SsdpRootDevice"/>
+ /// <seealso cref="ISsdpDeviceLocator"/>
+ public interface ISsdpDevicePublisher
+ {
+ /// <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>
+ /// <param name="device">The <see cref="SsdpRootDevice"/> instance to add.</param>
+ /// <returns>An awaitable <see cref="System.Threading.Tasks.Task"/>.</returns>
+ void AddDevice(SsdpRootDevice device);
+
+ /// <summary>
+ /// Removes a device (and it's children) from the list of devices being published by this server, making them undiscoverable.
+ /// </summary>
+ /// <param name="device">The <see cref="SsdpRootDevice"/> instance to add.</param>
+ /// <returns>An awaitable <see cref="System.Threading.Tasks.Task"/>.</returns>
+ Task RemoveDevice(SsdpRootDevice device);
+
+ /// <summary>
+ /// Returns a read only list of devices being published by this instance.
+ /// </summary>
+ /// <seealso cref="SsdpDevice"/>
+ System.Collections.Generic.IEnumerable<SsdpRootDevice> Devices { get; }
+
+ }
+}
diff --git a/RSSDP/IUPnPDeviceValidator.cs b/RSSDP/IUPnPDeviceValidator.cs
new file mode 100644
index 0000000000..39b80742e6
--- /dev/null
+++ b/RSSDP/IUPnPDeviceValidator.cs
@@ -0,0 +1,27 @@
+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.IEnumerable<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.IEnumerable<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/Properties/AssemblyInfo.cs b/RSSDP/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..1ce64b159d
--- /dev/null
+++ b/RSSDP/Properties/AssemblyInfo.cs
@@ -0,0 +1,30 @@
+using System.Resources;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("RSSDP2")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("RSSDP2")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[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
+//
+// 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/RSSDP/RSSDP.csproj b/RSSDP/RSSDP.csproj
new file mode 100644
index 0000000000..ef1f322078
--- /dev/null
+++ b/RSSDP/RSSDP.csproj
@@ -0,0 +1,89 @@
+<?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>
+ <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="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" />
+ </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" />
+ <!-- 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/RSSDP/RSSDP.nuget.targets b/RSSDP/RSSDP.nuget.targets
new file mode 100644
index 0000000000..e69ce0e64f
--- /dev/null
+++ b/RSSDP/RSSDP.nuget.targets
@@ -0,0 +1,6 @@
+<?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/RequestReceivedEventArgs.cs b/RSSDP/RequestReceivedEventArgs.cs
new file mode 100644
index 0000000000..03c059634f
--- /dev/null
+++ b/RSSDP/RequestReceivedEventArgs.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Net;
+
+namespace Rssdp.Infrastructure
+{
+ /// <summary>
+ /// Provides arguments for the <see cref="ISsdpCommunicationsServer.RequestReceived"/> event.
+ /// </summary>
+ public sealed class RequestReceivedEventArgs : EventArgs
+ {
+
+ #region Fields
+
+ private readonly HttpRequestMessage _Message;
+ private readonly IpEndPointInfo _ReceivedFrom;
+
+ #endregion
+
+ public IpAddressInfo LocalIpAddress { get; private set; }
+
+ #region Constructors
+
+ /// <summary>
+ /// Full constructor.
+ /// </summary>
+ public RequestReceivedEventArgs(HttpRequestMessage message, IpEndPointInfo receivedFrom, IpAddressInfo localIpAddress)
+ {
+ _Message = message;
+ _ReceivedFrom = receivedFrom;
+ LocalIpAddress = localIpAddress;
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ /// <summary>
+ /// The <see cref="HttpRequestMessage"/> that was received.
+ /// </summary>
+ public HttpRequestMessage Message
+ {
+ get { return _Message; }
+ }
+
+ /// <summary>
+ /// The <see cref="UdpEndPoint"/> the request came from.
+ /// </summary>
+ public IpEndPointInfo ReceivedFrom
+ {
+ get { return _ReceivedFrom; }
+ }
+
+ #endregion
+
+ }
+} \ No newline at end of file
diff --git a/RSSDP/ResponseReceivedEventArgs.cs b/RSSDP/ResponseReceivedEventArgs.cs
new file mode 100644
index 0000000000..f7dc5813d9
--- /dev/null
+++ b/RSSDP/ResponseReceivedEventArgs.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Net;
+
+namespace Rssdp.Infrastructure
+{
+ /// <summary>
+ /// Provides arguments for the <see cref="ISsdpCommunicationsServer.ResponseReceived"/> event.
+ /// </summary>
+ public sealed class ResponseReceivedEventArgs : EventArgs
+ {
+
+ #region Fields
+
+ private readonly HttpResponseMessage _Message;
+ private readonly IpEndPointInfo _ReceivedFrom;
+
+ #endregion
+
+ #region Constructors
+
+ /// <summary>
+ /// Full constructor.
+ /// </summary>
+ public ResponseReceivedEventArgs(HttpResponseMessage message, IpEndPointInfo receivedFrom)
+ {
+ _Message = message;
+ _ReceivedFrom = receivedFrom;
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ /// <summary>
+ /// The <see cref="HttpResponseMessage"/> that was received.
+ /// </summary>
+ public HttpResponseMessage Message
+ {
+ get { return _Message; }
+ }
+
+ /// <summary>
+ /// The <see cref="UdpEndPoint"/> the response came from.
+ /// </summary>
+ public IpEndPointInfo ReceivedFrom
+ {
+ get { return _ReceivedFrom; }
+ }
+
+ #endregion
+
+ }
+}
diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs
new file mode 100644
index 0000000000..b709861d5f
--- /dev/null
+++ b/RSSDP/SsdpCommunicationsServer.cs
@@ -0,0 +1,499 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Net;
+
+namespace Rssdp.Infrastructure
+{
+ /// <summary>
+ /// Provides the platform independent logic for publishing device existence and responding to search requests.
+ /// </summary>
+ public sealed class SsdpCommunicationsServer : DisposableManagedObjectBase, ISsdpCommunicationsServer
+ {
+
+ #region Fields
+
+ /*
+
+ We could technically use one socket listening on port 1900 for everything.
+ This should get both multicast (notifications) and unicast (search response) messages, however
+ this often doesn't work under Windows because the MS SSDP service is running. If that service
+ is running then it will steal the unicast messages and we will never see search responses.
+ Since stopping the service would be a bad idea (might not be allowed security wise and might
+ break other apps running on the system) the only other work around is to use two sockets.
+
+ We use one socket to listen for/receive notifications and search requests (_BroadcastListenSocket).
+ We use a second socket, bound to a different local port, to send search requests and listen for
+ responses (_SendSocket). The responses are sent to the local port this socket is bound to,
+ which isn't port 1900 so the MS service doesn't steal them. While the caller can specify a local
+ port to use, we will default to 0 which allows the underlying system to auto-assign a free port.
+
+ */
+
+ private object _BroadcastListenSocketSynchroniser = new object();
+ private IUdpSocket _BroadcastListenSocket;
+
+ private object _SendSocketSynchroniser = new object();
+ private List<IUdpSocket> _sendSockets;
+
+ private HttpRequestParser _RequestParser;
+ private HttpResponseParser _ResponseParser;
+ private readonly ILogger _logger;
+ private ISocketFactory _SocketFactory;
+ private readonly INetworkManager _networkManager;
+
+ private int _LocalPort;
+ private int _MulticastTtl;
+
+ private bool _IsShared;
+ private readonly bool _enableMultiSocketBinding;
+
+ #endregion
+
+ #region Events
+
+ /// <summary>
+ /// Raised when a HTTPU request message is received by a socket (unicast or multicast).
+ /// </summary>
+ public event EventHandler<RequestReceivedEventArgs> RequestReceived;
+
+ /// <summary>
+ /// Raised when an HTTPU response message is received by a socket (unicast or multicast).
+ /// </summary>
+ public event EventHandler<ResponseReceivedEventArgs> ResponseReceived;
+
+ #endregion
+
+ #region Constructors
+
+ /// <summary>
+ /// Minimum constructor.
+ /// </summary>
+ /// <exception cref="System.ArgumentNullException">The <paramref name="socketFactory"/> argument is null.</exception>
+ public SsdpCommunicationsServer(ISocketFactory socketFactory, INetworkManager networkManager, ILogger logger, bool enableMultiSocketBinding)
+ : this(socketFactory, 0, SsdpConstants.SsdpDefaultMulticastTimeToLive, networkManager, logger, enableMultiSocketBinding)
+ {
+ }
+
+ /// <summary>
+ /// Full constructor.
+ /// </summary>
+ /// <exception cref="System.ArgumentNullException">The <paramref name="socketFactory"/> argument is null.</exception>
+ /// <exception cref="System.ArgumentOutOfRangeException">The <paramref name="multicastTimeToLive"/> argument is less than or equal to zero.</exception>
+ public SsdpCommunicationsServer(ISocketFactory socketFactory, int localPort, int multicastTimeToLive, INetworkManager networkManager, ILogger logger, bool enableMultiSocketBinding)
+ {
+ if (socketFactory == null) throw new ArgumentNullException("socketFactory");
+ if (multicastTimeToLive <= 0) throw new ArgumentOutOfRangeException("multicastTimeToLive", "multicastTimeToLive must be greater than zero.");
+
+ _BroadcastListenSocketSynchroniser = new object();
+ _SendSocketSynchroniser = new object();
+
+ _LocalPort = localPort;
+ _SocketFactory = socketFactory;
+
+ _RequestParser = new HttpRequestParser();
+ _ResponseParser = new HttpResponseParser();
+
+ _MulticastTtl = multicastTimeToLive;
+ _networkManager = networkManager;
+ _logger = logger;
+ _enableMultiSocketBinding = enableMultiSocketBinding;
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ /// <summary>
+ /// Causes the server to begin listening for multicast messages, being SSDP search requests and notifications.
+ /// </summary>
+ /// <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 BeginListeningForBroadcasts()
+ {
+ ThrowIfDisposed();
+
+ if (_BroadcastListenSocket == null)
+ {
+ lock (_BroadcastListenSocketSynchroniser)
+ {
+ if (_BroadcastListenSocket == null)
+ _BroadcastListenSocket = ListenForBroadcastsAsync();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Causes the server to stop listening for multicast messages, being SSDP search requests and notifications.
+ /// </summary>
+ /// <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)
+ {
+ _BroadcastListenSocket.Dispose();
+ _BroadcastListenSocket = null;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Sends a message to a particular address (uni or multicast) and port.
+ /// </summary>
+ /// <param name="messageData">A byte array containing the data to send.</param>
+ /// <param name="destination">A <see cref="IpEndPointInfo"/> representing the destination address for the data. Can be either a multicast or unicast destination.</param>
+ /// <param name="fromLocalIpAddress">A <see cref="IpEndPointInfo"/> The local ip address to send from, or .Any if sending from all available</param>
+ /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="messageData"/> argument is null.</exception>
+ /// <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 async Task SendMessage(byte[] messageData, IpEndPointInfo destination, IpAddressInfo fromLocalIpAddress)
+ {
+ if (messageData == null) throw new ArgumentNullException("messageData");
+
+ ThrowIfDisposed();
+
+ var sockets = GetSendSockets(fromLocalIpAddress, destination);
+
+ if (sockets.Count == 0)
+ {
+ return;
+ }
+
+ // 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++)
+ {
+ var tasks = sockets.Select(s => SendFromSocket(s, messageData, destination)).ToArray();
+ await Task.WhenAll(tasks).ConfigureAwait(false);
+
+ await Task.Delay(100).ConfigureAwait(false);
+ }
+ }
+
+ private async Task SendFromSocket(IUdpSocket socket, byte[] messageData, IpEndPointInfo destination)
+ {
+ try
+ {
+ await socket.SendAsync(messageData, messageData.Length, destination).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error sending socket message from {0} to {1}", ex, socket.LocalIPAddress.ToString(), destination.ToString());
+ }
+ }
+
+ private List<IUdpSocket> GetSendSockets(IpAddressInfo fromLocalIpAddress, IpEndPointInfo destination)
+ {
+ EnsureSendSocketCreated();
+
+ lock (_SendSocketSynchroniser)
+ {
+ var sockets = _sendSockets.Where(i => i.LocalIPAddress.AddressFamily == fromLocalIpAddress.AddressFamily);
+
+ // Send from the Any socket and the socket with the matching address
+ if (fromLocalIpAddress.AddressFamily == IpAddressFamily.InterNetwork)
+ {
+ sockets = sockets.Where(i => i.LocalIPAddress.Equals(IpAddressInfo.Any) || fromLocalIpAddress.Equals(i.LocalIPAddress));
+
+ // If sending to the loopback address, filter the socket list as well
+ if (destination.IpAddress.Equals(IpAddressInfo.Loopback))
+ {
+ sockets = sockets.Where(i => i.LocalIPAddress.Equals(IpAddressInfo.Any) || i.LocalIPAddress.Equals(IpAddressInfo.Loopback));
+ }
+ }
+ else if (fromLocalIpAddress.AddressFamily == IpAddressFamily.InterNetworkV6)
+ {
+ sockets = sockets.Where(i => i.LocalIPAddress.Equals(IpAddressInfo.IPv6Any) || fromLocalIpAddress.Equals(i.LocalIPAddress));
+
+ // If sending to the loopback address, filter the socket list as well
+ if (destination.IpAddress.Equals(IpAddressInfo.IPv6Loopback))
+ {
+ sockets = sockets.Where(i => i.LocalIPAddress.Equals(IpAddressInfo.IPv6Any) || i.LocalIPAddress.Equals(IpAddressInfo.IPv6Loopback));
+ }
+ }
+
+ return sockets.ToList();
+ }
+ }
+
+ /// <summary>
+ /// Sends a message to the SSDP multicast address and port.
+ /// </summary>
+ public async Task SendMulticastMessage(string message)
+ {
+ if (message == null) throw new ArgumentNullException("messageData");
+
+ byte[] messageData = Encoding.UTF8.GetBytes(message);
+
+ ThrowIfDisposed();
+
+ 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++)
+ {
+ await SendMessageIfSocketNotDisposed(messageData, new IpEndPointInfo
+ {
+ IpAddress = new IpAddressInfo
+ {
+ Address = SsdpConstants.MulticastLocalAdminAddress
+ },
+ Port = SsdpConstants.MulticastPort
+
+ }).ConfigureAwait(false);
+
+ await Task.Delay(100).ConfigureAwait(false);
+ }
+ }
+
+ /// <summary>
+ /// Stops listening for search responses on the local, unicast socket.
+ /// </summary>
+ /// <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)
+ {
+ var sockets = _sendSockets.ToList();
+ _sendSockets = null;
+
+ foreach (var socket in sockets)
+ {
+ socket.Dispose();
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ /// <summary>
+ /// Gets or sets a boolean value indicating whether or not this instance is shared amongst multiple <see cref="SsdpDeviceLocatorBase"/> and/or <see cref="ISsdpDevicePublisher"/> instances.
+ /// </summary>
+ /// <remarks>
+ /// <para>If true, disposing an instance of a <see cref="SsdpDeviceLocatorBase"/>or a <see cref="ISsdpDevicePublisher"/> will not dispose this comms server instance. The calling code is responsible for managing the lifetime of the server.</para>
+ /// </remarks>
+ public bool IsShared
+ {
+ get { return _IsShared; }
+ set { _IsShared = value; }
+ }
+
+ #endregion
+
+ #region Overrides
+
+ /// <summary>
+ /// Stops listening for requests, disposes this instance and all internal resources.
+ /// </summary>
+ /// <param name="disposing"></param>
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ lock (_BroadcastListenSocketSynchroniser)
+ {
+ if (_BroadcastListenSocket != null)
+ _BroadcastListenSocket.Dispose();
+ }
+
+ lock (_SendSocketSynchroniser)
+ {
+ if (_sendSockets != null)
+ {
+ var sockets = _sendSockets.ToList();
+ _sendSockets = null;
+
+ foreach (var socket in sockets)
+ {
+ socket.Dispose();
+ }
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private async Task SendMessageIfSocketNotDisposed(byte[] messageData, IpEndPointInfo destination)
+ {
+ var sockets = _sendSockets;
+ if (sockets != null)
+ {
+ sockets = sockets.ToList();
+
+ foreach (var socket in sockets)
+ {
+ await socket.SendAsync(messageData, messageData.Length, destination).ConfigureAwait(false);
+ }
+ }
+
+ ThrowIfDisposed();
+ }
+
+ private IUdpSocket ListenForBroadcastsAsync()
+ {
+ var socket = _SocketFactory.CreateUdpMulticastSocket(SsdpConstants.MulticastLocalAdminAddress, _MulticastTtl, SsdpConstants.MulticastPort);
+
+ ListenToSocket(socket);
+
+ return socket;
+ }
+
+ private List<IUdpSocket> CreateSocketAndListenForResponsesAsync()
+ {
+ var sockets = new List<IUdpSocket>();
+
+ sockets.Add(_SocketFactory.CreateSsdpUdpSocket(IpAddressInfo.Any, _LocalPort));
+
+ if (_enableMultiSocketBinding)
+ {
+ foreach (var address in _networkManager.GetLocalIpAddresses().ToList())
+ {
+ try
+ {
+ sockets.Add(_SocketFactory.CreateSsdpUdpSocket(address, _LocalPort));
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error in CreateSsdpUdpSocket. IPAddress: {0}", ex, address);
+ }
+ }
+ }
+
+ foreach (var socket in sockets)
+ {
+ ListenToSocket(socket);
+ }
+
+ return sockets;
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "t", Justification = "Capturing task to local variable removes compiler warning, task is not otherwise required.")]
+ private void ListenToSocket(IUdpSocket 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;
+ while (!cancelled)
+ {
+ try
+ {
+ var result = await socket.ReceiveAsync().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)
+ {
+ cancelled = true;
+ }
+ }
+ });
+ }
+
+ private void EnsureSendSocketCreated()
+ {
+ if (_sendSockets == null)
+ {
+ lock (_SendSocketSynchroniser)
+ {
+ if (_sendSockets == null)
+ {
+ _sendSockets = CreateSocketAndListenForResponsesAsync();
+ }
+ }
+ }
+ }
+
+ private void ProcessMessage(string data, IpEndPointInfo endPoint, IpAddressInfo receivedOnLocalIpAddress)
+ {
+ //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.
+ if (data.StartsWith("HTTP/", StringComparison.OrdinalIgnoreCase))
+ {
+ HttpResponseMessage responseMessage = null;
+ try
+ {
+ responseMessage = _ResponseParser.Parse(data);
+ }
+ catch (ArgumentException ex)
+ {
+ // Ignore invalid packets.
+ }
+
+ if (responseMessage != null)
+ OnResponseReceived(responseMessage, endPoint);
+ }
+ else
+ {
+ HttpRequestMessage requestMessage = null;
+ try
+ {
+ requestMessage = _RequestParser.Parse(data);
+ }
+ catch (ArgumentException ex)
+ {
+ // Ignore invalid packets.
+ }
+
+ if (requestMessage != null)
+ {
+ OnRequestReceived(requestMessage, endPoint, receivedOnLocalIpAddress);
+ }
+ }
+ }
+
+ private void OnRequestReceived(HttpRequestMessage data, IpEndPointInfo remoteEndPoint, IpAddressInfo receivedOnLocalIpAddress)
+ {
+ //SSDP specification says only * is currently used but other uri's might
+ //be implemented in the future and should be ignored unless understood.
+ //Section 4.2 - http://tools.ietf.org/html/draft-cai-ssdp-v1-03#page-11
+ if (data.RequestUri.ToString() != "*")
+ {
+ return;
+ }
+
+ var handlers = this.RequestReceived;
+ if (handlers != null)
+ handlers(this, new RequestReceivedEventArgs(data, remoteEndPoint, receivedOnLocalIpAddress));
+ }
+
+ private void OnResponseReceived(HttpResponseMessage data, IpEndPointInfo endPoint)
+ {
+ var handlers = this.ResponseReceived;
+ if (handlers != null)
+ handlers(this, new ResponseReceivedEventArgs(data, endPoint));
+ }
+
+ #endregion
+
+ }
+} \ No newline at end of file
diff --git a/RSSDP/SsdpConstants.cs b/RSSDP/SsdpConstants.cs
new file mode 100644
index 0000000000..c839d9e0bf
--- /dev/null
+++ b/RSSDP/SsdpConstants.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Rssdp.Infrastructure
+{
+ /// <summary>
+ /// Provides constants for common values related to the SSDP protocols.
+ /// </summary>
+ public static class SsdpConstants
+ {
+
+ /// <summary>
+ /// Multicast IP Address used for SSDP multicast messages. Values is 239.255.255.250.
+ /// </summary>
+ public const string MulticastLocalAdminAddress = "239.255.255.250";
+ /// <summary>
+ /// The UDP port used for SSDP multicast messages. Values is 1900.
+ /// </summary>
+ public const int MulticastPort = 1900;
+ /// <summary>
+ /// The default multicase TTL for SSDP multicast messages. Value is 4.
+ /// </summary>
+ public const int SsdpDefaultMulticastTimeToLive = 4;
+
+ internal const string MSearchMethod = "M-SEARCH";
+
+ internal const string SsdpDiscoverMessage = "ssdp:discover";
+ internal const string SsdpDiscoverAllSTHeader = "ssdp:all";
+
+ internal const string SsdpDeviceDescriptionXmlNamespace = "urn:schemas-upnp-org:device-1-0";
+
+ /// <summary>
+ /// Default buffer size for receiving SSDP broadcasts. Value is 8192 (bytes).
+ /// </summary>
+ public const int DefaultUdpSocketBufferSize = 8192;
+ /// <summary>
+ /// The maximum possible buffer size for a UDP message. Value is 65507 (bytes).
+ /// </summary>
+ public const int MaxUdpSocketBufferSize = 65507; // Max possible UDP packet size on IPv4 without using 'jumbograms'.
+
+ /// <summary>
+ /// Namespace/prefix for UPnP device types. Values is schemas-upnp-org.
+ /// </summary>
+ public const string UpnpDeviceTypeNamespace = "schemas-upnp-org";
+ /// <summary>
+ /// UPnP Root Device type. Value is upnp:rootdevice.
+ /// </summary>
+ public const string UpnpDeviceTypeRootDevice = "upnp:rootdevice";
+ /// <summary>
+ /// The value is used by Windows Explorer for device searches instead of the UPNPDeviceTypeRootDevice constant.
+ /// Not sure why (different spec, bug, alternate protocol etc). Used to enable Windows Explorer support.
+ /// </summary>
+ public const string PnpDeviceTypeRootDevice = "pnp:rootdevice";
+ /// <summary>
+ /// UPnP Basic Device type. Value is Basic.
+ /// </summary>
+ public const string UpnpDeviceTypeBasicDevice = "Basic";
+
+ internal const string SsdpKeepAliveNotification = "ssdp:alive";
+ internal const string SsdpByeByeNotification = "ssdp:byebye";
+
+ internal const int UdpResendCount = 3;
+
+ }
+}
diff --git a/RSSDP/SsdpDevice.cs b/RSSDP/SsdpDevice.cs
new file mode 100644
index 0000000000..a595742d00
--- /dev/null
+++ b/RSSDP/SsdpDevice.cs
@@ -0,0 +1,783 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml;
+using Rssdp.Infrastructure;
+
+namespace Rssdp
+{
+ /// <summary>
+ /// Base class representing the common details of a (root or embedded) device, either to be published or that has been located.
+ /// </summary>
+ /// <remarks>
+ /// <para>Do not derive new types directly from this class. New device classes should derive from either <see cref="SsdpRootDevice"/> or <see cref="SsdpEmbeddedDevice"/>.</para>
+ /// </remarks>
+ /// <seealso cref="SsdpRootDevice"/>
+ /// <seealso cref="SsdpEmbeddedDevice"/>
+ public abstract class SsdpDevice
+ {
+
+ #region Fields
+
+ private string _Udn;
+ private string _DeviceType;
+ private string _DeviceTypeNamespace;
+ private int _DeviceVersion;
+ private SsdpDevicePropertiesCollection _CustomProperties;
+ private CustomHttpHeadersCollection _CustomResponseHeaders;
+
+ private IList<SsdpDevice> _Devices;
+
+ #endregion
+
+ #region Events
+
+ /// <summary>
+ /// Raised when a new child device is added.
+ /// </summary>
+ /// <seealso cref="AddDevice"/>
+ /// <seealso cref="DeviceAdded"/>
+ public event EventHandler<DeviceEventArgs> DeviceAdded;
+
+ /// <summary>
+ /// Raised when a child device is removed.
+ /// </summary>
+ /// <seealso cref="RemoveDevice"/>
+ /// <seealso cref="DeviceRemoved"/>
+ public event EventHandler<DeviceEventArgs> DeviceRemoved;
+
+ #endregion
+
+ #region Constructors
+
+ /// <summary>
+ /// Derived type constructor, allows constructing a device with no parent. Should only be used from derived types that are or inherit from <see cref="SsdpRootDevice"/>.
+ /// </summary>
+ protected SsdpDevice()
+ {
+ _DeviceTypeNamespace = SsdpConstants.UpnpDeviceTypeNamespace;
+ _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()
+ {
+ if (deviceDescriptionXml == null) throw new ArgumentNullException("deviceDescriptionXml");
+ if (deviceDescriptionXml.Length == 0) throw new ArgumentException("deviceDescriptionXml cannot be an empty string.", "deviceDescriptionXml");
+
+ using (var ms = new System.IO.MemoryStream(System.Text.UTF8Encoding.UTF8.GetBytes(deviceDescriptionXml)))
+ {
+ var reader = XmlReader.Create(ms);
+
+ LoadDeviceProperties(reader, this);
+ }
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ #region UPnP Device Description Properties
+
+ /// <summary>
+ /// Sets or returns the core device type (not including namespace, version etc.). Required.
+ /// </summary>
+ /// <remarks><para>Defaults to the UPnP basic device type.</para></remarks>
+ /// <seealso cref="DeviceTypeNamespace"/>
+ /// <seealso cref="DeviceVersion"/>
+ /// <seealso cref="FullDeviceType"/>
+ public string DeviceType
+ {
+ get
+ {
+ return _DeviceType;
+ }
+ set
+ {
+ _DeviceType = value;
+ }
+ }
+
+ public string DeviceClass { get; set; }
+
+ /// <summary>
+ /// Sets or returns the namespace for the <see cref="DeviceType"/> of this device. Optional, but defaults to UPnP schema so should be changed if <see cref="DeviceType"/> is not a UPnP device type.
+ /// </summary>
+ /// <remarks><para>Defaults to the UPnP standard namespace.</para></remarks>
+ /// <seealso cref="DeviceType"/>
+ /// <seealso cref="DeviceVersion"/>
+ /// <seealso cref="FullDeviceType"/>
+ public string DeviceTypeNamespace
+ {
+ get
+ {
+ return _DeviceTypeNamespace;
+ }
+ set
+ {
+ _DeviceTypeNamespace = value;
+ }
+ }
+
+ /// <summary>
+ /// Sets or returns the version of the device type. Optional, defaults to 1.
+ /// </summary>
+ /// <remarks><para>Defaults to a value of 1.</para></remarks>
+ /// <seealso cref="DeviceType"/>
+ /// <seealso cref="DeviceTypeNamespace"/>
+ /// <seealso cref="FullDeviceType"/>
+ public int DeviceVersion
+ {
+ get
+ {
+ return _DeviceVersion;
+ }
+ set
+ {
+ _DeviceVersion = value;
+ }
+ }
+
+ /// <summary>
+ /// Returns the full device type string.
+ /// </summary>
+ /// <remarks>
+ /// <para>The format used is urn:<see cref="DeviceTypeNamespace"/>:device:<see cref="DeviceType"/>:<see cref="DeviceVersion"/></para>
+ /// </remarks>
+ public string FullDeviceType
+ {
+ get
+ {
+ return String.Format("urn:{0}:{3}:{1}:{2}",
+ this.DeviceTypeNamespace ?? String.Empty,
+ this.DeviceType ?? String.Empty,
+ this.DeviceVersion,
+ this.DeviceClass ?? "device");
+ }
+ }
+
+ /// <summary>
+ /// Sets or returns the universally unique identifier for this device (without the uuid: prefix). Required.
+ /// </summary>
+ /// <remarks>
+ /// <para>Must be the same over time for a specific device instance (i.e. must survive reboots).</para>
+ /// <para>For UPnP 1.0 this can be any unique string. For UPnP 1.1 this should be a 128 bit number formatted in a specific way, preferably generated using the time and MAC based algorithm. See section 1.1.4 of http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf for details.</para>
+ /// <para>Technically this library implements UPnP 1.0, so any value is allowed, but we advise using UPnP 1.1 compatible values for good behaviour and forward compatibility with future versions.</para>
+ /// </remarks>
+ public string Uuid { get; set; }
+
+ /// <summary>
+ /// Returns (or sets*) a unique device name for this device. Optional, not recommended to be explicitly set.
+ /// </summary>
+ /// <remarks>
+ /// <para>* In general you should not explicitly set this property. If it is not set (or set to null/empty string) the property will return a UDN value that is correct as per the UPnP specification, based on the other device properties.</para>
+ /// <para>The setter is provided to allow for devices that do not correctly follow the specification (when we discover them), rather than to intentionally deviate from the specification.</para>
+ /// <para>If a value is explicitly set, it is used verbatim, and so any prefix (such as uuid:) must be provided in the value.</para>
+ /// </remarks>
+ public string Udn
+ {
+ get
+ {
+ if (String.IsNullOrEmpty(_Udn) && !String.IsNullOrEmpty(this.Uuid))
+ return "uuid:" + this.Uuid;
+ else
+ return _Udn;
+ }
+ set
+ {
+ _Udn = value;
+ }
+ }
+
+ /// <summary>
+ /// Sets or returns a friendly/display name for this device on the network. Something the user can identify the device/instance by, i.e Lounge Main Light. Required.
+ /// </summary>
+ /// <remarks><para>A short description for the end user. </para></remarks>
+ public string FriendlyName { get; set; }
+
+ /// <summary>
+ /// Sets or returns the name of the manufacturer of this device. Required.
+ /// </summary>
+ public string Manufacturer { get; set; }
+
+ /// <summary>
+ /// Sets or returns a URL to the manufacturers web site. Optional.
+ /// </summary>
+ public Uri ManufacturerUrl { get; set; }
+
+ /// <summary>
+ /// Sets or returns a description of this device model. Recommended.
+ /// </summary>
+ /// <remarks><para>A long description for the end user.</para></remarks>
+ public string ModelDescription { get; set; }
+
+ /// <summary>
+ /// Sets or returns the name of this model. Required.
+ /// </summary>
+ public string ModelName { get; set; }
+
+ /// <summary>
+ /// Sets or returns the number of this model. Recommended.
+ /// </summary>
+ public string ModelNumber { get; set; }
+
+ /// <summary>
+ /// Sets or returns a URL to a web page with details of this device model. Optional.
+ /// </summary>
+ /// <remarks>
+ /// <para>Optional. May be relative to base URL.</para>
+ /// </remarks>
+ public Uri ModelUrl { get; set; }
+
+ /// <summary>
+ /// Sets or returns the serial number for this device. Recommended.
+ /// </summary>
+ public string SerialNumber { get; set; }
+
+ /// <summary>
+ /// Sets or returns the universal product code of the device, if any. Optional.
+ /// </summary>
+ /// <remarks>
+ /// <para>If not blank, must be exactly 12 numeric digits.</para>
+ /// </remarks>
+ public string Upc { get; set; }
+
+ /// <summary>
+ /// Sets or returns the URL to a web page that can be used to configure/manager/use the device. Recommended.
+ /// </summary>
+ /// <remarks>
+ /// <para>May be relative to base URL. </para>
+ /// </remarks>
+ public Uri PresentationUrl { get; set; }
+
+ #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"/>
+ /// <seealso cref="RemoveDevice"/>
+ public IEnumerable<SsdpDevice> Devices
+ {
+ get;
+ 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
+
+ /// <summary>
+ /// Adds a child device to the <see cref="Devices"/> collection.
+ /// </summary>
+ /// <param name="device">The <see cref="SsdpEmbeddedDevice"/> instance to add.</param>
+ /// <remarks>
+ /// <para>If the device is already a member of the <see cref="Devices"/> collection, this method does nothing.</para>
+ /// <para>Also sets the <see cref="SsdpEmbeddedDevice.RootDevice"/> property of the added device and all descendant devices to the relevant <see cref="SsdpRootDevice"/> instance.</para>
+ /// </remarks>
+ /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="device"/> argument is null.</exception>
+ /// <exception cref="System.InvalidOperationException">Thrown if the <paramref name="device"/> is already associated with a different <see cref="SsdpRootDevice"/> instance than used in this tree. Can occur if you try to add the same device instance to more than one tree. Also thrown if you try to add a device to itself.</exception>
+ /// <seealso cref="DeviceAdded"/>
+ public void AddDevice(SsdpEmbeddedDevice device)
+ {
+ if (device == null) throw new ArgumentNullException("device");
+ if (device.RootDevice != null && device.RootDevice != this.ToRootDevice()) throw new InvalidOperationException("This device is already associated with a different root device (has been added as a child in another branch).");
+ if (device == this) throw new InvalidOperationException("Can't add device to itself.");
+
+ bool wasAdded = false;
+ lock (_Devices)
+ {
+ device.RootDevice = this.ToRootDevice();
+ _Devices.Add(device);
+ wasAdded = true;
+ }
+
+ if (wasAdded)
+ OnDeviceAdded(device);
+ }
+
+ /// <summary>
+ /// Removes a child device from the <see cref="Devices"/> collection.
+ /// </summary>
+ /// <param name="device">The <see cref="SsdpEmbeddedDevice"/> instance to remove.</param>
+ /// <remarks>
+ /// <para>If the device is not a member of the <see cref="Devices"/> collection, this method does nothing.</para>
+ /// <para>Also sets the <see cref="SsdpEmbeddedDevice.RootDevice"/> property to null for the removed device and all descendant devices.</para>
+ /// </remarks>
+ /// <exception cref="System.ArgumentNullException">Thrown if the <paramref name="device"/> argument is null.</exception>
+ /// <seealso cref="DeviceRemoved"/>
+ public void RemoveDevice(SsdpEmbeddedDevice device)
+ {
+ if (device == null) throw new ArgumentNullException("device");
+
+ bool wasRemoved = false;
+ lock (_Devices)
+ {
+ wasRemoved = _Devices.Remove(device);
+ if (wasRemoved)
+ {
+ device.RootDevice = null;
+ }
+ }
+
+ if (wasRemoved)
+ OnDeviceRemoved(device);
+ }
+
+ /// <summary>
+ /// Raises the <see cref="DeviceAdded"/> event.
+ /// </summary>
+ /// <param name="device">The <see cref="SsdpEmbeddedDevice"/> instance added to the <see cref="Devices"/> collection.</param>
+ /// <seealso cref="AddDevice"/>
+ /// <seealso cref="DeviceAdded"/>
+ protected virtual void OnDeviceAdded(SsdpEmbeddedDevice device)
+ {
+ var handlers = this.DeviceAdded;
+ if (handlers != null)
+ handlers(this, new DeviceEventArgs(device));
+ }
+
+ /// <summary>
+ /// Raises the <see cref="DeviceRemoved"/> event.
+ /// </summary>
+ /// <param name="device">The <see cref="SsdpEmbeddedDevice"/> instance removed from the <see cref="Devices"/> collection.</param>
+ /// <seealso cref="RemoveDevice"/>
+ /// <see cref="DeviceRemoved"/>
+ protected virtual void OnDeviceRemoved(SsdpEmbeddedDevice device)
+ {
+ var handlers = this.DeviceRemoved;
+ if (handlers != null)
+ 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.Any())
+ {
+ 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.Any())
+ {
+ 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)
+ {
+ 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
new file mode 100644
index 0000000000..0ad710a6b0
--- /dev/null
+++ b/RSSDP/SsdpDeviceExtensions.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+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
new file mode 100644
index 0000000000..4ffda58ff9
--- /dev/null
+++ b/RSSDP/SsdpDeviceIcon.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+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
new file mode 100644
index 0000000000..3ea17237df
--- /dev/null
+++ b/RSSDP/SsdpDeviceLocator.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Threading;
+using Rssdp.Infrastructure;
+
+namespace Rssdp
+{
+ // 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.
+ }
+ }
+} \ No newline at end of file
diff --git a/RSSDP/SsdpDeviceLocatorBase.cs b/RSSDP/SsdpDeviceLocatorBase.cs
new file mode 100644
index 0000000000..b6276e4997
--- /dev/null
+++ b/RSSDP/SsdpDeviceLocatorBase.cs
@@ -0,0 +1,716 @@
+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.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, ISsdpDeviceLocator
+ {
+
+ #region Fields & Constants
+
+ private List<DiscoveredSsdpDevice> _Devices;
+ private ISsdpCommunicationsServer _CommunicationsServer;
+
+ private IList<DiscoveredSsdpDevice> _SearchResults;
+ private object _SearchResultsSynchroniser;
+
+ private ITimer _ExpireCachedDevicesTimer;
+ private ITimerFactory _timerFactory;
+
+ 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;
+
+ _SearchResultsSynchroniser = new object();
+ _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
+
+ /// <summary>
+ /// Performs a search for all devices using the default search timeout.
+ /// </summary>
+ /// <returns>A task whose result is an <see cref="IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns>
+ public Task<IEnumerable<DiscoveredSsdpDevice>> SearchAsync()
+ {
+ return SearchAsync(SsdpConstants.SsdpDiscoverAllSTHeader, DefaultSearchWaitTime);
+ }
+
+ /// <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>
+ /// <returns>A task whose result is an <see cref="IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns>
+ public Task<IEnumerable<DiscoveredSsdpDevice>> SearchAsync(string searchTarget)
+ {
+ return SearchAsync(searchTarget, DefaultSearchWaitTime);
+ }
+
+ /// <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>
+ /// <returns>A task whose result is an <see cref="IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns>
+ public Task<IEnumerable<DiscoveredSsdpDevice>> SearchAsync(TimeSpan searchWaitTime)
+ {
+ return SearchAsync(SsdpConstants.SsdpDiscoverAllSTHeader, searchWaitTime);
+ }
+
+ /// <summary>
+ /// Performs a search for the specified search target (criteria) and 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>A device namespace and type in format of urn:&lt;device namespace&gt;:device:&lt;device type&gt;:&lt;device version&gt; i.e urn:schemas-upnp-org:device:Basic:1</description></item>
+ /// <item><term>Service type</term><description>A service namespace and type in format of urn:&lt;service namespace&gt;:service:&lt;servicetype&gt;:&lt;service version&gt; i.e urn:my-namespace:service:MyCustomService:1</description></item>
+ /// </list>
+ /// </param>
+ /// <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>
+ /// <remarks>
+ /// <para>By design RSSDP does not support 'publishing services' as it is intended for use with non-standard UPnP devices that don't publish UPnP style services. However, it is still possible to use RSSDP to search for devices implemetning these services if you know the service type.</para>
+ /// </remarks>
+ /// <returns>A task whose result is an <see cref="IEnumerable{T}"/> of <see cref="DiscoveredSsdpDevice" /> instances, representing all found devices.</returns>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "expireTask", Justification = "Task is not actually required, but capturing to local variable suppresses compiler warning")]
+ public async Task<IEnumerable<DiscoveredSsdpDevice>> SearchAsync(string searchTarget, TimeSpan searchWaitTime)
+ {
+ 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();
+
+ if (_SearchResults != null) throw new InvalidOperationException("Search already in progress. Only one search at a time is allowed.");
+ _SearchResults = new List<DiscoveredSsdpDevice>();
+
+ // If searchWaitTime == 0 then we are only going to report unexpired cached items, not actually do a search.
+ if (searchWaitTime > TimeSpan.Zero)
+ await BroadcastDiscoverMessage(searchTarget, SearchTimeToMXValue(searchWaitTime)).ConfigureAwait(false);
+
+ lock (_SearchResultsSynchroniser)
+ {
+ foreach (var device in GetUnexpiredDevices().Where(NotificationTypeMatchesFilter))
+ {
+ DeviceFound(device, false);
+ }
+ }
+
+ if (searchWaitTime != TimeSpan.Zero)
+ await Task.Delay(searchWaitTime).ConfigureAwait(false);
+
+ IEnumerable<DiscoveredSsdpDevice> retVal = null;
+
+ try
+ {
+ lock (_SearchResultsSynchroniser)
+ {
+ retVal = _SearchResults;
+ _SearchResults = null;
+ }
+
+ RemoveExpiredDevicesFromCache();
+ }
+ finally
+ {
+ var server = _CommunicationsServer;
+ try
+ {
+ if (server != null) // In case we were disposed while searching.
+ server.StopListeningForResponses();
+ }
+ catch (ObjectDisposedException) { }
+ }
+
+ return retVal;
+ }
+
+ #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>
+ /// <param name="device">A <see cref="DiscoveredSsdpDevice"/> representing the device that is now available.</param>
+ /// <param name="isNewDevice">True if the device was not currently in the cahce before this event was raised.</param>
+ /// <seealso cref="DeviceAvailable"/>
+ protected virtual void OnDeviceAvailable(DiscoveredSsdpDevice device, bool isNewDevice)
+ {
+ if (this.IsDisposed) return;
+
+ var handlers = this.DeviceAvailable;
+ if (handlers != null)
+ handlers(this, new DeviceAvailableEventArgs(device, isNewDevice));
+ }
+
+ /// <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>
+ /// Returns a boolean indicating whether or not a search is currently in progress.
+ /// </summary>
+ /// <remarks>
+ /// <para>Only one search can be performed at a time, per <see cref="SsdpDeviceLocatorBase"/> instance.</para>
+ /// </remarks>
+ public bool IsSearching
+ {
+ get { return _SearchResults != null; }
+ }
+
+ /// <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)
+ {
+ var timer = _ExpireCachedDevicesTimer;
+ if (timer != null)
+ timer.Dispose();
+
+ 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)
+ {
+ 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);
+ }
+
+ private void DeviceFound(DiscoveredSsdpDevice device, bool isNewDevice)
+ {
+ // Don't raise the event if we've already done it for a cached
+ // version of this device, and the cached version isn't
+ // "significantly" different, i.e location and cachelifetime
+ // haven't changed.
+ var raiseEvent = false;
+
+ if (!NotificationTypeMatchesFilter(device)) return;
+
+ lock (_SearchResultsSynchroniser)
+ {
+ if (_SearchResults != null)
+ {
+ var existingDevice = FindExistingDeviceNotification(_SearchResults, device.NotificationType, device.Usn);
+ if (existingDevice == null)
+ {
+ _SearchResults.Add(device);
+ raiseEvent = true;
+ }
+ else
+ {
+ if (existingDevice.DescriptionLocation != device.DescriptionLocation || existingDevice.CacheLifetime != device.CacheLifetime)
+ {
+ _SearchResults.Remove(existingDevice);
+ _SearchResults.Add(device);
+ raiseEvent = true;
+ }
+ }
+ }
+ else
+ raiseEvent = true;
+ }
+
+ if (raiseEvent)
+ OnDeviceAvailable(device, isNewDevice);
+ }
+
+ 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)
+ {
+ 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);
+ }
+
+ private void ProcessSearchResponseMessage(HttpResponseMessage message)
+ {
+ 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);
+ }
+ }
+
+ private void ProcessNotificationMessage(HttpRequestMessage message)
+ {
+ 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);
+ else if (String.Compare(notificationType, SsdpConstants.SsdpByeByeNotification, StringComparison.OrdinalIgnoreCase) == 0)
+ ProcessByeByeNotification(message);
+ }
+
+ private void ProcessAliveNotification(HttpRequestMessage message)
+ {
+ 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);
+
+ ResetExpireCachedDevicesTimer();
+ }
+ }
+
+ 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);
+ }
+
+ ResetExpireCachedDevicesTimer();
+ }
+ }
+
+ private void ResetExpireCachedDevicesTimer()
+ {
+ if (IsDisposed) return;
+
+ if (_ExpireCachedDevicesTimer == null)
+ _ExpireCachedDevicesTimer = _timerFactory.Create(this.ExpireCachedDevices, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
+
+ _ExpireCachedDevicesTimer.Change(60000, System.Threading.Timeout.Infinite);
+ }
+
+ private void ExpireCachedDevices(object state)
+ {
+ RemoveExpiredDevicesFromCache();
+ }
+
+ #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 IEnumerable<DiscoveredSsdpDevice> GetUnexpiredDevices()
+ {
+ lock (_Devices)
+ {
+ return (from device in _Devices where !device.IsExpired() select device).ToArray();
+ }
+ }
+
+ private bool DeviceDied(string deviceUsn, bool expired)
+ {
+ IEnumerable<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.Any())
+ {
+ lock (_SearchResultsSynchroniser)
+ {
+ if (_SearchResults != null)
+ {
+ var resultsToRemove = (from result in _SearchResults where result.Usn == deviceUsn select result).ToArray();
+ foreach (var result in resultsToRemove)
+ {
+ if (this.IsDisposed) return true;
+
+ _SearchResults.Remove(result);
+ }
+ }
+ }
+
+ 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)
+ {
+ return (from d in devices where d.NotificationType == notificationType && d.Usn == usn select d).FirstOrDefault();
+ }
+
+ private static IEnumerable<DiscoveredSsdpDevice> FindExistingDeviceNotifications(IList<DiscoveredSsdpDevice> devices, string usn)
+ {
+ return (from d in devices where d.Usn == usn select d).ToArray();
+ }
+
+ #endregion
+
+ #region Event Handlers
+
+ private void CommsServer_ResponseReceived(object sender, ResponseReceivedEventArgs e)
+ {
+ ProcessSearchResponseMessage(e.Message);
+ }
+
+ private void CommsServer_RequestReceived(object sender, RequestReceivedEventArgs e)
+ {
+ ProcessNotificationMessage(e.Message);
+ }
+
+ #endregion
+
+ }
+} \ No newline at end of file
diff --git a/RSSDP/SsdpDeviceProperties.cs b/RSSDP/SsdpDeviceProperties.cs
new file mode 100644
index 0000000000..850dfb0ba8
--- /dev/null
+++ b/RSSDP/SsdpDeviceProperties.cs
@@ -0,0 +1,206 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+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
new file mode 100644
index 0000000000..3a8dd2ec7f
--- /dev/null
+++ b/RSSDP/SsdpDeviceProperty.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+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
new file mode 100644
index 0000000000..1c17c78372
--- /dev/null
+++ b/RSSDP/SsdpDevicePublisher.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Threading;
+using Rssdp.Infrastructure;
+
+namespace Rssdp
+{
+ /// <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
+
+ }
+} \ No newline at end of file
diff --git a/RSSDP/SsdpDevicePublisherBase.cs b/RSSDP/SsdpDevicePublisherBase.cs
new file mode 100644
index 0000000000..8ab35d6616
--- /dev/null
+++ b/RSSDP/SsdpDevicePublisherBase.cs
@@ -0,0 +1,708 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Net.Http;
+using System.Text;
+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);
+ }
+ }
+
+ /// <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).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)
+ {
+ 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);
+ }
+ }
+ 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)
+ {
+ bool isRootDevice = (device as SsdpRootDevice) != null;
+ if (isRootDevice)
+ {
+ SendSearchResponse(SsdpConstants.UpnpDeviceTypeRootDevice, device, GetUsn(device.Udn, SsdpConstants.UpnpDeviceTypeRootDevice), endPoint, receivedOnlocalIpAddress);
+ if (this.SupportPnpRootDevice)
+ SendSearchResponse(SsdpConstants.PnpDeviceTypeRootDevice, device, GetUsn(device.Udn, SsdpConstants.PnpDeviceTypeRootDevice), endPoint, receivedOnlocalIpAddress);
+ }
+
+ SendSearchResponse(device.Udn, device, device.Udn, endPoint, receivedOnlocalIpAddress);
+
+ SendSearchResponse(device.FullDeviceType, device, GetUsn(device.Udn, device.FullDeviceType), endPoint, receivedOnlocalIpAddress);
+ }
+
+ 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)
+ {
+ 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).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);
+ }
+
+ //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)
+ {
+ if (isRoot)
+ {
+ SendAliveNotification(device, SsdpConstants.UpnpDeviceTypeRootDevice, GetUsn(device.Udn, SsdpConstants.UpnpDeviceTypeRootDevice));
+ if (this.SupportPnpRootDevice)
+ SendAliveNotification(device, SsdpConstants.PnpDeviceTypeRootDevice, GetUsn(device.Udn, SsdpConstants.PnpDeviceTypeRootDevice));
+ }
+
+ SendAliveNotification(device, device.Udn, device.Udn);
+ SendAliveNotification(device, device.FullDeviceType, GetUsn(device.Udn, device.FullDeviceType));
+
+ foreach (var childDevice in device.Devices)
+ {
+ SendAliveNotifications(childDevice, false);
+ }
+ }
+
+ private void SendAliveNotification(SsdpDevice device, string notificationType, string uniqueServiceName)
+ {
+ 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);
+
+ WriteTrace(String.Format("Sent alive notification"), device);
+ }
+
+ #endregion
+
+ #region ByeBye
+
+ private async Task SendByeByeNotifications(SsdpDevice device, bool isRoot)
+ {
+ if (isRoot)
+ {
+ await SendByeByeNotification(device, SsdpConstants.UpnpDeviceTypeRootDevice, GetUsn(device.Udn, SsdpConstants.UpnpDeviceTypeRootDevice)).ConfigureAwait(false);
+ if (this.SupportPnpRootDevice)
+ await SendByeByeNotification(device, "pnp:rootdevice", GetUsn(device.Udn, "pnp:rootdevice")).ConfigureAwait(false); ;
+ }
+
+ await SendByeByeNotification(device, device.Udn, device.Udn).ConfigureAwait(false); ;
+ await SendByeByeNotification(device, String.Format("urn:{0}", device.FullDeviceType), GetUsn(device.Udn, device.FullDeviceType)).ConfigureAwait(false); ;
+
+ foreach (var childDevice in device.Devices)
+ {
+ await SendByeByeNotifications(childDevice, false).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)
+ {
+ 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);
+
+ //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);
+ ConnectToDeviceEvents(e.Device);
+ }
+
+ private void device_DeviceRemoved(object sender, DeviceEventArgs e)
+ {
+ var task = SendByeByeNotifications(e.Device, false);
+ 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);
+ }
+ }
+
+ #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
new file mode 100644
index 0000000000..c03106b2dd
--- /dev/null
+++ b/RSSDP/SsdpEmbeddedDevice.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Rssdp
+{
+ /// <summary>
+ /// Represents a device that is a descendant of a <see cref="SsdpRootDevice"/> instance.
+ /// </summary>
+ public class SsdpEmbeddedDevice : SsdpDevice
+ {
+
+ #region Fields
+
+ private SsdpRootDevice _RootDevice;
+
+ #endregion
+
+ #region Constructors
+
+ /// <summary>
+ /// Default constructor.
+ /// </summary>
+ public SsdpEmbeddedDevice()
+ {
+ }
+
+ /// <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
+
+ /// <summary>
+ /// Returns the <see cref="SsdpRootDevice"/> that is this device's first ancestor. If this device is itself an <see cref="SsdpRootDevice"/>, then returns a reference to itself.
+ /// </summary>
+ public SsdpRootDevice RootDevice
+ {
+ get
+ {
+ return _RootDevice;
+ }
+ internal set
+ {
+ _RootDevice = value;
+ lock (this.Devices)
+ {
+ foreach (var embeddedDevice in this.Devices)
+ {
+ ((SsdpEmbeddedDevice)embeddedDevice).RootDevice = _RootDevice;
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ }
+} \ No newline at end of file
diff --git a/RSSDP/SsdpHelper.cs b/RSSDP/SsdpHelper.cs
new file mode 100644
index 0000000000..2eacf3c11b
--- /dev/null
+++ b/RSSDP/SsdpHelper.cs
@@ -0,0 +1,88 @@
+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
new file mode 100644
index 0000000000..faf851bcbe
--- /dev/null
+++ b/RSSDP/SsdpRootDevice.cs
@@ -0,0 +1,176 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Xml;
+using Rssdp.Infrastructure;
+
+namespace Rssdp
+{
+ /// <summary>
+ /// Represents a 'root' device, a device that has no parent. Used for publishing devices and for the root device in a tree of discovered devices.
+ /// </summary>
+ /// <remarks>
+ /// <para>Child (embedded) devices are represented by the <see cref="SsdpDevice"/> in the <see cref="SsdpDevice.Devices"/> property.</para>
+ /// <para>Root devices contain some information that applies to the whole device tree and is therefore not present on child devices, such as <see cref="CacheLifetime"/> and <see cref="Location"/>.</para>
+ /// </remarks>
+ public class SsdpRootDevice : SsdpDevice
+ {
+
+ #region Fields
+
+ private Uri _UrlBase;
+
+ #endregion
+
+ #region Constructors
+
+ /// <summary>
+ /// Default constructor.
+ /// </summary>
+ public SsdpRootDevice() : base()
+ {
+ }
+
+ /// <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
+
+ /// <summary>
+ /// Specifies how long clients can cache this device's details for. Optional but defaults to <see cref="TimeSpan.Zero"/> which means no-caching. Recommended value is half an hour.
+ /// </summary>
+ /// <remarks>
+ /// <para>Specifiy <see cref="TimeSpan.Zero"/> to indicate no caching allowed.</para>
+ /// <para>Also used to specify how often to rebroadcast alive notifications.</para>
+ /// <para>The UPnP/SSDP specifications indicate this should not be less than 1800 seconds (half an hour), but this is not enforced by this library.</para>
+ /// </remarks>
+ public TimeSpan CacheLifetime
+ {
+ get; set;
+ }
+
+ /// <summary>
+ /// Gets or sets the URL used to retrieve the description document for this device/tree. Required.
+ /// </summary>
+ public Uri Location { get; set; }
+
+
+ /// <summary>
+ /// The base URL to use for all relative url's provided in other propertise (and those of child devices). Optional.
+ /// </summary>
+ /// <remarks>
+ /// <para>Defines the base URL. Used to construct fully-qualified URLs. All relative URLs that appear elsewhere in the description are combined with this base URL. If URLBase is empty or not given, the base URL is the URL from which the device description was retrieved (which is the preferred implementation; use of URLBase is no longer recommended). Specified by UPnP vendor. Single URL.</para>
+ /// </remarks>
+ public Uri UrlBase
+ {
+ get
+ {
+ return _UrlBase ?? this.Location;
+ }
+
+ set
+ {
+ _UrlBase = value;
+ }
+ }
+
+ #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
new file mode 100644
index 0000000000..f802b7639e
--- /dev/null
+++ b/RSSDP/UPnP10DeviceValidator.cs
@@ -0,0 +1,194 @@
+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 IEnumerable<string> GetValidationErrors(SsdpRootDevice device)
+ {
+ if (device == null) throw new ArgumentNullException("device");
+
+ var retVal = GetValidationErrors((SsdpDevice)device) as IList<string>;
+
+ 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 IEnumerable<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.Any())
+ 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.Any()) 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/RSSDP/project.json b/RSSDP/project.json
new file mode 100644
index 0000000000..fbbe9eaf32
--- /dev/null
+++ b/RSSDP/project.json
@@ -0,0 +1,17 @@
+{
+ "frameworks":{
+ "netstandard1.6":{
+ "dependencies":{
+ "NETStandard.Library":"1.6.0",
+ }
+ },
+ ".NETPortable,Version=v4.5,Profile=Profile7":{
+ "buildOptions": {
+ "define": [ ]
+ },
+ "frameworkAssemblies":{
+
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ServiceStack/FilterAttributeCache.cs b/ServiceStack/FilterAttributeCache.cs
new file mode 100644
index 0000000000..378433addd
--- /dev/null
+++ b/ServiceStack/FilterAttributeCache.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Threading;
+using ServiceStack;
+
+namespace ServiceStack.Support.WebHost
+{
+ public static class FilterAttributeCache
+ {
+ public static MediaBrowser.Model.Services.IHasRequestFilter[] GetRequestFilterAttributes(Type requestDtoType)
+ {
+ var attributes = requestDtoType.AllAttributes().OfType<MediaBrowser.Model.Services.IHasRequestFilter>().ToList();
+
+ var serviceType = ServiceStackHost.Instance.Metadata.GetServiceTypeByRequest(requestDtoType);
+ if (serviceType != null)
+ {
+ attributes.AddRange(serviceType.AllAttributes().OfType<MediaBrowser.Model.Services.IHasRequestFilter>());
+ }
+
+ attributes.Sort((x,y) => x.Priority - y.Priority);
+
+ return attributes.ToArray();
+ }
+ }
+}
diff --git a/ServiceStack/Host/ActionContext.cs b/ServiceStack/Host/ActionContext.cs
new file mode 100644
index 0000000000..9f165cff15
--- /dev/null
+++ b/ServiceStack/Host/ActionContext.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace ServiceStack.Host
+{
+ /// <summary>
+ /// Context to capture IService action
+ /// </summary>
+ public class ActionContext
+ {
+ public const string AnyAction = "ANY";
+
+ public string Id { get; set; }
+
+ public ActionInvokerFn ServiceAction { get; set; }
+ public MediaBrowser.Model.Services.IHasRequestFilter[] RequestFilters { get; set; }
+
+ public static string Key(Type serviceType, string method, string requestDtoName)
+ {
+ return serviceType.FullName + " " + method.ToUpper() + " " + requestDtoName;
+ }
+
+ public static string AnyKey(Type serviceType, string requestDtoName)
+ {
+ return Key(serviceType, AnyAction, requestDtoName);
+ }
+ }
+} \ No newline at end of file
diff --git a/ServiceStack/Host/ContentTypes.cs b/ServiceStack/Host/ContentTypes.cs
new file mode 100644
index 0000000000..f7734a36b6
--- /dev/null
+++ b/ServiceStack/Host/ContentTypes.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using MediaBrowser.Model.Services;
+
+namespace ServiceStack.Host
+{
+ public class ContentTypes
+ {
+ public static ContentTypes Instance = new ContentTypes();
+
+ public void SerializeToStream(IRequest req, object response, Stream responseStream)
+ {
+ var contentType = req.ResponseContentType;
+ var serializer = GetStreamSerializer(contentType);
+
+ serializer(response, responseStream);
+ }
+
+ public static Action<object, Stream> GetStreamSerializer(string contentType)
+ {
+ switch (GetRealContentType(contentType))
+ {
+ case "application/xml":
+ case "text/xml":
+ case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml
+ return (o, s) => ServiceStackHost.Instance.SerializeToXml(o, s);
+
+ case "application/json":
+ case "text/json":
+ return (o, s) => ServiceStackHost.Instance.SerializeToJson(o, s);
+ }
+
+ return null;
+ }
+
+ public Func<Type, Stream, object> GetStreamDeserializer(string contentType)
+ {
+ switch (GetRealContentType(contentType))
+ {
+ case "application/xml":
+ case "text/xml":
+ case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml
+ return ServiceStackHost.Instance.DeserializeXml;
+
+ case "application/json":
+ case "text/json":
+ return ServiceStackHost.Instance.DeserializeJson;
+ }
+
+ return null;
+ }
+
+ private static string GetRealContentType(string contentType)
+ {
+ return contentType == null
+ ? null
+ : contentType.Split(';')[0].ToLower().Trim();
+ }
+
+ }
+} \ No newline at end of file
diff --git a/ServiceStack/Host/RestHandler.cs b/ServiceStack/Host/RestHandler.cs
new file mode 100644
index 0000000000..1eae6be389
--- /dev/null
+++ b/ServiceStack/Host/RestHandler.cs
@@ -0,0 +1,176 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
+
+namespace ServiceStack.Host
+{
+ public class RestHandler
+ {
+ public string RequestName { get; set; }
+
+ public async Task<object> HandleResponseAsync(object response)
+ {
+ var taskResponse = response as Task;
+
+ if (taskResponse == null)
+ {
+ return response;
+ }
+
+ await taskResponse.ConfigureAwait(false);
+
+ var taskResult = ServiceStackHost.Instance.GetTaskResult(taskResponse, RequestName);
+
+ var subTask = taskResult as Task;
+ if (subTask != null)
+ {
+ taskResult = ServiceStackHost.Instance.GetTaskResult(subTask, RequestName);
+ }
+
+ return taskResult;
+ }
+
+ protected static object CreateContentTypeRequest(IRequest httpReq, Type requestType, string contentType)
+ {
+ if (!string.IsNullOrEmpty(contentType) && httpReq.ContentLength > 0)
+ {
+ var deserializer = ContentTypes.Instance.GetStreamDeserializer(contentType);
+ if (deserializer != null)
+ {
+ return deserializer(requestType, httpReq.InputStream);
+ }
+ }
+ return ServiceStackHost.Instance.CreateInstance(requestType); //Return an empty DTO, even for empty request bodies
+ }
+
+ protected static object GetCustomRequestFromBinder(IRequest httpReq, Type requestType)
+ {
+ Func<IRequest, object> requestFactoryFn;
+ ServiceStackHost.Instance.ServiceController.RequestTypeFactoryMap.TryGetValue(
+ requestType, out requestFactoryFn);
+
+ return requestFactoryFn != null ? requestFactoryFn(httpReq) : null;
+ }
+
+ public static RestPath FindMatchingRestPath(string httpMethod, string pathInfo, out string contentType)
+ {
+ pathInfo = GetSanitizedPathInfo(pathInfo, out contentType);
+
+ return ServiceStackHost.Instance.ServiceController.GetRestPathForRequest(httpMethod, pathInfo);
+ }
+
+ public static string GetSanitizedPathInfo(string pathInfo, out string contentType)
+ {
+ contentType = null;
+ var pos = pathInfo.LastIndexOf('.');
+ if (pos >= 0)
+ {
+ var format = pathInfo.Substring(pos + 1);
+ contentType = GetFormatContentType(format);
+ if (contentType != null)
+ {
+ pathInfo = pathInfo.Substring(0, pos);
+ }
+ }
+ return pathInfo;
+ }
+
+ private static string GetFormatContentType(string format)
+ {
+ //built-in formats
+ if (format == "json")
+ return "application/json";
+ if (format == "xml")
+ return "application/xml";
+
+ return null;
+ }
+
+ public RestPath GetRestPath(string httpMethod, string pathInfo)
+ {
+ if (this.RestPath == null)
+ {
+ string contentType;
+ this.RestPath = FindMatchingRestPath(httpMethod, pathInfo, out contentType);
+
+ if (contentType != null)
+ ResponseContentType = contentType;
+ }
+ return this.RestPath;
+ }
+
+ public RestPath RestPath { get; set; }
+
+ // Set from SSHHF.GetHandlerForPathInfo()
+ public string ResponseContentType { get; set; }
+
+ public async Task ProcessRequestAsync(IRequest httpReq, IResponse httpRes, string operationName)
+ {
+ var appHost = ServiceStackHost.Instance;
+
+ var restPath = GetRestPath(httpReq.Verb, httpReq.PathInfo);
+ if (restPath == null)
+ {
+ throw new NotSupportedException("No RestPath found for: " + httpReq.Verb + " " + httpReq.PathInfo);
+ }
+ httpReq.SetRoute(restPath);
+
+ if (ResponseContentType != null)
+ httpReq.ResponseContentType = ResponseContentType;
+
+ var request = httpReq.Dto = CreateRequest(httpReq, restPath);
+
+ appHost.ApplyRequestFilters(httpReq, httpRes, request);
+
+ var rawResponse = await ServiceStackHost.Instance.ServiceController.Execute(request, httpReq).ConfigureAwait(false);
+
+ var response = await HandleResponseAsync(rawResponse).ConfigureAwait(false);
+
+ appHost.ApplyResponseFilters(httpReq, httpRes, response);
+
+ await httpRes.WriteToResponse(httpReq, response).ConfigureAwait(false);
+ }
+
+ public static object CreateRequest(IRequest httpReq, RestPath restPath)
+ {
+ var dtoFromBinder = GetCustomRequestFromBinder(httpReq, restPath.RequestType);
+ if (dtoFromBinder != null)
+ return dtoFromBinder;
+
+ var requestParams = httpReq.GetFlattenedRequestParams();
+ return CreateRequest(httpReq, restPath, requestParams);
+ }
+
+ public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams)
+ {
+ var requestDto = CreateContentTypeRequest(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;
+ var pathInfo = !restPath.IsWildCardPath
+ ? GetSanitizedPathInfo(httpReq.PathInfo, out contentType)
+ : httpReq.PathInfo;
+
+ return restPath.CreateRequest(pathInfo, requestParams, requestDto);
+ }
+
+ /// <summary>
+ /// Used in Unit tests
+ /// </summary>
+ /// <returns></returns>
+ public object CreateRequest(IRequest httpReq, string operationName)
+ {
+ if (this.RestPath == null)
+ throw new ArgumentNullException("No RestPath found");
+
+ return CreateRequest(httpReq, this.RestPath);
+ }
+ }
+
+}
diff --git a/ServiceStack/Host/RestPath.cs b/ServiceStack/Host/RestPath.cs
new file mode 100644
index 0000000000..5bbd03a21e
--- /dev/null
+++ b/ServiceStack/Host/RestPath.cs
@@ -0,0 +1,448 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using ServiceStack.Serialization;
+
+namespace ServiceStack.Host
+{
+ public class RestPath
+ {
+ private const string WildCard = "*";
+ private const char WildCardChar = '*';
+ private const string PathSeperator = "/";
+ private const char PathSeperatorChar = '/';
+ private const char ComponentSeperator = '.';
+ private const string VariablePrefix = "{";
+
+ readonly bool[] componentsWithSeparators;
+
+ private readonly string restPath;
+ private readonly string allowedVerbs;
+ private readonly bool allowsAllVerbs;
+ public bool IsWildCardPath { get; private set; }
+
+ private readonly string[] literalsToMatch;
+
+ private readonly string[] variablesNames;
+
+ private readonly bool[] isWildcard;
+ private readonly int wildcardCount = 0;
+
+ public int VariableArgsCount { get; set; }
+
+ /// <summary>
+ /// The number of segments separated by '/' determinable by path.Split('/').Length
+ /// e.g. /path/to/here.ext == 3
+ /// </summary>
+ public int PathComponentsCount { get; set; }
+
+ /// <summary>
+ /// The total number of segments after subparts have been exploded ('.')
+ /// e.g. /path/to/here.ext == 4
+ /// </summary>
+ public int TotalComponentsCount { get; set; }
+
+ public string[] Verbs
+ {
+ get
+ {
+ return allowsAllVerbs
+ ? new[] { ActionContext.AnyAction }
+ : AllowedVerbs.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
+ }
+ }
+
+ public Type RequestType { get; private set; }
+
+ public string Path { get { return this.restPath; } }
+
+ public string Summary { get; private set; }
+
+ public string Notes { get; private set; }
+
+ public bool AllowsAllVerbs { get { return this.allowsAllVerbs; } }
+
+ public string AllowedVerbs { get { return this.allowedVerbs; } }
+
+ public int Priority { get; set; } //passed back to RouteAttribute
+
+ public static string[] GetPathPartsForMatching(string pathInfo)
+ {
+ var parts = pathInfo.ToLower().Split(PathSeperatorChar)
+ .Where(x => !string.IsNullOrEmpty(x)).ToArray();
+ return parts;
+ }
+
+ public static IEnumerable<string> GetFirstMatchHashKeys(string[] pathPartsForMatching)
+ {
+ var hashPrefix = pathPartsForMatching.Length + PathSeperator;
+ return GetPotentialMatchesWithPrefix(hashPrefix, pathPartsForMatching);
+ }
+
+ public static IEnumerable<string> GetFirstMatchWildCardHashKeys(string[] pathPartsForMatching)
+ {
+ const string hashPrefix = WildCard + PathSeperator;
+ return GetPotentialMatchesWithPrefix(hashPrefix, pathPartsForMatching);
+ }
+
+ private static IEnumerable<string> GetPotentialMatchesWithPrefix(string hashPrefix, string[] pathPartsForMatching)
+ {
+ foreach (var part in pathPartsForMatching)
+ {
+ yield return hashPrefix + part;
+ var subParts = part.Split(ComponentSeperator);
+ if (subParts.Length == 1) continue;
+
+ foreach (var subPart in subParts)
+ {
+ yield return hashPrefix + subPart;
+ }
+ }
+ }
+
+ public RestPath(Type requestType, string path, string verbs, string summary = null, string notes = null)
+ {
+ this.RequestType = requestType;
+ this.Summary = summary;
+ this.Notes = notes;
+ this.restPath = path;
+
+ this.allowsAllVerbs = verbs == null || string.Equals(verbs, WildCard, StringComparison.OrdinalIgnoreCase);
+ if (!this.allowsAllVerbs)
+ {
+ this.allowedVerbs = verbs.ToUpper();
+ }
+
+ var componentsList = new List<string>();
+
+ //We only split on '.' if the restPath has them. Allows for /{action}.{type}
+ var hasSeparators = new List<bool>();
+ foreach (var component in this.restPath.Split(PathSeperatorChar))
+ {
+ if (string.IsNullOrEmpty(component)) continue;
+
+ if (StringContains(component, VariablePrefix)
+ && component.IndexOf(ComponentSeperator) != -1)
+ {
+ hasSeparators.Add(true);
+ componentsList.AddRange(component.Split(ComponentSeperator));
+ }
+ else
+ {
+ hasSeparators.Add(false);
+ componentsList.Add(component);
+ }
+ }
+
+ var components = componentsList.ToArray();
+ this.TotalComponentsCount = components.Length;
+
+ this.literalsToMatch = new string[this.TotalComponentsCount];
+ this.variablesNames = new string[this.TotalComponentsCount];
+ this.isWildcard = new bool[this.TotalComponentsCount];
+ this.componentsWithSeparators = hasSeparators.ToArray();
+ this.PathComponentsCount = this.componentsWithSeparators.Length;
+ string firstLiteralMatch = null;
+
+ var sbHashKey = new StringBuilder();
+ for (var i = 0; i < components.Length; i++)
+ {
+ var component = components[i];
+
+ if (component.StartsWith(VariablePrefix))
+ {
+ var variableName = component.Substring(1, component.Length - 2);
+ if (variableName[variableName.Length - 1] == WildCardChar)
+ {
+ this.isWildcard[i] = true;
+ variableName = variableName.Substring(0, variableName.Length - 1);
+ }
+ this.variablesNames[i] = variableName;
+ this.VariableArgsCount++;
+ }
+ else
+ {
+ this.literalsToMatch[i] = component.ToLower();
+ sbHashKey.Append(i + PathSeperatorChar.ToString() + this.literalsToMatch);
+
+ if (firstLiteralMatch == null)
+ {
+ firstLiteralMatch = this.literalsToMatch[i];
+ }
+ }
+ }
+
+ for (var i = 0; i < components.Length - 1; i++)
+ {
+ if (!this.isWildcard[i]) continue;
+ if (this.literalsToMatch[i + 1] == null)
+ {
+ throw new ArgumentException(
+ "A wildcard path component must be at the end of the path or followed by a literal path component.");
+ }
+ }
+
+ this.wildcardCount = this.isWildcard.Count(x => x);
+ this.IsWildCardPath = this.wildcardCount > 0;
+
+ this.FirstMatchHashKey = !this.IsWildCardPath
+ ? this.PathComponentsCount + PathSeperator + firstLiteralMatch
+ : WildCardChar + PathSeperator + firstLiteralMatch;
+
+ this.IsValid = sbHashKey.Length > 0;
+ this.UniqueMatchHashKey = sbHashKey.ToString();
+
+ this.typeDeserializer = new StringMapTypeDeserializer(this.RequestType);
+ RegisterCaseInsenstivePropertyNameMappings();
+ }
+
+ private void RegisterCaseInsenstivePropertyNameMappings()
+ {
+ foreach (var propertyInfo in RequestType.GetSerializableProperties())
+ {
+ var propertyName = propertyInfo.Name;
+ propertyNamesMap.Add(propertyName.ToLower(), propertyName);
+ }
+ }
+
+ public bool IsValid { get; set; }
+
+ /// <summary>
+ /// Provide for quick lookups based on hashes that can be determined from a request url
+ /// </summary>
+ public string FirstMatchHashKey { get; private set; }
+
+ public string UniqueMatchHashKey { get; private set; }
+
+ private readonly StringMapTypeDeserializer typeDeserializer;
+
+ private readonly Dictionary<string, string> propertyNamesMap = new Dictionary<string, string>();
+
+ public static Func<RestPath, string, string[], int> CalculateMatchScore { get; set; }
+
+ public int MatchScore(string httpMethod, string[] withPathInfoParts)
+ {
+ if (CalculateMatchScore != null)
+ return CalculateMatchScore(this, httpMethod, withPathInfoParts);
+
+ int wildcardMatchCount;
+ var isMatch = IsMatch(httpMethod, withPathInfoParts, out wildcardMatchCount);
+ if (!isMatch) return -1;
+
+ var score = 0;
+
+ //Routes with least wildcard matches get the highest score
+ score += Math.Max((100 - wildcardMatchCount), 1) * 1000;
+
+ //Routes with less variable (and more literal) matches
+ score += Math.Max((10 - VariableArgsCount), 1) * 100;
+
+ //Exact verb match is better than ANY
+ var exactVerb = string.Equals(httpMethod, AllowedVerbs, StringComparison.OrdinalIgnoreCase);
+ score += exactVerb ? 10 : 1;
+
+ return score;
+ }
+
+ private bool StringContains(string str1, string str2)
+ {
+ return str1.IndexOf(str2, StringComparison.OrdinalIgnoreCase) != -1;
+ }
+
+ /// <summary>
+ /// For performance withPathInfoParts should already be a lower case string
+ /// to minimize redundant matching operations.
+ /// </summary>
+ /// <param name="httpMethod"></param>
+ /// <param name="withPathInfoParts"></param>
+ /// <param name="wildcardMatchCount"></param>
+ /// <returns></returns>
+ public bool IsMatch(string httpMethod, string[] withPathInfoParts, out int wildcardMatchCount)
+ {
+ wildcardMatchCount = 0;
+
+ if (withPathInfoParts.Length != this.PathComponentsCount && !this.IsWildCardPath) return false;
+ if (!this.allowsAllVerbs && !StringContains(this.allowedVerbs, httpMethod)) return false;
+
+ if (!ExplodeComponents(ref withPathInfoParts)) return false;
+ if (this.TotalComponentsCount != withPathInfoParts.Length && !this.IsWildCardPath) return false;
+
+ int pathIx = 0;
+ for (var i = 0; i < this.TotalComponentsCount; i++)
+ {
+ if (this.isWildcard[i])
+ {
+ if (i < this.TotalComponentsCount - 1)
+ {
+ // Continue to consume up until a match with the next literal
+ while (pathIx < withPathInfoParts.Length && withPathInfoParts[pathIx] != this.literalsToMatch[i + 1])
+ {
+ pathIx++;
+ wildcardMatchCount++;
+ }
+
+ // Ensure there are still enough parts left to match the remainder
+ if ((withPathInfoParts.Length - pathIx) < (this.TotalComponentsCount - i - 1))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ // A wildcard at the end matches the remainder of path
+ wildcardMatchCount += withPathInfoParts.Length - pathIx;
+ pathIx = withPathInfoParts.Length;
+ }
+ }
+ else
+ {
+ var literalToMatch = this.literalsToMatch[i];
+ if (literalToMatch == null)
+ {
+ // Matching an ordinary (non-wildcard) variable consumes a single part
+ pathIx++;
+ continue;
+ }
+
+ if (withPathInfoParts.Length <= pathIx || withPathInfoParts[pathIx] != literalToMatch) return false;
+ pathIx++;
+ }
+ }
+
+ return pathIx == withPathInfoParts.Length;
+ }
+
+ private bool ExplodeComponents(ref string[] withPathInfoParts)
+ {
+ var totalComponents = new List<string>();
+ for (var i = 0; i < withPathInfoParts.Length; i++)
+ {
+ var component = withPathInfoParts[i];
+ if (string.IsNullOrEmpty(component)) continue;
+
+ if (this.PathComponentsCount != this.TotalComponentsCount
+ && this.componentsWithSeparators[i])
+ {
+ var subComponents = component.Split(ComponentSeperator);
+ if (subComponents.Length < 2) return false;
+ totalComponents.AddRange(subComponents);
+ }
+ else
+ {
+ totalComponents.Add(component);
+ }
+ }
+
+ withPathInfoParts = totalComponents.ToArray();
+ return true;
+ }
+
+ public object CreateRequest(string pathInfo, Dictionary<string, string> queryStringAndFormData, object fromInstance)
+ {
+ var requestComponents = pathInfo.Split(PathSeperatorChar)
+ .Where(x => !string.IsNullOrEmpty(x)).ToArray();
+
+ ExplodeComponents(ref requestComponents);
+
+ if (requestComponents.Length != this.TotalComponentsCount)
+ {
+ var isValidWildCardPath = this.IsWildCardPath
+ && requestComponents.Length >= this.TotalComponentsCount - this.wildcardCount;
+
+ if (!isValidWildCardPath)
+ throw new ArgumentException(string.Format(
+ "Path Mismatch: Request Path '{0}' has invalid number of components compared to: '{1}'",
+ pathInfo, this.restPath));
+ }
+
+ var requestKeyValuesMap = new Dictionary<string, string>();
+ var pathIx = 0;
+ for (var i = 0; i < this.TotalComponentsCount; i++)
+ {
+ var variableName = this.variablesNames[i];
+ if (variableName == null)
+ {
+ pathIx++;
+ continue;
+ }
+
+ string propertyNameOnRequest;
+ if (!this.propertyNamesMap.TryGetValue(variableName.ToLower(), out propertyNameOnRequest))
+ {
+ if (string.Equals("ignore", variableName, StringComparison.OrdinalIgnoreCase))
+ {
+ pathIx++;
+ continue;
+ }
+
+ throw new ArgumentException("Could not find property "
+ + variableName + " on " + RequestType.GetOperationName());
+ }
+
+ var value = requestComponents.Length > pathIx ? requestComponents[pathIx] : null; //wildcard has arg mismatch
+ if (value != null && this.isWildcard[i])
+ {
+ if (i == this.TotalComponentsCount - 1)
+ {
+ // Wildcard at end of path definition consumes all the rest
+ var sb = new StringBuilder();
+ sb.Append(value);
+ for (var j = pathIx + 1; j < requestComponents.Length; j++)
+ {
+ sb.Append(PathSeperatorChar + requestComponents[j]);
+ }
+ value = sb.ToString();
+ }
+ else
+ {
+ // Wildcard in middle of path definition consumes up until it
+ // hits a match for the next element in the definition (which must be a literal)
+ // It may consume 0 or more path parts
+ var stopLiteral = i == this.TotalComponentsCount - 1 ? null : this.literalsToMatch[i + 1];
+ if (!string.Equals(requestComponents[pathIx], stopLiteral, StringComparison.OrdinalIgnoreCase))
+ {
+ var sb = new StringBuilder();
+ sb.Append(value);
+ pathIx++;
+ while (!string.Equals(requestComponents[pathIx], stopLiteral, StringComparison.OrdinalIgnoreCase))
+ {
+ sb.Append(PathSeperatorChar + requestComponents[pathIx++]);
+ }
+ value = sb.ToString();
+ }
+ else
+ {
+ value = null;
+ }
+ }
+ }
+ else
+ {
+ // Variable consumes single path item
+ pathIx++;
+ }
+
+ requestKeyValuesMap[propertyNameOnRequest] = value;
+ }
+
+ if (queryStringAndFormData != null)
+ {
+ //Query String and form data can override variable path matches
+ //path variables < query string < form data
+ foreach (var name in queryStringAndFormData)
+ {
+ requestKeyValuesMap[name.Key] = name.Value;
+ }
+ }
+
+ return this.typeDeserializer.PopulateFromMap(fromInstance, requestKeyValuesMap);
+ }
+
+ public override int GetHashCode()
+ {
+ return UniqueMatchHashKey.GetHashCode();
+ }
+ }
+} \ No newline at end of file
diff --git a/ServiceStack/Host/ServiceController.cs b/ServiceStack/Host/ServiceController.cs
new file mode 100644
index 0000000000..378c21d5d9
--- /dev/null
+++ b/ServiceStack/Host/ServiceController.cs
@@ -0,0 +1,217 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
+
+namespace ServiceStack.Host
+{
+ public delegate Task<object> InstanceExecFn(IRequest requestContext, object intance, object request);
+ public delegate object ActionInvokerFn(object intance, object request);
+ public delegate void VoidActionInvokerFn(object intance, object request);
+
+ public class ServiceController
+ {
+ private readonly Func<IEnumerable<Type>> _resolveServicesFn;
+
+ public ServiceController(Func<IEnumerable<Type>> resolveServicesFn)
+ {
+ _resolveServicesFn = resolveServicesFn;
+ this.RequestTypeFactoryMap = new Dictionary<Type, Func<IRequest, object>>();
+ }
+
+ public Dictionary<Type, Func<IRequest, object>> RequestTypeFactoryMap { get; set; }
+
+ public void Init()
+ {
+ foreach (var serviceType in _resolveServicesFn())
+ {
+ RegisterService(serviceType);
+ }
+ }
+
+ private Type[] GetGenericArguments(Type type)
+ {
+ return type.GetTypeInfo().IsGenericTypeDefinition
+ ? type.GetTypeInfo().GenericTypeParameters
+ : type.GetTypeInfo().GenericTypeArguments;
+ }
+
+ public void RegisterService(Type serviceType)
+ {
+ var processedReqs = new HashSet<Type>();
+
+ var actions = ServiceExecGeneral.Reset(serviceType);
+
+ var requiresRequestStreamTypeInfo = typeof(IRequiresRequestStream).GetTypeInfo();
+
+ var appHost = ServiceStackHost.Instance;
+ foreach (var mi in serviceType.GetActions())
+ {
+ var requestType = mi.GetParameters()[0].ParameterType;
+ if (processedReqs.Contains(requestType)) continue;
+ processedReqs.Add(requestType);
+
+ ServiceExecGeneral.CreateServiceRunnersFor(requestType, actions);
+
+ var returnMarker = requestType.GetTypeWithGenericTypeDefinitionOf(typeof(IReturn<>));
+ var responseType = returnMarker != null ?
+ GetGenericArguments(returnMarker)[0]
+ : mi.ReturnType != typeof(object) && mi.ReturnType != typeof(void) ?
+ mi.ReturnType
+ : Type.GetType(requestType.FullName + "Response");
+
+ RegisterRestPaths(requestType);
+
+ appHost.Metadata.Add(serviceType, requestType, responseType);
+
+ if (requiresRequestStreamTypeInfo.IsAssignableFrom(requestType.GetTypeInfo()))
+ {
+ this.RequestTypeFactoryMap[requestType] = req =>
+ {
+ var restPath = req.GetRoute();
+ var request = RestHandler.CreateRequest(req, restPath, req.GetRequestParams(), ServiceStackHost.Instance.CreateInstance(requestType));
+
+ var rawReq = (IRequiresRequestStream)request;
+ rawReq.RequestStream = req.InputStream;
+ return rawReq;
+ };
+ }
+ }
+ }
+
+ public readonly Dictionary<string, List<RestPath>> RestPathMap = new Dictionary<string, List<RestPath>>(StringComparer.OrdinalIgnoreCase);
+
+ public void RegisterRestPaths(Type requestType)
+ {
+ var appHost = ServiceStackHost.Instance;
+ var attrs = appHost.GetRouteAttributes(requestType);
+ foreach (MediaBrowser.Model.Services.RouteAttribute attr in attrs)
+ {
+ var restPath = new RestPath(requestType, attr.Path, attr.Verbs, attr.Summary, attr.Notes);
+
+ if (!restPath.IsValid)
+ throw new NotSupportedException(string.Format(
+ "RestPath '{0}' on Type '{1}' is not Valid", attr.Path, requestType.GetOperationName()));
+
+ RegisterRestPath(restPath);
+ }
+ }
+
+ private static readonly char[] InvalidRouteChars = new[] { '?', '&' };
+
+ public void RegisterRestPath(RestPath restPath)
+ {
+ if (!restPath.Path.StartsWith("/"))
+ throw new ArgumentException(string.Format("Route '{0}' on '{1}' must start with a '/'", restPath.Path, restPath.RequestType.GetOperationName()));
+ if (restPath.Path.IndexOfAny(InvalidRouteChars) != -1)
+ throw new ArgumentException(string.Format("Route '{0}' on '{1}' contains invalid chars. " +
+ "See https://github.com/ServiceStack/ServiceStack/wiki/Routing for info on valid routes.", restPath.Path, restPath.RequestType.GetOperationName()));
+
+ List<RestPath> pathsAtFirstMatch;
+ if (!RestPathMap.TryGetValue(restPath.FirstMatchHashKey, out pathsAtFirstMatch))
+ {
+ pathsAtFirstMatch = new List<RestPath>();
+ RestPathMap[restPath.FirstMatchHashKey] = pathsAtFirstMatch;
+ }
+ pathsAtFirstMatch.Add(restPath);
+ }
+
+ public void AfterInit()
+ {
+ var appHost = ServiceStackHost.Instance;
+
+ //Register any routes configured on Metadata.Routes
+ foreach (var restPath in appHost.RestPaths)
+ {
+ RegisterRestPath(restPath);
+ }
+
+ //Sync the RestPaths collections
+ appHost.RestPaths.Clear();
+ appHost.RestPaths.AddRange(RestPathMap.Values.SelectMany(x => x));
+ }
+
+ public RestPath GetRestPathForRequest(string httpMethod, string pathInfo)
+ {
+ var matchUsingPathParts = RestPath.GetPathPartsForMatching(pathInfo);
+
+ List<RestPath> firstMatches;
+
+ var yieldedHashMatches = RestPath.GetFirstMatchHashKeys(matchUsingPathParts);
+ foreach (var potentialHashMatch in yieldedHashMatches)
+ {
+ if (!this.RestPathMap.TryGetValue(potentialHashMatch, out firstMatches)) continue;
+
+ var bestScore = -1;
+ foreach (var restPath in firstMatches)
+ {
+ var score = restPath.MatchScore(httpMethod, matchUsingPathParts);
+ if (score > bestScore) bestScore = score;
+ }
+ if (bestScore > 0)
+ {
+ foreach (var restPath in firstMatches)
+ {
+ if (bestScore == restPath.MatchScore(httpMethod, matchUsingPathParts))
+ return restPath;
+ }
+ }
+ }
+
+ var yieldedWildcardMatches = RestPath.GetFirstMatchWildCardHashKeys(matchUsingPathParts);
+ foreach (var potentialHashMatch in yieldedWildcardMatches)
+ {
+ if (!this.RestPathMap.TryGetValue(potentialHashMatch, out firstMatches)) continue;
+
+ var bestScore = -1;
+ foreach (var restPath in firstMatches)
+ {
+ var score = restPath.MatchScore(httpMethod, matchUsingPathParts);
+ if (score > bestScore) bestScore = score;
+ }
+ if (bestScore > 0)
+ {
+ foreach (var restPath in firstMatches)
+ {
+ if (bestScore == restPath.MatchScore(httpMethod, matchUsingPathParts))
+ return restPath;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public async Task<object> Execute(object requestDto, IRequest req)
+ {
+ req.Dto = requestDto;
+ var requestType = requestDto.GetType();
+ req.OperationName = requestType.Name;
+
+ var serviceType = ServiceStackHost.Instance.Metadata.GetServiceTypeByRequest(requestType);
+
+ var service = ServiceStackHost.Instance.CreateInstance(serviceType);
+
+ //var service = typeFactory.CreateInstance(serviceType);
+
+ var serviceRequiresContext = service as IRequiresRequest;
+ if (serviceRequiresContext != null)
+ {
+ serviceRequiresContext.Request = req;
+ }
+
+ if (req.Dto == null) // Don't override existing batched DTO[]
+ req.Dto = requestDto;
+
+ //Executes the service and returns the result
+ var response = await ServiceExecGeneral.Execute(serviceType, req, service, requestDto, requestType.GetOperationName()).ConfigureAwait(false);
+
+ return response;
+ }
+ }
+
+} \ No newline at end of file
diff --git a/ServiceStack/Host/ServiceExec.cs b/ServiceStack/Host/ServiceExec.cs
new file mode 100644
index 0000000000..cb501a3ad3
--- /dev/null
+++ b/ServiceStack/Host/ServiceExec.cs
@@ -0,0 +1,156 @@
+//Copyright (c) Service Stack LLC. All Rights Reserved.
+//License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
+
+namespace ServiceStack.Host
+{
+ public static class ServiceExecExtensions
+ {
+ public static IEnumerable<MethodInfo> GetActions(this Type serviceType)
+ {
+ foreach (var mi in serviceType.GetRuntimeMethods().Where(i => i.IsPublic && !i.IsStatic))
+ {
+ if (mi.GetParameters().Length != 1)
+ continue;
+
+ var actionName = mi.Name.ToUpper();
+ if (!HttpMethods.AllVerbs.Contains(actionName) && actionName != ActionContext.AnyAction)
+ continue;
+
+ yield return mi;
+ }
+ }
+ }
+
+ internal static class ServiceExecGeneral
+ {
+ public static Dictionary<string, ActionContext> execMap = new Dictionary<string, ActionContext>();
+
+ public static void CreateServiceRunnersFor(Type requestType, List<ActionContext> actions)
+ {
+ foreach (var actionCtx in actions)
+ {
+ if (execMap.ContainsKey(actionCtx.Id)) continue;
+
+ execMap[actionCtx.Id] = actionCtx;
+ }
+ }
+
+ public static async Task<object> Execute(Type serviceType, IRequest request, object instance, object requestDto, string requestName)
+ {
+ var actionName = request.Verb
+ ?? HttpMethods.Post; //MQ Services
+
+ ActionContext actionContext;
+ if (ServiceExecGeneral.execMap.TryGetValue(ActionContext.Key(serviceType, actionName, requestName), out actionContext)
+ || ServiceExecGeneral.execMap.TryGetValue(ActionContext.AnyKey(serviceType, requestName), out actionContext))
+ {
+ if (actionContext.RequestFilters != null)
+ {
+ foreach (var requestFilter in actionContext.RequestFilters)
+ {
+ requestFilter.RequestFilter(request, request.Response, requestDto);
+ if (request.Response.IsClosed) return null;
+ }
+ }
+
+ var response = actionContext.ServiceAction(instance, requestDto);
+
+ var taskResponse = response as Task;
+ if (taskResponse != null)
+ {
+ await taskResponse.ConfigureAwait(false);
+ response = ServiceStackHost.Instance.GetTaskResult(taskResponse, requestName);
+ }
+
+ return 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().GetOperationName(), expectedMethodName, serviceType.GetOperationName()));
+ }
+
+ public static List<ActionContext> Reset(Type serviceType)
+ {
+ var actions = new List<ActionContext>();
+
+ foreach (var mi in serviceType.GetActions())
+ {
+ var actionName = mi.Name.ToUpper();
+ var args = mi.GetParameters();
+
+ var requestType = args[0].ParameterType;
+ var actionCtx = new ActionContext
+ {
+ Id = ActionContext.Key(serviceType, actionName, requestType.GetOperationName())
+ };
+
+ try
+ {
+ actionCtx.ServiceAction = CreateExecFn(serviceType, requestType, mi);
+ }
+ catch
+ {
+ //Potential problems with MONO, using reflection for fallback
+ actionCtx.ServiceAction = (service, request) =>
+ mi.Invoke(service, new[] { request });
+ }
+
+ var reqFilters = new List<IHasRequestFilter>();
+
+ foreach (var attr in mi.GetCustomAttributes(true))
+ {
+ var hasReqFilter = attr as IHasRequestFilter;
+
+ if (hasReqFilter != null)
+ reqFilters.Add(hasReqFilter);
+ }
+
+ if (reqFilters.Count > 0)
+ actionCtx.RequestFilters = reqFilters.OrderBy(i => i.Priority).ToArray();
+
+ actions.Add(actionCtx);
+ }
+
+ return actions;
+ }
+
+ private static ActionInvokerFn CreateExecFn(Type serviceType, Type requestType, MethodInfo mi)
+ {
+ var serviceParam = Expression.Parameter(typeof(object), "serviceObj");
+ var serviceStrong = Expression.Convert(serviceParam, serviceType);
+
+ var requestDtoParam = Expression.Parameter(typeof(object), "requestDto");
+ var requestDtoStrong = Expression.Convert(requestDtoParam, requestType);
+
+ Expression callExecute = Expression.Call(
+ serviceStrong, mi, requestDtoStrong);
+
+ if (mi.ReturnType != typeof(void))
+ {
+ var executeFunc = Expression.Lambda<ActionInvokerFn>
+ (callExecute, serviceParam, requestDtoParam).Compile();
+
+ return executeFunc;
+ }
+ else
+ {
+ var executeFunc = Expression.Lambda<VoidActionInvokerFn>
+ (callExecute, serviceParam, requestDtoParam).Compile();
+
+ return (service, request) =>
+ {
+ executeFunc(service, request);
+ return null;
+ };
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/ServiceStack/Host/ServiceMetadata.cs b/ServiceStack/Host/ServiceMetadata.cs
new file mode 100644
index 0000000000..240e6f32d4
--- /dev/null
+++ b/ServiceStack/Host/ServiceMetadata.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+
+namespace ServiceStack.Host
+{
+ public class ServiceMetadata
+ {
+ public ServiceMetadata()
+ {
+ this.OperationsMap = new Dictionary<Type, Type>();
+ }
+
+ public Dictionary<Type, Type> OperationsMap { get; protected set; }
+
+ public void Add(Type serviceType, Type requestType, Type responseType)
+ {
+ this.OperationsMap[requestType] = serviceType;
+ }
+
+ public Type GetServiceTypeByRequest(Type requestType)
+ {
+ Type serviceType;
+ OperationsMap.TryGetValue(requestType, out serviceType);
+ return serviceType;
+ }
+ }
+}
diff --git a/ServiceStack/HttpHandlerFactory.cs b/ServiceStack/HttpHandlerFactory.cs
new file mode 100644
index 0000000000..5f4892d51c
--- /dev/null
+++ b/ServiceStack/HttpHandlerFactory.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Services;
+using ServiceStack.Host;
+
+namespace ServiceStack
+{
+ public class HttpHandlerFactory
+ {
+ // Entry point for HttpListener
+ public static RestHandler GetHandler(IHttpRequest httpReq, ILogger logger)
+ {
+ var pathInfo = httpReq.PathInfo;
+
+ var pathParts = pathInfo.TrimStart('/').Split('/');
+ if (pathParts.Length == 0)
+ {
+ logger.Error("Path parts empty for PathInfo: {0}, Url: {1}", pathInfo, httpReq.RawUrl);
+ return null;
+ }
+
+ string contentType;
+ var restPath = RestHandler.FindMatchingRestPath(httpReq.HttpMethod, pathInfo, out contentType);
+ if (restPath != null)
+ return new RestHandler { RestPath = restPath, RequestName = restPath.RequestType.GetOperationName(), ResponseContentType = contentType };
+
+ return null;
+ }
+ }
+} \ No newline at end of file
diff --git a/ServiceStack/HttpRequestExtensions.cs b/ServiceStack/HttpRequestExtensions.cs
new file mode 100644
index 0000000000..c34d626016
--- /dev/null
+++ b/ServiceStack/HttpRequestExtensions.cs
@@ -0,0 +1,127 @@
+using System;
+using System.Collections.Generic;
+using MediaBrowser.Model.Services;
+using ServiceStack.Host;
+
+namespace ServiceStack
+{
+ public static class HttpRequestExtensions
+ {
+ /**
+ *
+ Input: http://localhost:96/Cambia3/Temp/Test.aspx/path/info?q=item#fragment
+
+ Some HttpRequest path and URL properties:
+ Request.ApplicationPath: /Cambia3
+ Request.CurrentExecutionFilePath: /Cambia3/Temp/Test.aspx
+ Request.FilePath: /Cambia3/Temp/Test.aspx
+ Request.Path: /Cambia3/Temp/Test.aspx/path/info
+ Request.PathInfo: /path/info
+ Request.PhysicalApplicationPath: D:\Inetpub\wwwroot\CambiaWeb\Cambia3\
+ Request.QueryString: /Cambia3/Temp/Test.aspx/path/info?query=arg
+ Request.Url.AbsolutePath: /Cambia3/Temp/Test.aspx/path/info
+ Request.Url.AbsoluteUri: http://localhost:96/Cambia3/Temp/Test.aspx/path/info?query=arg
+ Request.Url.Fragment:
+ Request.Url.Host: localhost
+ Request.Url.LocalPath: /Cambia3/Temp/Test.aspx/path/info
+ Request.Url.PathAndQuery: /Cambia3/Temp/Test.aspx/path/info?query=arg
+ Request.Url.Port: 96
+ Request.Url.Query: ?query=arg
+ Request.Url.Scheme: http
+ Request.Url.Segments: /
+ Cambia3/
+ Temp/
+ Test.aspx/
+ path/
+ info
+ * */
+
+ /// <summary>
+ /// Duplicate Params are given a unique key by appending a #1 suffix
+ /// </summary>
+ public static Dictionary<string, string> GetRequestParams(this IRequest request)
+ {
+ var map = new Dictionary<string, string>();
+
+ foreach (var name in request.QueryString.Keys)
+ {
+ if (name == null) continue; //thank you ASP.NET
+
+ var values = request.QueryString.GetValues(name);
+ if (values.Length == 1)
+ {
+ map[name] = values[0];
+ }
+ else
+ {
+ for (var i = 0; i < values.Length; i++)
+ {
+ map[name + (i == 0 ? "" : "#" + i)] = values[i];
+ }
+ }
+ }
+
+ if ((request.Verb == HttpMethods.Post || request.Verb == HttpMethods.Put)
+ && request.FormData != null)
+ {
+ foreach (var name in request.FormData.Keys)
+ {
+ if (name == null) continue; //thank you ASP.NET
+
+ var values = request.FormData.GetValues(name);
+ if (values.Length == 1)
+ {
+ map[name] = values[0];
+ }
+ else
+ {
+ for (var i = 0; i < values.Length; i++)
+ {
+ map[name + (i == 0 ? "" : "#" + i)] = values[i];
+ }
+ }
+ }
+ }
+
+ return map;
+ }
+
+ /// <summary>
+ /// Duplicate params have their values joined together in a comma-delimited string
+ /// </summary>
+ public static Dictionary<string, string> GetFlattenedRequestParams(this IRequest request)
+ {
+ var map = new Dictionary<string, string>();
+
+ foreach (var name in request.QueryString.Keys)
+ {
+ if (name == null) continue; //thank you ASP.NET
+ map[name] = request.QueryString[name];
+ }
+
+ if ((request.Verb == HttpMethods.Post || request.Verb == HttpMethods.Put)
+ && request.FormData != null)
+ {
+ foreach (var name in request.FormData.Keys)
+ {
+ if (name == null) continue; //thank you ASP.NET
+ map[name] = request.FormData[name];
+ }
+ }
+
+ return map;
+ }
+
+ public static void SetRoute(this IRequest req, RestPath route)
+ {
+ req.Items["__route"] = route;
+ }
+
+ public static RestPath GetRoute(this IRequest req)
+ {
+ object route;
+ req.Items.TryGetValue("__route", out route);
+ return route as RestPath;
+ }
+ }
+} \ No newline at end of file
diff --git a/ServiceStack/HttpResponseExtensionsInternal.cs b/ServiceStack/HttpResponseExtensionsInternal.cs
new file mode 100644
index 0000000000..feb18081ae
--- /dev/null
+++ b/ServiceStack/HttpResponseExtensionsInternal.cs
@@ -0,0 +1,190 @@
+//Copyright (c) Service Stack LLC. All Rights Reserved.
+//License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt
+
+using System;
+using System.IO;
+using System.Net;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+using MediaBrowser.Model.Services;
+using ServiceStack.Host;
+
+namespace ServiceStack
+{
+ public static class HttpResponseExtensionsInternal
+ {
+ public static async Task<bool> WriteToOutputStream(IResponse response, object result)
+ {
+ var asyncStreamWriter = result as IAsyncStreamWriter;
+ if (asyncStreamWriter != null)
+ {
+ await asyncStreamWriter.WriteToAsync(response.OutputStream, CancellationToken.None).ConfigureAwait(false);
+ return true;
+ }
+
+ var streamWriter = result as IStreamWriter;
+ if (streamWriter != null)
+ {
+ streamWriter.WriteTo(response.OutputStream);
+ return true;
+ }
+
+ var stream = result as Stream;
+ if (stream != null)
+ {
+ using (stream)
+ {
+ await stream.CopyToAsync(response.OutputStream).ConfigureAwait(false);
+ return true;
+ }
+ }
+
+ var bytes = result as byte[];
+ if (bytes != null)
+ {
+ response.ContentType = "application/octet-stream";
+ response.SetContentLength(bytes.Length);
+
+ await response.OutputStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// End a ServiceStack Request with no content
+ /// </summary>
+ public static void EndRequestWithNoContent(this IResponse httpRes)
+ {
+ if (httpRes.StatusCode == (int)HttpStatusCode.OK)
+ {
+ httpRes.StatusCode = (int)HttpStatusCode.NoContent;
+ }
+
+ httpRes.SetContentLength(0);
+ }
+
+ public static Task WriteToResponse(this IResponse httpRes, IRequest httpReq, object result)
+ {
+ if (result == null)
+ {
+ httpRes.EndRequestWithNoContent();
+ return Task.FromResult(true);
+ }
+
+ var httpResult = result as IHttpResult;
+ if (httpResult != null)
+ {
+ httpResult.RequestContext = httpReq;
+ httpReq.ResponseContentType = httpResult.ContentType ?? httpReq.ResponseContentType;
+ return httpRes.WriteToResponseInternal(httpResult, httpReq);
+ }
+
+ return httpRes.WriteToResponseInternal(result, httpReq);
+ }
+
+ /// <summary>
+ /// Writes to response.
+ /// Response headers are customizable by implementing IHasHeaders an returning Dictionary of Http headers.
+ /// </summary>
+ /// <param name="response">The response.</param>
+ /// <param name="result">Whether or not it was implicity handled by ServiceStack's built-in handlers.</param>
+ /// <param name="request">The serialization context.</param>
+ /// <returns></returns>
+ private static async Task WriteToResponseInternal(this IResponse response, object result, IRequest request)
+ {
+ var defaultContentType = request.ResponseContentType;
+
+ var httpResult = result as IHttpResult;
+ if (httpResult != null)
+ {
+ if (httpResult.RequestContext == null)
+ httpResult.RequestContext = request;
+
+ response.StatusCode = httpResult.Status;
+ response.StatusDescription = httpResult.StatusCode.ToString();
+ if (string.IsNullOrEmpty(httpResult.ContentType))
+ {
+ 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;
+ if (responseOptions != null)
+ {
+ foreach (var responseHeaders in responseOptions.Headers)
+ {
+ if (string.Equals(responseHeaders.Key, "Content-Length", StringComparison.OrdinalIgnoreCase))
+ {
+ response.SetContentLength(long.Parse(responseHeaders.Value));
+ continue;
+ }
+
+ response.AddHeader(responseHeaders.Key, responseHeaders.Value);
+ }
+ }
+
+ //ContentType='text/html' is the default for a HttpResponse
+ //Do not override if another has been set
+ if (response.ContentType == null || response.ContentType == "text/html")
+ {
+ response.ContentType = defaultContentType;
+ }
+
+ if (new HashSet<string> { "application/json", }.Contains(response.ContentType))
+ {
+ response.ContentType += "; charset=utf-8";
+ }
+
+ var writeToOutputStreamResult = await WriteToOutputStream(response, result).ConfigureAwait(false);
+ if (writeToOutputStreamResult)
+ {
+ return;
+ }
+
+ var responseText = result as string;
+ if (responseText != null)
+ {
+ var bytes = Encoding.UTF8.GetBytes(responseText);
+ response.SetContentLength(bytes.Length);
+ await response.OutputStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
+ return;
+ }
+
+ await WriteObject(request, result, response).ConfigureAwait(false);
+ }
+
+ public static async Task WriteObject(IRequest request, object result, IResponse response)
+ {
+ var contentType = request.ResponseContentType;
+ var serializer = ContentTypes.GetStreamSerializer(contentType);
+
+ using (var ms = new MemoryStream())
+ {
+ serializer(result, ms);
+
+ ms.Position = 0;
+ response.SetContentLength(ms.Length);
+ await ms.CopyToAsync(response.OutputStream).ConfigureAwait(false);
+ }
+
+ //serializer(result, outputStream);
+ }
+ }
+}
diff --git a/ServiceStack/HttpResult.cs b/ServiceStack/HttpResult.cs
new file mode 100644
index 0000000000..3f86ffdf78
--- /dev/null
+++ b/ServiceStack/HttpResult.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Services;
+using ServiceStack.Host;
+
+namespace ServiceStack
+{
+ public class HttpResult
+ : IHttpResult, IAsyncStreamWriter
+ {
+ public object Response { get; set; }
+
+ public HttpResult(object response, string contentType, HttpStatusCode statusCode)
+ {
+ this.Headers = new Dictionary<string, string>();
+ this.Cookies = new List<Cookie>();
+
+ this.Response = response;
+ this.ContentType = contentType;
+ this.StatusCode = statusCode;
+ }
+
+ public string ContentType { get; set; }
+
+ public IDictionary<string, string> Headers { get; private set; }
+
+ public List<Cookie> Cookies { get; private set; }
+
+ public int Status { get; set; }
+
+ public HttpStatusCode StatusCode
+ {
+ get { return (HttpStatusCode)Status; }
+ set { Status = (int)value; }
+ }
+
+ public IRequest RequestContext { get; set; }
+
+ public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken)
+ {
+ var response = RequestContext != null ? RequestContext.Response : null;
+
+ var bytesResponse = this.Response as byte[];
+ if (bytesResponse != null)
+ {
+ if (response != null)
+ response.SetContentLength(bytesResponse.Length);
+
+ await responseStream.WriteAsync(bytesResponse, 0, bytesResponse.Length).ConfigureAwait(false);
+ return;
+ }
+
+ await HttpResponseExtensionsInternal.WriteObject(this.RequestContext, this.Response, response).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/ServiceStack/HttpUtils.cs b/ServiceStack/HttpUtils.cs
new file mode 100644
index 0000000000..41d191d613
--- /dev/null
+++ b/ServiceStack/HttpUtils.cs
@@ -0,0 +1,34 @@
+//Copyright (c) Service Stack LLC. All Rights Reserved.
+//License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt
+
+using System;
+using System.Collections.Generic;
+
+namespace ServiceStack
+{
+ internal static class HttpMethods
+ {
+ static readonly string[] allVerbs = new[] {
+ "OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT", // RFC 2616
+ "PROPFIND", "PROPPATCH", "MKCOL", "COPY", "MOVE", "LOCK", "UNLOCK", // RFC 2518
+ "VERSION-CONTROL", "REPORT", "CHECKOUT", "CHECKIN", "UNCHECKOUT",
+ "MKWORKSPACE", "UPDATE", "LABEL", "MERGE", "BASELINE-CONTROL", "MKACTIVITY", // RFC 3253
+ "ORDERPATCH", // RFC 3648
+ "ACL", // RFC 3744
+ "PATCH", // https://datatracker.ietf.org/doc/draft-dusseault-http-patch/
+ "SEARCH", // https://datatracker.ietf.org/doc/draft-reschke-webdav-search/
+ "BCOPY", "BDELETE", "BMOVE", "BPROPFIND", "BPROPPATCH", "NOTIFY",
+ "POLL", "SUBSCRIBE", "UNSUBSCRIBE" //MS Exchange WebDav: http://msdn.microsoft.com/en-us/library/aa142917.aspx
+ };
+
+ public static HashSet<string> AllVerbs = new HashSet<string>(allVerbs);
+
+ public const string Get = "GET";
+ public const string Put = "PUT";
+ public const string Post = "POST";
+ public const string Delete = "DELETE";
+ public const string Options = "OPTIONS";
+ public const string Head = "HEAD";
+ public const string Patch = "PATCH";
+ }
+}
diff --git a/ServiceStack/Properties/AssemblyInfo.cs b/ServiceStack/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..6073dc0b44
--- /dev/null
+++ b/ServiceStack/Properties/AssemblyInfo.cs
@@ -0,0 +1,25 @@
+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("ServiceStack")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Service Stack LLC")]
+[assembly: AssemblyProduct("ServiceStack")]
+[assembly: AssemblyCopyright("Copyright (c) ServiceStack 2016")]
+[assembly: AssemblyTrademark("Service Stack")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("06704d66-af8e-411f-8260-8d05de5ce6ad")]
+
+[assembly: AssemblyVersion("4.0.0.0")]
+[assembly: AssemblyFileVersion("4.0.0.0")]
diff --git a/ServiceStack/ReflectionExtensions.cs b/ServiceStack/ReflectionExtensions.cs
new file mode 100644
index 0000000000..bbabd0dd74
--- /dev/null
+++ b/ServiceStack/ReflectionExtensions.cs
@@ -0,0 +1,270 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ServiceStack
+{
+ public static class ReflectionExtensions
+ {
+ public static bool IsInstanceOf(this Type type, Type thisOrBaseType)
+ {
+ while (type != null)
+ {
+ if (type == thisOrBaseType)
+ return true;
+
+ type = type.BaseType();
+ }
+ return false;
+ }
+
+ public static Type FirstGenericType(this Type type)
+ {
+ while (type != null)
+ {
+ if (type.IsGeneric())
+ return type;
+
+ type = type.BaseType();
+ }
+ return null;
+ }
+
+ public static Type GetTypeWithGenericTypeDefinitionOf(this Type type, Type genericTypeDefinition)
+ {
+ foreach (var t in type.GetTypeInterfaces())
+ {
+ if (t.IsGeneric() && t.GetGenericTypeDefinition() == genericTypeDefinition)
+ {
+ return t;
+ }
+ }
+
+ var genericType = type.FirstGenericType();
+ if (genericType != null && genericType.GetGenericTypeDefinition() == genericTypeDefinition)
+ {
+ return genericType;
+ }
+
+ return null;
+ }
+
+ public static PropertyInfo[] GetAllProperties(this Type type)
+ {
+ if (type.IsInterface())
+ {
+ var propertyInfos = new List<PropertyInfo>();
+
+ var considered = new List<Type>();
+ var queue = new Queue<Type>();
+ considered.Add(type);
+ queue.Enqueue(type);
+
+ while (queue.Count > 0)
+ {
+ var subType = queue.Dequeue();
+ foreach (var subInterface in subType.GetTypeInterfaces())
+ {
+ if (considered.Contains(subInterface)) continue;
+
+ considered.Add(subInterface);
+ queue.Enqueue(subInterface);
+ }
+
+ var typeProperties = subType.GetTypesProperties();
+
+ var newPropertyInfos = typeProperties
+ .Where(x => !propertyInfos.Contains(x));
+
+ propertyInfos.InsertRange(0, newPropertyInfos);
+ }
+
+ return propertyInfos.ToArray();
+ }
+
+ return type.GetTypesProperties()
+ .Where(t => t.GetIndexParameters().Length == 0) // ignore indexed properties
+ .ToArray();
+ }
+
+ public static PropertyInfo[] GetPublicProperties(this Type type)
+ {
+ if (type.IsInterface())
+ {
+ var propertyInfos = new List<PropertyInfo>();
+
+ var considered = new List<Type>();
+ var queue = new Queue<Type>();
+ considered.Add(type);
+ queue.Enqueue(type);
+
+ while (queue.Count > 0)
+ {
+ var subType = queue.Dequeue();
+ foreach (var subInterface in subType.GetTypeInterfaces())
+ {
+ if (considered.Contains(subInterface)) continue;
+
+ considered.Add(subInterface);
+ queue.Enqueue(subInterface);
+ }
+
+ var typeProperties = subType.GetTypesPublicProperties();
+
+ var newPropertyInfos = typeProperties
+ .Where(x => !propertyInfos.Contains(x));
+
+ propertyInfos.InsertRange(0, newPropertyInfos);
+ }
+
+ return propertyInfos.ToArray();
+ }
+
+ return type.GetTypesPublicProperties()
+ .Where(t => t.GetIndexParameters().Length == 0) // ignore indexed properties
+ .ToArray();
+ }
+
+ public const string DataMember = "DataMemberAttribute";
+
+ internal static string[] IgnoreAttributesNamed = new[] {
+ "IgnoreDataMemberAttribute",
+ "JsonIgnoreAttribute"
+ };
+
+ public static PropertyInfo[] GetSerializableProperties(this Type type)
+ {
+ var properties = type.IsDto()
+ ? type.GetAllProperties()
+ : type.GetPublicProperties();
+ return properties.OnlySerializableProperties(type);
+ }
+
+
+ private static List<Type> _excludeTypes = new List<Type> { typeof(Stream) };
+
+ public static PropertyInfo[] OnlySerializableProperties(this PropertyInfo[] properties, Type type = null)
+ {
+ var isDto = type.IsDto();
+ var readableProperties = properties.Where(x => x.PropertyGetMethod(nonPublic: isDto) != null);
+
+ if (isDto)
+ {
+ return readableProperties.Where(attr =>
+ attr.HasAttribute<DataMemberAttribute>()).ToArray();
+ }
+
+ // else return those properties that are not decorated with IgnoreDataMember
+ return readableProperties
+ .Where(prop => prop.AllAttributes()
+ .All(attr =>
+ {
+ var name = attr.GetType().Name;
+ return !IgnoreAttributesNamed.Contains(name);
+ }))
+ .Where(prop => !_excludeTypes.Contains(prop.PropertyType))
+ .ToArray();
+ }
+ }
+
+ public static class PlatformExtensions //Because WinRT is a POS
+ {
+ public static bool IsInterface(this Type type)
+ {
+ return type.GetTypeInfo().IsInterface;
+ }
+
+ public static bool IsGeneric(this Type type)
+ {
+ return type.GetTypeInfo().IsGenericType;
+ }
+
+ public static Type BaseType(this Type type)
+ {
+ return type.GetTypeInfo().BaseType;
+ }
+
+ public static Type[] GetTypeInterfaces(this Type type)
+ {
+ return type.GetTypeInfo().ImplementedInterfaces.ToArray();
+ }
+
+ internal static PropertyInfo[] GetTypesPublicProperties(this Type subType)
+ {
+ var pis = new List<PropertyInfo>();
+ foreach (var pi in subType.GetRuntimeProperties())
+ {
+ var mi = pi.GetMethod ?? pi.SetMethod;
+ if (mi != null && mi.IsStatic) continue;
+ pis.Add(pi);
+ }
+ return pis.ToArray();
+ }
+
+ internal static PropertyInfo[] GetTypesProperties(this Type subType)
+ {
+ var pis = new List<PropertyInfo>();
+ foreach (var pi in subType.GetRuntimeProperties())
+ {
+ var mi = pi.GetMethod ?? pi.SetMethod;
+ if (mi != null && mi.IsStatic) continue;
+ pis.Add(pi);
+ }
+ return pis.ToArray();
+ }
+
+ public static bool HasAttribute<T>(this Type type)
+ {
+ return type.AllAttributes().Any(x => x.GetType() == typeof(T));
+ }
+
+ public static bool HasAttribute<T>(this PropertyInfo pi)
+ {
+ return pi.AllAttributes().Any(x => x.GetType() == typeof(T));
+ }
+
+ public static bool IsDto(this Type type)
+ {
+ if (type == null)
+ return false;
+
+ return type.HasAttribute<DataContractAttribute>();
+ }
+
+ public static MethodInfo PropertyGetMethod(this PropertyInfo pi, bool nonPublic = false)
+ {
+ return pi.GetMethod;
+ }
+
+ public static object[] AllAttributes(this PropertyInfo propertyInfo)
+ {
+ return propertyInfo.GetCustomAttributes(true).ToArray();
+ }
+
+ public static object[] AllAttributes(this PropertyInfo propertyInfo, Type attrType)
+ {
+ return propertyInfo.GetCustomAttributes(true).Where(x => attrType.IsInstanceOf(x.GetType())).ToArray();
+ }
+
+ public static object[] AllAttributes(this Type type)
+ {
+ return type.GetTypeInfo().GetCustomAttributes(true).ToArray();
+ }
+
+ public static TAttr[] AllAttributes<TAttr>(this PropertyInfo pi)
+ {
+ return pi.AllAttributes(typeof(TAttr)).Cast<TAttr>().ToArray();
+ }
+
+ public static TAttr[] AllAttributes<TAttr>(this Type type)
+ where TAttr : Attribute
+ {
+ return type.GetTypeInfo().GetCustomAttributes<TAttr>(true).ToArray();
+ }
+ }
+}
diff --git a/ServiceStack/ServiceStack.csproj b/ServiceStack/ServiceStack.csproj
new file mode 100644
index 0000000000..5413d4e550
--- /dev/null
+++ b/ServiceStack/ServiceStack.csproj
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.30729</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{680A1709-25EB-4D52-A87F-EE03FFD94BAA}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>ServiceStack</RootNamespace>
+ <AssemblyName>ServiceStack</AssemblyName>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <FileUpgradeFlags>
+ </FileUpgradeFlags>
+ <OldToolsVersion>3.5</OldToolsVersion>
+ <UpgradeBackupLocation />
+ <PublishUrl>publish\</PublishUrl>
+ <Install>true</Install>
+ <InstallFrom>Disk</InstallFrom>
+ <UpdateEnabled>false</UpdateEnabled>
+ <UpdateMode>Foreground</UpdateMode>
+ <UpdateInterval>7</UpdateInterval>
+ <UpdateIntervalUnits>Days</UpdateIntervalUnits>
+ <UpdatePeriodically>false</UpdatePeriodically>
+ <UpdateRequired>false</UpdateRequired>
+ <MapFileExtensions>true</MapFileExtensions>
+ <ApplicationRevision>0</ApplicationRevision>
+ <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
+ <IsWebBootstrapper>false</IsWebBootstrapper>
+ <UseApplicationTrust>false</UseApplicationTrust>
+ <BootstrapperEnabled>true</BootstrapperEnabled>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>True</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>False</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>TRACE;DEBUG;MONO</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+ <Prefer32Bit>false</Prefer32Bit>
+ </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>
+ <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+ <DocumentationFile>
+ </DocumentationFile>
+ <Prefer32Bit>false</Prefer32Bit>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Signed|AnyCPU'">
+ <OutputPath>bin\Signed\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <DocumentationFile>bin\Release\ServiceStack.XML</DocumentationFile>
+ <Optimize>true</Optimize>
+ <DebugType>pdbonly</DebugType>
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <ErrorReport>prompt</ErrorReport>
+ <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
+ <Prefer32Bit>false</Prefer32Bit>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="HttpUtils.cs" />
+ <Compile Include="Host\ContentTypes.cs" />
+ <Compile Include="ReflectionExtensions.cs" />
+ <Compile Include="StringMapTypeDeserializer.cs" />
+ <Compile Include="HttpResult.cs" />
+ <Compile Include="ServiceStackHost.cs" />
+ <Compile Include="ServiceStackHost.Runtime.cs" />
+ <Compile Include="Host\ServiceExec.cs" />
+ <Compile Include="UrlExtensions.cs" />
+ <Compile Include="Host\ActionContext.cs" />
+ <Compile Include="HttpRequestExtensions.cs" />
+ <Compile Include="Host\RestPath.cs" />
+ <Compile Include="Host\ServiceController.cs" />
+ <Compile Include="Host\ServiceMetadata.cs" />
+ <Compile Include="Host\RestHandler.cs" />
+ <Compile Include="HttpResponseExtensionsInternal.cs" />
+ <Compile Include="HttpHandlerFactory.cs" />
+ <Compile Include="FilterAttributeCache.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="packages.config" />
+ </ItemGroup>
+ <ItemGroup>
+ <BootstrapperPackage Include="Microsoft.Net.Client.3.5">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
+ <Visible>False</Visible>
+ <ProductName>Windows Installer 3.1</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ </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>
+ -->
+ <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> \ No newline at end of file
diff --git a/ServiceStack/ServiceStack.nuget.targets b/ServiceStack/ServiceStack.nuget.targets
new file mode 100644
index 0000000000..e69ce0e64f
--- /dev/null
+++ b/ServiceStack/ServiceStack.nuget.targets
@@ -0,0 +1,6 @@
+<?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/ServiceStack/ServiceStackHost.Runtime.cs b/ServiceStack/ServiceStackHost.Runtime.cs
new file mode 100644
index 0000000000..aaa50633b3
--- /dev/null
+++ b/ServiceStack/ServiceStackHost.Runtime.cs
@@ -0,0 +1,57 @@
+// Copyright (c) Service Stack LLC. All Rights Reserved.
+// License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt
+
+
+using MediaBrowser.Model.Services;
+using ServiceStack.Support.WebHost;
+
+namespace ServiceStack
+{
+ public abstract partial class ServiceStackHost
+ {
+ /// <summary>
+ /// Applies the request filters. Returns whether or not the request has been handled
+ /// and no more processing should be done.
+ /// </summary>
+ /// <returns></returns>
+ public virtual void ApplyRequestFilters(IRequest req, IResponse res, object requestDto)
+ {
+ //Exec all RequestFilter attributes with Priority < 0
+ var attributes = FilterAttributeCache.GetRequestFilterAttributes(requestDto.GetType());
+ var i = 0;
+ for (; i < attributes.Length && attributes[i].Priority < 0; i++)
+ {
+ var attribute = attributes[i];
+ attribute.RequestFilter(req, res, requestDto);
+ }
+
+ //Exec global filters
+ foreach (var requestFilter in GlobalRequestFilters)
+ {
+ requestFilter(req, res, requestDto);
+ }
+
+ //Exec remaining RequestFilter attributes with Priority >= 0
+ for (; i < attributes.Length && attributes[i].Priority >= 0; i++)
+ {
+ var attribute = attributes[i];
+ attribute.RequestFilter(req, res, requestDto);
+ }
+ }
+
+ /// <summary>
+ /// Applies the response filters. Returns whether or not the request has been handled
+ /// and no more processing should be done.
+ /// </summary>
+ /// <returns></returns>
+ public virtual void ApplyResponseFilters(IRequest req, IResponse res, object response)
+ {
+ //Exec global filters
+ foreach (var responseFilter in GlobalResponseFilters)
+ {
+ responseFilter(req, res, response);
+ }
+ }
+ }
+
+} \ No newline at end of file
diff --git a/ServiceStack/ServiceStackHost.cs b/ServiceStack/ServiceStackHost.cs
new file mode 100644
index 0000000000..8a1db25e48
--- /dev/null
+++ b/ServiceStack/ServiceStackHost.cs
@@ -0,0 +1,104 @@
+// Copyright (c) Service Stack LLC. All Rights Reserved.
+// License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt
+
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Services;
+using ServiceStack.Host;
+
+namespace ServiceStack
+{
+ public abstract partial class ServiceStackHost : IDisposable
+ {
+ public static ServiceStackHost Instance { get; protected set; }
+
+ protected ServiceStackHost(string serviceName)
+ {
+ ServiceName = serviceName;
+ ServiceController = CreateServiceController();
+
+ RestPaths = new List<RestPath>();
+ Metadata = new ServiceMetadata();
+ GlobalRequestFilters = new List<Action<IRequest, IResponse, object>>();
+ GlobalResponseFilters = new List<Action<IRequest, IResponse, object>>();
+ }
+
+ public abstract void Configure();
+
+ public abstract object CreateInstance(Type type);
+
+ protected abstract ServiceController CreateServiceController();
+
+ public virtual ServiceStackHost Init()
+ {
+ Instance = this;
+
+ ServiceController.Init();
+ Configure();
+
+ ServiceController.AfterInit();
+
+ return this;
+ }
+
+ public virtual ServiceStackHost Start(string urlBase)
+ {
+ throw new NotImplementedException("Start(listeningAtUrlBase) is not supported by this AppHost");
+ }
+
+ public string ServiceName { get; set; }
+
+ public ServiceMetadata Metadata { get; set; }
+
+ public ServiceController ServiceController { get; set; }
+
+ public List<RestPath> RestPaths = new List<RestPath>();
+
+ public List<Action<IRequest, IResponse, object>> GlobalRequestFilters { get; set; }
+
+ public List<Action<IRequest, IResponse, object>> GlobalResponseFilters { get; set; }
+
+ public abstract T TryResolve<T>();
+ public abstract T Resolve<T>();
+
+ public virtual MediaBrowser.Model.Services.RouteAttribute[] GetRouteAttributes(Type requestType)
+ {
+ return requestType.AllAttributes<MediaBrowser.Model.Services.RouteAttribute>();
+ }
+
+ public abstract object GetTaskResult(Task task, string requestName);
+
+ public abstract Func<string, object> GetParseFn(Type propertyType);
+
+ public abstract void SerializeToJson(object o, Stream stream);
+ public abstract void SerializeToXml(object o, Stream stream);
+ public abstract object DeserializeXml(Type type, Stream stream);
+ public abstract object DeserializeJson(Type type, Stream stream);
+
+ public virtual void Dispose()
+ {
+ //JsConfig.Reset(); //Clears Runtime Attributes
+
+ Instance = null;
+ }
+
+ protected abstract ILogger Logger
+ {
+ get;
+ }
+
+ public void OnLogError(Type type, string message)
+ {
+ Logger.Error(message);
+ }
+
+ public void OnLogError(Type type, string message, Exception ex)
+ {
+ Logger.ErrorException(message, ex);
+ }
+ }
+}
diff --git a/ServiceStack/StringMapTypeDeserializer.cs b/ServiceStack/StringMapTypeDeserializer.cs
new file mode 100644
index 0000000000..762e8aafff
--- /dev/null
+++ b/ServiceStack/StringMapTypeDeserializer.cs
@@ -0,0 +1,126 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+using System.Linq;
+using System.Reflection;
+
+namespace ServiceStack.Serialization
+{
+ /// <summary>
+ /// Serializer cache of delegates required to create a type from a string map (e.g. for REST urls)
+ /// </summary>
+ public class StringMapTypeDeserializer
+ {
+ internal class PropertySerializerEntry
+ {
+ public PropertySerializerEntry(Action<object,object> propertySetFn, Func<string, object> propertyParseStringFn)
+ {
+ PropertySetFn = propertySetFn;
+ PropertyParseStringFn = propertyParseStringFn;
+ }
+
+ public Action<object, object> PropertySetFn;
+ public Func<string,object> PropertyParseStringFn;
+ public Type PropertyType;
+ }
+
+ private readonly Type type;
+ private readonly Dictionary<string, PropertySerializerEntry> propertySetterMap
+ = new Dictionary<string, PropertySerializerEntry>(StringComparer.OrdinalIgnoreCase);
+
+ public Func<string, object> GetParseFn(Type propertyType)
+ {
+ //Don't JSV-decode string values for string properties
+ if (propertyType == typeof(string))
+ return s => s;
+
+ return ServiceStackHost.Instance.GetParseFn(propertyType);
+ }
+
+ public StringMapTypeDeserializer(Type type)
+ {
+ this.type = type;
+
+ foreach (var propertyInfo in type.GetSerializableProperties())
+ {
+ var propertySetFn = TypeAccessor.GetSetPropertyMethod(type, propertyInfo);
+ var propertyType = propertyInfo.PropertyType;
+ var propertyParseStringFn = GetParseFn(propertyType);
+ var propertySerializer = new PropertySerializerEntry(propertySetFn, propertyParseStringFn) { PropertyType = propertyType };
+
+ var attr = propertyInfo.AllAttributes<DataMemberAttribute>().FirstOrDefault();
+ if (attr != null && attr.Name != null)
+ {
+ propertySetterMap[attr.Name] = propertySerializer;
+ }
+ propertySetterMap[propertyInfo.Name] = propertySerializer;
+ }
+ }
+
+ public object PopulateFromMap(object instance, IDictionary<string, string> keyValuePairs)
+ {
+ string propertyName = null;
+ string propertyTextValue = null;
+ PropertySerializerEntry propertySerializerEntry = null;
+
+ if (instance == null)
+ instance = ServiceStackHost.Instance.CreateInstance(type);
+
+ foreach (var pair in keyValuePairs.Where(x => !string.IsNullOrEmpty(x.Value)))
+ {
+ propertyName = pair.Key;
+ propertyTextValue = pair.Value;
+
+ if (!propertySetterMap.TryGetValue(propertyName, out propertySerializerEntry))
+ {
+ if (propertyName == "v")
+ {
+ continue;
+ }
+
+ continue;
+ }
+
+ if (propertySerializerEntry.PropertySetFn == null)
+ {
+ continue;
+ }
+
+ if (propertySerializerEntry.PropertyType == typeof(bool))
+ {
+ //InputExtensions.cs#530 MVC Checkbox helper emits extra hidden input field, generating 2 values, first is the real value
+ propertyTextValue = LeftPart(propertyTextValue, ',');
+ }
+
+ var value = propertySerializerEntry.PropertyParseStringFn(propertyTextValue);
+ if (value == null)
+ {
+ continue;
+ }
+ propertySerializerEntry.PropertySetFn(instance, value);
+ }
+
+ return instance;
+ }
+
+ public static string LeftPart(string strVal, char needle)
+ {
+ if (strVal == null) return null;
+ var pos = strVal.IndexOf(needle);
+ return pos == -1
+ ? strVal
+ : strVal.Substring(0, pos);
+ }
+ }
+
+ internal class TypeAccessor
+ {
+ public static Action<object, object> GetSetPropertyMethod(Type type, PropertyInfo propertyInfo)
+ {
+ if (!propertyInfo.CanWrite || propertyInfo.GetIndexParameters().Any()) return null;
+
+ var setMethodInfo = propertyInfo.SetMethod;
+ return (instance, value) => setMethodInfo.Invoke(instance, new[] { value });
+ }
+ }
+}
diff --git a/ServiceStack/UrlExtensions.cs b/ServiceStack/UrlExtensions.cs
new file mode 100644
index 0000000000..7b5a50ef17
--- /dev/null
+++ b/ServiceStack/UrlExtensions.cs
@@ -0,0 +1,33 @@
+using System;
+
+namespace ServiceStack
+{
+ /// <summary>
+ /// Donated by Ivan Korneliuk from his post:
+ /// http://korneliuk.blogspot.com/2012/08/servicestack-reusing-dtos.html
+ ///
+ /// Modified to only allow using routes matching the supplied HTTP Verb
+ /// </summary>
+ public static class UrlExtensions
+ {
+ public static string GetOperationName(this Type type)
+ {
+ var typeName = type.FullName != null //can be null, e.g. generic types
+ ? LeftPart(type.FullName, "[[") //Generic Fullname
+ .Replace(type.Namespace + ".", "") //Trim Namespaces
+ .Replace("+", ".") //Convert nested into normal type
+ : type.Name;
+
+ return type.IsGenericParameter ? "'" + typeName : typeName;
+ }
+
+ public static string LeftPart(string strVal, string needle)
+ {
+ if (strVal == null) return null;
+ var pos = strVal.IndexOf(needle, StringComparison.OrdinalIgnoreCase);
+ return pos == -1
+ ? strVal
+ : strVal.Substring(0, pos);
+ }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Model.Portable/FodyWeavers.xml b/ServiceStack/packages.config
index 6e2fa02e64..6b8deb9c96 100644
--- a/MediaBrowser.Model.Portable/FodyWeavers.xml
+++ b/ServiceStack/packages.config
@@ -1,3 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
-<Weavers>
-</Weavers> \ No newline at end of file
+<packages>
+</packages> \ No newline at end of file
diff --git a/ServiceStack/project.json b/ServiceStack/project.json
new file mode 100644
index 0000000000..fbbe9eaf32
--- /dev/null
+++ b/ServiceStack/project.json
@@ -0,0 +1,17 @@
+{
+ "frameworks":{
+ "netstandard1.6":{
+ "dependencies":{
+ "NETStandard.Library":"1.6.0",
+ }
+ },
+ ".NETPortable,Version=v4.5,Profile=Profile7":{
+ "buildOptions": {
+ "define": [ ]
+ },
+ "frameworkAssemblies":{
+
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/SocketHttpListener.Portable/ByteOrder.cs b/SocketHttpListener.Portable/ByteOrder.cs
new file mode 100644
index 0000000000..f5db52fd72
--- /dev/null
+++ b/SocketHttpListener.Portable/ByteOrder.cs
@@ -0,0 +1,17 @@
+namespace SocketHttpListener
+{
+ /// <summary>
+ /// Contains the values that indicate whether the byte order is a Little-endian or Big-endian.
+ /// </summary>
+ public enum ByteOrder : byte
+ {
+ /// <summary>
+ /// Indicates a Little-endian.
+ /// </summary>
+ Little,
+ /// <summary>
+ /// Indicates a Big-endian.
+ /// </summary>
+ Big
+ }
+}
diff --git a/SocketHttpListener.Portable/CloseEventArgs.cs b/SocketHttpListener.Portable/CloseEventArgs.cs
new file mode 100644
index 0000000000..b1bb4b1960
--- /dev/null
+++ b/SocketHttpListener.Portable/CloseEventArgs.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Text;
+
+namespace SocketHttpListener
+{
+ /// <summary>
+ /// Contains the event data associated with a <see cref="WebSocket.OnClose"/> event.
+ /// </summary>
+ /// <remarks>
+ /// A <see cref="WebSocket.OnClose"/> event occurs when the WebSocket connection has been closed.
+ /// If you would like to get the reason for the close, you should access the <see cref="Code"/> or
+ /// <see cref="Reason"/> property.
+ /// </remarks>
+ public class CloseEventArgs : EventArgs
+ {
+ #region Private Fields
+
+ private bool _clean;
+ private ushort _code;
+ private string _reason;
+
+ #endregion
+
+ #region Internal Constructors
+
+ internal CloseEventArgs (PayloadData payload)
+ {
+ var data = payload.ApplicationData;
+ var len = data.Length;
+ _code = len > 1
+ ? data.SubArray (0, 2).ToUInt16 (ByteOrder.Big)
+ : (ushort) CloseStatusCode.NoStatusCode;
+
+ _reason = len > 2
+ ? GetUtf8String(data.SubArray (2, len - 2))
+ : String.Empty;
+ }
+
+ private string GetUtf8String(byte[] bytes)
+ {
+ return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ /// <summary>
+ /// Gets the status code for the close.
+ /// </summary>
+ /// <value>
+ /// A <see cref="ushort"/> that represents the status code for the close if any.
+ /// </value>
+ public ushort Code {
+ get {
+ return _code;
+ }
+ }
+
+ /// <summary>
+ /// Gets the reason for the close.
+ /// </summary>
+ /// <value>
+ /// A <see cref="string"/> that represents the reason for the close if any.
+ /// </value>
+ public string Reason {
+ get {
+ return _reason;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether the WebSocket connection has been closed cleanly.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if the WebSocket connection has been closed cleanly; otherwise, <c>false</c>.
+ /// </value>
+ public bool WasClean {
+ get {
+ return _clean;
+ }
+
+ internal set {
+ _clean = value;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/SocketHttpListener.Portable/CloseStatusCode.cs b/SocketHttpListener.Portable/CloseStatusCode.cs
new file mode 100644
index 0000000000..62a268bce1
--- /dev/null
+++ b/SocketHttpListener.Portable/CloseStatusCode.cs
@@ -0,0 +1,94 @@
+namespace SocketHttpListener
+{
+ /// <summary>
+ /// Contains the values of the status code for the WebSocket connection close.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// The values of the status code are defined in
+ /// <see href="http://tools.ietf.org/html/rfc6455#section-7.4">Section 7.4</see>
+ /// of RFC 6455.
+ /// </para>
+ /// <para>
+ /// "Reserved value" must not be set as a status code in a close control frame
+ /// by an endpoint. It's designated for use in applications expecting a status
+ /// code to indicate that the connection was closed due to the system grounds.
+ /// </para>
+ /// </remarks>
+ public enum CloseStatusCode : ushort
+ {
+ /// <summary>
+ /// Equivalent to close status 1000.
+ /// Indicates a normal close.
+ /// </summary>
+ Normal = 1000,
+ /// <summary>
+ /// Equivalent to close status 1001.
+ /// Indicates that an endpoint is going away.
+ /// </summary>
+ Away = 1001,
+ /// <summary>
+ /// Equivalent to close status 1002.
+ /// Indicates that an endpoint is terminating the connection due to a protocol error.
+ /// </summary>
+ ProtocolError = 1002,
+ /// <summary>
+ /// Equivalent to close status 1003.
+ /// Indicates that an endpoint is terminating the connection because it has received
+ /// an unacceptable type message.
+ /// </summary>
+ IncorrectData = 1003,
+ /// <summary>
+ /// Equivalent to close status 1004.
+ /// Still undefined. Reserved value.
+ /// </summary>
+ Undefined = 1004,
+ /// <summary>
+ /// Equivalent to close status 1005.
+ /// Indicates that no status code was actually present. Reserved value.
+ /// </summary>
+ NoStatusCode = 1005,
+ /// <summary>
+ /// Equivalent to close status 1006.
+ /// Indicates that the connection was closed abnormally. Reserved value.
+ /// </summary>
+ Abnormal = 1006,
+ /// <summary>
+ /// Equivalent to close status 1007.
+ /// Indicates that an endpoint is terminating the connection because it has received
+ /// a message that contains a data that isn't consistent with the type of the message.
+ /// </summary>
+ InconsistentData = 1007,
+ /// <summary>
+ /// Equivalent to close status 1008.
+ /// Indicates that an endpoint is terminating the connection because it has received
+ /// a message that violates its policy.
+ /// </summary>
+ PolicyViolation = 1008,
+ /// <summary>
+ /// Equivalent to close status 1009.
+ /// Indicates that an endpoint is terminating the connection because it has received
+ /// a message that is too big to process.
+ /// </summary>
+ TooBig = 1009,
+ /// <summary>
+ /// Equivalent to close status 1010.
+ /// Indicates that the client is terminating the connection because it has expected
+ /// the server to negotiate one or more extension, but the server didn't return them
+ /// in the handshake response.
+ /// </summary>
+ IgnoreExtension = 1010,
+ /// <summary>
+ /// Equivalent to close status 1011.
+ /// Indicates that the server is terminating the connection because it has encountered
+ /// an unexpected condition that prevented it from fulfilling the request.
+ /// </summary>
+ ServerError = 1011,
+ /// <summary>
+ /// Equivalent to close status 1015.
+ /// Indicates that the connection was closed due to a failure to perform a TLS handshake.
+ /// Reserved value.
+ /// </summary>
+ TlsHandshakeFailure = 1015
+ }
+}
diff --git a/SocketHttpListener.Portable/CompressionMethod.cs b/SocketHttpListener.Portable/CompressionMethod.cs
new file mode 100644
index 0000000000..36a48d94cb
--- /dev/null
+++ b/SocketHttpListener.Portable/CompressionMethod.cs
@@ -0,0 +1,23 @@
+namespace SocketHttpListener
+{
+ /// <summary>
+ /// Contains the values of the compression method used to compress the message on the WebSocket
+ /// connection.
+ /// </summary>
+ /// <remarks>
+ /// The values of the compression method are defined in
+ /// <see href="http://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-09">Compression
+ /// Extensions for WebSocket</see>.
+ /// </remarks>
+ public enum CompressionMethod : byte
+ {
+ /// <summary>
+ /// Indicates non compression.
+ /// </summary>
+ None,
+ /// <summary>
+ /// Indicates using DEFLATE.
+ /// </summary>
+ Deflate
+ }
+}
diff --git a/SocketHttpListener.Portable/ErrorEventArgs.cs b/SocketHttpListener.Portable/ErrorEventArgs.cs
new file mode 100644
index 0000000000..bf1d6fc95f
--- /dev/null
+++ b/SocketHttpListener.Portable/ErrorEventArgs.cs
@@ -0,0 +1,46 @@
+using System;
+
+namespace SocketHttpListener
+{
+ /// <summary>
+ /// Contains the event data associated with a <see cref="WebSocket.OnError"/> event.
+ /// </summary>
+ /// <remarks>
+ /// A <see cref="WebSocket.OnError"/> event occurs when the <see cref="WebSocket"/> gets an error.
+ /// If you would like to get the error message, you should access the <see cref="Message"/>
+ /// property.
+ /// </remarks>
+ public class ErrorEventArgs : EventArgs
+ {
+ #region Private Fields
+
+ private string _message;
+
+ #endregion
+
+ #region Internal Constructors
+
+ internal ErrorEventArgs (string message)
+ {
+ _message = message;
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ /// <summary>
+ /// Gets the error message.
+ /// </summary>
+ /// <value>
+ /// A <see cref="string"/> that represents the error message.
+ /// </value>
+ public string Message {
+ get {
+ return _message;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/SocketHttpListener.Portable/Ext.cs b/SocketHttpListener.Portable/Ext.cs
new file mode 100644
index 0000000000..87f0887ed4
--- /dev/null
+++ b/SocketHttpListener.Portable/Ext.cs
@@ -0,0 +1,1083 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.IO;
+using System.IO.Compression;
+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;
+
+namespace SocketHttpListener
+{
+ /// <summary>
+ /// Provides a set of static methods for the websocket-sharp.
+ /// </summary>
+ public static class Ext
+ {
+ #region Private Const Fields
+
+ private const string _tspecials = "()<>@,;:\\\"/[]?={} \t";
+
+ #endregion
+
+ #region Private Methods
+
+ private static MemoryStream compress(this Stream stream)
+ {
+ var output = new MemoryStream();
+ if (stream.Length == 0)
+ return output;
+
+ stream.Position = 0;
+ using (var ds = new DeflateStream(output, CompressionMode.Compress, true))
+ {
+ stream.CopyTo(ds);
+ //ds.Close(); // "BFINAL" set to 1.
+ output.Position = 0;
+
+ return output;
+ }
+ }
+
+ private static byte[] decompress(this byte[] value)
+ {
+ if (value.Length == 0)
+ return value;
+
+ using (var input = new MemoryStream(value))
+ {
+ return input.decompressToArray();
+ }
+ }
+
+ private static MemoryStream decompress(this Stream stream)
+ {
+ var output = new MemoryStream();
+ if (stream.Length == 0)
+ return output;
+
+ stream.Position = 0;
+ using (var ds = new DeflateStream(stream, CompressionMode.Decompress, true))
+ {
+ ds.CopyTo(output, true);
+ return output;
+ }
+ }
+
+ private static byte[] decompressToArray(this Stream stream)
+ {
+ using (var decomp = stream.decompress())
+ {
+ return decomp.ToArray();
+ }
+ }
+
+ private static byte[] readBytes(this Stream stream, byte[] buffer, int offset, int length)
+ {
+ var len = stream.Read(buffer, offset, length);
+ if (len < 1)
+ return buffer.SubArray(0, offset);
+
+ var tmp = 0;
+ while (len < length)
+ {
+ tmp = stream.Read(buffer, offset + len, length - len);
+ if (tmp < 1)
+ break;
+
+ len += tmp;
+ }
+
+ return len < length
+ ? buffer.SubArray(0, offset + len)
+ : buffer;
+ }
+
+ private static bool readBytes(
+ this Stream stream, byte[] buffer, int offset, int length, Stream dest)
+ {
+ var bytes = stream.readBytes(buffer, offset, length);
+ var len = bytes.Length;
+ dest.Write(bytes, 0, len);
+
+ return len == offset + length;
+ }
+
+ #endregion
+
+ #region Internal Methods
+
+ internal static byte[] Append(this ushort code, string reason)
+ {
+ using (var buffer = new MemoryStream())
+ {
+ var tmp = code.ToByteArrayInternally(ByteOrder.Big);
+ buffer.Write(tmp, 0, 2);
+ if (reason != null && reason.Length > 0)
+ {
+ tmp = Encoding.UTF8.GetBytes(reason);
+ buffer.Write(tmp, 0, tmp.Length);
+ }
+
+ return buffer.ToArray();
+ }
+ }
+
+ internal static string CheckIfClosable(this WebSocketState state)
+ {
+ return state == WebSocketState.Closing
+ ? "While closing the WebSocket connection."
+ : state == WebSocketState.Closed
+ ? "The WebSocket connection has already been closed."
+ : null;
+ }
+
+ internal static string CheckIfOpen(this WebSocketState state)
+ {
+ return state == WebSocketState.Connecting
+ ? "A WebSocket connection isn't established."
+ : state == WebSocketState.Closing
+ ? "While closing the WebSocket connection."
+ : state == WebSocketState.Closed
+ ? "The WebSocket connection has already been closed."
+ : null;
+ }
+
+ internal static string CheckIfValidControlData(this byte[] data, string paramName)
+ {
+ return data.Length > 125
+ ? String.Format("'{0}' length must be less.", paramName)
+ : 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
+ ? stream.compress()
+ : stream;
+ }
+
+ internal static bool Contains<T>(this IEnumerable<T> source, Func<T, bool> condition)
+ {
+ foreach (T elm in source)
+ if (condition(elm))
+ return true;
+
+ return false;
+ }
+
+ internal static void CopyTo(this Stream src, Stream dest, bool setDefaultPosition)
+ {
+ var readLen = 0;
+ var bufferLen = 256;
+ var buffer = new byte[bufferLen];
+ while ((readLen = src.Read(buffer, 0, bufferLen)) > 0)
+ {
+ dest.Write(buffer, 0, readLen);
+ }
+
+ if (setDefaultPosition)
+ dest.Position = 0;
+ }
+
+ internal static byte[] Decompress(this byte[] value, CompressionMethod method)
+ {
+ return method == CompressionMethod.Deflate
+ ? value.decompress()
+ : value;
+ }
+
+ internal static byte[] DecompressToArray(this Stream stream, CompressionMethod method)
+ {
+ return method == CompressionMethod.Deflate
+ ? stream.decompressToArray()
+ : stream.ToByteArray();
+ }
+
+ /// <summary>
+ /// Determines whether the specified <see cref="int"/> equals the specified <see cref="char"/>,
+ /// and invokes the specified Action&lt;int&gt; delegate at the same time.
+ /// </summary>
+ /// <returns>
+ /// <c>true</c> if <paramref name="value"/> equals <paramref name="c"/>;
+ /// otherwise, <c>false</c>.
+ /// </returns>
+ /// <param name="value">
+ /// An <see cref="int"/> to compare.
+ /// </param>
+ /// <param name="c">
+ /// A <see cref="char"/> to compare.
+ /// </param>
+ /// <param name="action">
+ /// An Action&lt;int&gt; delegate that references the method(s) called at
+ /// the same time as comparing. An <see cref="int"/> parameter to pass to
+ /// the method(s) is <paramref name="value"/>.
+ /// </param>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// <paramref name="value"/> isn't between 0 and 255.
+ /// </exception>
+ internal static bool EqualsWith(this int value, char c, Action<int> action)
+ {
+ if (value < 0 || value > 255)
+ throw new ArgumentOutOfRangeException("value");
+
+ action(value);
+ return value == c - 0;
+ }
+
+ internal static string GetMessage(this CloseStatusCode code)
+ {
+ return code == CloseStatusCode.ProtocolError
+ ? "A WebSocket protocol error has occurred."
+ : code == CloseStatusCode.IncorrectData
+ ? "An incorrect data has been received."
+ : code == CloseStatusCode.Abnormal
+ ? "An exception has occurred."
+ : code == CloseStatusCode.InconsistentData
+ ? "An inconsistent data has been received."
+ : code == CloseStatusCode.PolicyViolation
+ ? "A policy violation has occurred."
+ : code == CloseStatusCode.TooBig
+ ? "A too big data has been received."
+ : code == CloseStatusCode.IgnoreExtension
+ ? "WebSocket client did not receive expected extension(s)."
+ : code == CloseStatusCode.ServerError
+ ? "WebSocket server got an internal error."
+ : code == CloseStatusCode.TlsHandshakeFailure
+ ? "An error has occurred while handshaking."
+ : String.Empty;
+ }
+
+ internal static string GetNameInternal(this string nameAndValue, string separator)
+ {
+ var i = nameAndValue.IndexOf(separator);
+ return i > 0
+ ? nameAndValue.Substring(0, i).Trim()
+ : null;
+ }
+
+ internal static string GetValueInternal(this string nameAndValue, string separator)
+ {
+ var i = nameAndValue.IndexOf(separator);
+ return i >= 0 && i < nameAndValue.Length - 1
+ ? nameAndValue.Substring(i + 1).Trim()
+ : null;
+ }
+
+ internal static bool IsCompressionExtension(this string value, CompressionMethod method)
+ {
+ return value.StartsWith(method.ToExtensionString());
+ }
+
+ internal static bool IsPortNumber(this int value)
+ {
+ return value > 0 && value < 65536;
+ }
+
+ internal static bool IsReserved(this ushort code)
+ {
+ return code == (ushort)CloseStatusCode.Undefined ||
+ code == (ushort)CloseStatusCode.NoStatusCode ||
+ code == (ushort)CloseStatusCode.Abnormal ||
+ code == (ushort)CloseStatusCode.TlsHandshakeFailure;
+ }
+
+ internal static bool IsReserved(this CloseStatusCode code)
+ {
+ return code == CloseStatusCode.Undefined ||
+ code == CloseStatusCode.NoStatusCode ||
+ code == CloseStatusCode.Abnormal ||
+ code == CloseStatusCode.TlsHandshakeFailure;
+ }
+
+ internal static bool IsText(this string value)
+ {
+ var len = value.Length;
+ for (var i = 0; i < len; i++)
+ {
+ char c = value[i];
+ if (c < 0x20 && !"\r\n\t".Contains(c))
+ return false;
+
+ if (c == 0x7f)
+ return false;
+
+ if (c == '\n' && ++i < len)
+ {
+ c = value[i];
+ if (!" \t".Contains(c))
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ internal static bool IsToken(this string value)
+ {
+ foreach (char c in value)
+ if (c < 0x20 || c >= 0x7f || _tspecials.Contains(c))
+ return false;
+
+ return true;
+ }
+
+ internal static string Quote(this string value)
+ {
+ return value.IsToken()
+ ? value
+ : String.Format("\"{0}\"", value.Replace("\"", "\\\""));
+ }
+
+ internal static byte[] ReadBytes(this Stream stream, int length)
+ {
+ return stream.readBytes(new byte[length], 0, length);
+ }
+
+ internal static byte[] ReadBytes(this Stream stream, long length, int bufferLength)
+ {
+ using (var result = new MemoryStream())
+ {
+ var count = length / bufferLength;
+ var rem = (int)(length % bufferLength);
+
+ var buffer = new byte[bufferLength];
+ var end = false;
+ for (long i = 0; i < count; i++)
+ {
+ if (!stream.readBytes(buffer, 0, bufferLength, result))
+ {
+ end = true;
+ break;
+ }
+ }
+
+ if (!end && rem > 0)
+ stream.readBytes(new byte[rem], 0, rem, result);
+
+ return result.ToArray();
+ }
+ }
+
+ internal static async Task<byte[]> ReadBytesAsync(this Stream stream, int length)
+ {
+ var buffer = new byte[length];
+
+ var len = await stream.ReadAsync(buffer, 0, length).ConfigureAwait(false);
+ var bytes = len < 1
+ ? new byte[0]
+ : len < length
+ ? stream.readBytes(buffer, len, length - len)
+ : buffer;
+
+ return bytes;
+ }
+
+ internal static string RemovePrefix(this string value, params string[] prefixes)
+ {
+ var i = 0;
+ foreach (var prefix in prefixes)
+ {
+ if (value.StartsWith(prefix))
+ {
+ i = prefix.Length;
+ break;
+ }
+ }
+
+ return i > 0
+ ? value.Substring(i)
+ : value;
+ }
+
+ internal static T[] Reverse<T>(this T[] array)
+ {
+ var len = array.Length;
+ T[] reverse = new T[len];
+
+ var end = len - 1;
+ for (var i = 0; i <= end; i++)
+ reverse[i] = array[end - i];
+
+ return reverse;
+ }
+
+ internal static IEnumerable<string> SplitHeaderValue(
+ this string value, params char[] separator)
+ {
+ var len = value.Length;
+ var separators = new string(separator);
+
+ var buffer = new StringBuilder(32);
+ var quoted = false;
+ var escaped = false;
+
+ char c;
+ for (var i = 0; i < len; i++)
+ {
+ c = value[i];
+ if (c == '"')
+ {
+ if (escaped)
+ escaped = !escaped;
+ else
+ quoted = !quoted;
+ }
+ else if (c == '\\')
+ {
+ if (i < len - 1 && value[i + 1] == '"')
+ escaped = true;
+ }
+ else if (separators.Contains(c))
+ {
+ if (!quoted)
+ {
+ yield return buffer.ToString();
+ buffer.Length = 0;
+
+ continue;
+ }
+ }
+ else {
+ }
+
+ buffer.Append(c);
+ }
+
+ if (buffer.Length > 0)
+ yield return buffer.ToString();
+ }
+
+ internal static byte[] ToByteArray(this Stream stream)
+ {
+ using (var output = new MemoryStream())
+ {
+ stream.Position = 0;
+ stream.CopyTo(output);
+
+ return output.ToArray();
+ }
+ }
+
+ internal static byte[] ToByteArrayInternally(this ushort value, ByteOrder order)
+ {
+ var bytes = BitConverter.GetBytes(value);
+ if (!order.IsHostOrder())
+ Array.Reverse(bytes);
+
+ return bytes;
+ }
+
+ internal static byte[] ToByteArrayInternally(this ulong value, ByteOrder order)
+ {
+ var bytes = BitConverter.GetBytes(value);
+ if (!order.IsHostOrder())
+ Array.Reverse(bytes);
+
+ return bytes;
+ }
+
+ internal static string ToExtensionString(
+ this CompressionMethod method, params string[] parameters)
+ {
+ if (method == CompressionMethod.None)
+ return String.Empty;
+
+ var m = String.Format("permessage-{0}", method.ToString().ToLower());
+ if (parameters == null || parameters.Length == 0)
+ return m;
+
+ return String.Format("{0}; {1}", m, parameters.ToString("; "));
+ }
+
+ internal static List<TSource> ToList<TSource>(this IEnumerable<TSource> source)
+ {
+ return new List<TSource>(source);
+ }
+
+ internal static ushort ToUInt16(this byte[] src, ByteOrder srcOrder)
+ {
+ return BitConverter.ToUInt16(src.ToHostOrder(srcOrder), 0);
+ }
+
+ internal static ulong ToUInt64(this byte[] src, ByteOrder srcOrder)
+ {
+ return BitConverter.ToUInt64(src.ToHostOrder(srcOrder), 0);
+ }
+
+ internal static string TrimEndSlash(this string value)
+ {
+ value = value.TrimEnd('/');
+ return value.Length > 0
+ ? value
+ : "/";
+ }
+
+ internal static string Unquote(this string value)
+ {
+ var start = value.IndexOf('\"');
+ var end = value.LastIndexOf('\"');
+ if (start < end)
+ value = value.Substring(start + 1, end - start - 1).Replace("\\\"", "\"");
+
+ return value.Trim();
+ }
+
+ internal static void WriteBytes(this Stream stream, byte[] value)
+ {
+ using (var src = new MemoryStream(value))
+ {
+ src.CopyTo(stream);
+ }
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ /// <summary>
+ /// Determines whether the specified <see cref="string"/> contains any of characters
+ /// in the specified array of <see cref="char"/>.
+ /// </summary>
+ /// <returns>
+ /// <c>true</c> if <paramref name="value"/> contains any of <paramref name="chars"/>;
+ /// otherwise, <c>false</c>.
+ /// </returns>
+ /// <param name="value">
+ /// A <see cref="string"/> to test.
+ /// </param>
+ /// <param name="chars">
+ /// An array of <see cref="char"/> that contains characters to find.
+ /// </param>
+ public static bool Contains(this string value, params char[] chars)
+ {
+ return chars == null || chars.Length == 0
+ ? true
+ : value == null || value.Length == 0
+ ? false
+ : value.IndexOfAny(chars) != -1;
+ }
+
+ /// <summary>
+ /// Determines whether the specified <see cref="QueryParamCollection"/> contains the entry
+ /// with the specified <paramref name="name"/>.
+ /// </summary>
+ /// <returns>
+ /// <c>true</c> if <paramref name="collection"/> contains the entry
+ /// with <paramref name="name"/>; otherwise, <c>false</c>.
+ /// </returns>
+ /// <param name="collection">
+ /// A <see cref="QueryParamCollection"/> to test.
+ /// </param>
+ /// <param name="name">
+ /// A <see cref="string"/> that represents the key of the entry to find.
+ /// </param>
+ public static bool Contains(this QueryParamCollection collection, string name)
+ {
+ return collection == null || collection.Count == 0
+ ? false
+ : collection[name] != null;
+ }
+
+ /// <summary>
+ /// Determines whether the specified <see cref="QueryParamCollection"/> contains the entry
+ /// with the specified both <paramref name="name"/> and <paramref name="value"/>.
+ /// </summary>
+ /// <returns>
+ /// <c>true</c> if <paramref name="collection"/> contains the entry
+ /// with both <paramref name="name"/> and <paramref name="value"/>;
+ /// otherwise, <c>false</c>.
+ /// </returns>
+ /// <param name="collection">
+ /// A <see cref="QueryParamCollection"/> to test.
+ /// </param>
+ /// <param name="name">
+ /// A <see cref="string"/> that represents the key of the entry to find.
+ /// </param>
+ /// <param name="value">
+ /// A <see cref="string"/> that represents the value of the entry to find.
+ /// </param>
+ public static bool Contains(this QueryParamCollection collection, string name, string value)
+ {
+ if (collection == null || collection.Count == 0)
+ return false;
+
+ var values = collection[name];
+ if (values == null)
+ return false;
+
+ foreach (var v in values.Split(','))
+ if (v.Trim().Equals(value, StringComparison.OrdinalIgnoreCase))
+ return true;
+
+ return false;
+ }
+
+ /// <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>
+ /// <param name="eventHandler">
+ /// An <c>EventHandler&lt;TEventArgs&gt;</c> to emit.
+ /// </param>
+ /// <param name="sender">
+ /// An <see cref="object"/> from which emits this <paramref name="eventHandler"/>.
+ /// </param>
+ /// <param name="e">
+ /// A <c>TEventArgs</c> that represents the event data.
+ /// </param>
+ /// <typeparam name="TEventArgs">
+ /// The type of the event data generated by the event.
+ /// </typeparam>
+ public static void Emit<TEventArgs>(
+ this EventHandler<TEventArgs> eventHandler, object sender, TEventArgs e)
+ where TEventArgs : EventArgs
+ {
+ if (eventHandler != null)
+ eventHandler(sender, e);
+ }
+
+ /// <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>
+ /// A <see cref="string"/> that represents the description of the HTTP status code.
+ /// </returns>
+ /// <param name="code">
+ /// One of <see cref="HttpStatusCode"/> enum values, indicates the HTTP status codes.
+ /// </param>
+ public static string GetDescription(this HttpStatusCode code)
+ {
+ return ((int)code).GetStatusDescription();
+ }
+
+ /// <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>
+ /// A <see cref="string"/> that represents the description of the HTTP status code.
+ /// </returns>
+ /// <param name="code">
+ /// An <see cref="int"/> that represents the HTTP status code.
+ /// </param>
+ public static string GetStatusDescription(this int code)
+ {
+ switch (code)
+ {
+ case 100: return "Continue";
+ case 101: return "Switching Protocols";
+ case 102: return "Processing";
+ case 200: return "OK";
+ case 201: return "Created";
+ case 202: return "Accepted";
+ case 203: return "Non-Authoritative Information";
+ case 204: return "No Content";
+ case 205: return "Reset Content";
+ case 206: return "Partial Content";
+ case 207: return "Multi-Status";
+ case 300: return "Multiple Choices";
+ case 301: return "Moved Permanently";
+ case 302: return "Found";
+ case 303: return "See Other";
+ case 304: return "Not Modified";
+ case 305: return "Use Proxy";
+ case 307: return "Temporary Redirect";
+ case 400: return "Bad Request";
+ case 401: return "Unauthorized";
+ case 402: return "Payment Required";
+ case 403: return "Forbidden";
+ case 404: return "Not Found";
+ case 405: return "Method Not Allowed";
+ case 406: return "Not Acceptable";
+ case 407: return "Proxy Authentication Required";
+ case 408: return "Request Timeout";
+ case 409: return "Conflict";
+ case 410: return "Gone";
+ case 411: return "Length Required";
+ case 412: return "Precondition Failed";
+ case 413: return "Request Entity Too Large";
+ case 414: return "Request-Uri Too Long";
+ case 415: return "Unsupported Media Type";
+ case 416: return "Requested Range Not Satisfiable";
+ case 417: return "Expectation Failed";
+ case 422: return "Unprocessable Entity";
+ case 423: return "Locked";
+ case 424: return "Failed Dependency";
+ case 500: return "Internal Server Error";
+ case 501: return "Not Implemented";
+ case 502: return "Bad Gateway";
+ case 503: return "Service Unavailable";
+ case 504: return "Gateway Timeout";
+ case 505: return "Http Version Not Supported";
+ case 507: return "Insufficient Storage";
+ }
+
+ return String.Empty;
+ }
+
+ /// <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>
+ /// <returns>
+ /// <c>true</c> if <paramref name="order"/> is host byte order;
+ /// otherwise, <c>false</c>.
+ /// </returns>
+ /// <param name="order">
+ /// One of the <see cref="ByteOrder"/> enum values, to test.
+ /// </param>
+ public static bool IsHostOrder(this ByteOrder order)
+ {
+ // true : !(true ^ true) or !(false ^ false)
+ // false: !(true ^ false) or !(false ^ true)
+ return !(BitConverter.IsLittleEndian ^ (order == ByteOrder.Little));
+ }
+
+ /// <summary>
+ /// Determines whether the specified <see cref="string"/> is a predefined scheme.
+ /// </summary>
+ /// <returns>
+ /// <c>true</c> if <paramref name="value"/> is a predefined scheme; otherwise, <c>false</c>.
+ /// </returns>
+ /// <param name="value">
+ /// A <see cref="string"/> to test.
+ /// </param>
+ public static bool IsPredefinedScheme(this string value)
+ {
+ if (value == null || value.Length < 2)
+ return false;
+
+ var c = value[0];
+ if (c == 'h')
+ return value == "http" || value == "https";
+
+ if (c == 'w')
+ return value == "ws" || value == "wss";
+
+ if (c == 'f')
+ return value == "file" || value == "ftp";
+
+ if (c == 'n')
+ {
+ c = value[1];
+ return c == 'e'
+ ? value == "news" || value == "net.pipe" || value == "net.tcp"
+ : value == "nntp";
+ }
+
+ return (c == 'g' && value == "gopher") || (c == 'm' && value == "mailto");
+ }
+
+ /// <summary>
+ /// Determines whether the specified <see cref="string"/> is a URI string.
+ /// </summary>
+ /// <returns>
+ /// <c>true</c> if <paramref name="value"/> may be a URI string; otherwise, <c>false</c>.
+ /// </returns>
+ /// <param name="value">
+ /// A <see cref="string"/> to test.
+ /// </param>
+ public static bool MaybeUri(this string value)
+ {
+ if (value == null || value.Length == 0)
+ return false;
+
+ var i = value.IndexOf(':');
+ if (i == -1)
+ return false;
+
+ if (i >= 10)
+ return false;
+
+ return value.Substring(0, i).IsPredefinedScheme();
+ }
+
+ /// <summary>
+ /// Retrieves a sub-array from the specified <paramref name="array"/>.
+ /// A sub-array starts at the specified element position.
+ /// </summary>
+ /// <returns>
+ /// An array of T that receives a sub-array, or an empty array of T if any problems
+ /// with the parameters.
+ /// </returns>
+ /// <param name="array">
+ /// An array of T that contains the data to retrieve a sub-array.
+ /// </param>
+ /// <param name="startIndex">
+ /// An <see cref="int"/> that contains the zero-based starting position of a sub-array
+ /// in <paramref name="array"/>.
+ /// </param>
+ /// <param name="length">
+ /// An <see cref="int"/> that contains the number of elements to retrieve a sub-array.
+ /// </param>
+ /// <typeparam name="T">
+ /// The type of elements in the <paramref name="array"/>.
+ /// </typeparam>
+ public static T[] SubArray<T>(this T[] array, int startIndex, int length)
+ {
+ if (array == null || array.Length == 0)
+ return new T[0];
+
+ if (startIndex < 0 || length <= 0)
+ return new T[0];
+
+ if (startIndex + length > array.Length)
+ return new T[0];
+
+ if (startIndex == 0 && array.Length == length)
+ return array;
+
+ T[] subArray = new T[length];
+ Array.Copy(array, startIndex, subArray, 0, length);
+
+ return subArray;
+ }
+
+ /// <summary>
+ /// Converts the order of the specified array of <see cref="byte"/> to the host byte order.
+ /// </summary>
+ /// <returns>
+ /// An array of <see cref="byte"/> converted from <paramref name="src"/>.
+ /// </returns>
+ /// <param name="src">
+ /// An array of <see cref="byte"/> to convert.
+ /// </param>
+ /// <param name="srcOrder">
+ /// One of the <see cref="ByteOrder"/> enum values, indicates the byte order of
+ /// <paramref name="src"/>.
+ /// </param>
+ /// <exception cref="ArgumentNullException">
+ /// <paramref name="src"/> is <see langword="null"/>.
+ /// </exception>
+ public static byte[] ToHostOrder(this byte[] src, ByteOrder srcOrder)
+ {
+ if (src == null)
+ throw new ArgumentNullException("src");
+
+ return src.Length > 1 && !srcOrder.IsHostOrder()
+ ? src.Reverse()
+ : src;
+ }
+
+ /// <summary>
+ /// Converts the specified <paramref name="array"/> to a <see cref="string"/> that
+ /// concatenates the each element of <paramref name="array"/> across the specified
+ /// <paramref name="separator"/>.
+ /// </summary>
+ /// <returns>
+ /// A <see cref="string"/> converted from <paramref name="array"/>,
+ /// or <see cref="String.Empty"/> if <paramref name="array"/> is empty.
+ /// </returns>
+ /// <param name="array">
+ /// An array of T to convert.
+ /// </param>
+ /// <param name="separator">
+ /// A <see cref="string"/> that represents the separator string.
+ /// </param>
+ /// <typeparam name="T">
+ /// The type of elements in <paramref name="array"/>.
+ /// </typeparam>
+ /// <exception cref="ArgumentNullException">
+ /// <paramref name="array"/> is <see langword="null"/>.
+ /// </exception>
+ public static string ToString<T>(this T[] array, string separator)
+ {
+ if (array == null)
+ throw new ArgumentNullException("array");
+
+ var len = array.Length;
+ if (len == 0)
+ return String.Empty;
+
+ if (separator == null)
+ separator = String.Empty;
+
+ var buff = new StringBuilder(64);
+ (len - 1).Times(i => buff.AppendFormat("{0}{1}", array[i].ToString(), separator));
+
+ buff.Append(array[len - 1].ToString());
+ return buff.ToString();
+ }
+
+ /// <summary>
+ /// Executes the specified <c>Action&lt;int&gt;</c> delegate <paramref name="n"/> times.
+ /// </summary>
+ /// <param name="n">
+ /// An <see cref="int"/> is the number of times to execute.
+ /// </param>
+ /// <param name="action">
+ /// An <c>Action&lt;int&gt;</c> delegate that references the method(s) to execute.
+ /// An <see cref="int"/> parameter to pass to the method(s) is the zero-based count of
+ /// iteration.
+ /// </param>
+ public static void Times(this int n, Action<int> action)
+ {
+ if (n > 0 && action != null)
+ for (int i = 0; i < n; i++)
+ action(i);
+ }
+
+ /// <summary>
+ /// Converts the specified <see cref="string"/> to a <see cref="Uri"/>.
+ /// </summary>
+ /// <returns>
+ /// A <see cref="Uri"/> converted from <paramref name="uriString"/>, or <see langword="null"/>
+ /// if <paramref name="uriString"/> isn't successfully converted.
+ /// </returns>
+ /// <param name="uriString">
+ /// A <see cref="string"/> to convert.
+ /// </param>
+ public static Uri ToUri(this string uriString)
+ {
+ Uri res;
+ return Uri.TryCreate(
+ uriString, uriString.MaybeUri() ? UriKind.Absolute : UriKind.Relative, out res)
+ ? res
+ : null;
+ }
+
+ /// <summary>
+ /// URL-decodes the specified <see cref="string"/>.
+ /// </summary>
+ /// <returns>
+ /// A <see cref="string"/> that receives the decoded string, or the <paramref name="value"/>
+ /// if it's <see langword="null"/> or empty.
+ /// </returns>
+ /// <param name="value">
+ /// A <see cref="string"/> to decode.
+ /// </param>
+ public static string UrlDecode(this string value)
+ {
+ return value == null || value.Length == 0
+ ? value
+ : WebUtility.UrlDecode(value);
+ }
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/SocketHttpListener.Portable/Fin.cs b/SocketHttpListener.Portable/Fin.cs
new file mode 100644
index 0000000000..f91401b995
--- /dev/null
+++ b/SocketHttpListener.Portable/Fin.cs
@@ -0,0 +1,8 @@
+namespace SocketHttpListener
+{
+ internal enum Fin : byte
+ {
+ More = 0x0,
+ Final = 0x1
+ }
+}
diff --git a/SocketHttpListener.Portable/HttpBase.cs b/SocketHttpListener.Portable/HttpBase.cs
new file mode 100644
index 0000000000..5172ba4975
--- /dev/null
+++ b/SocketHttpListener.Portable/HttpBase.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.IO;
+using System.Text;
+using System.Threading;
+using MediaBrowser.Model.Services;
+
+namespace SocketHttpListener
+{
+ internal abstract class HttpBase
+ {
+ #region Private Fields
+
+ private QueryParamCollection _headers;
+ private Version _version;
+
+ #endregion
+
+ #region Internal Fields
+
+ internal byte[] EntityBodyData;
+
+ #endregion
+
+ #region Protected Fields
+
+ protected const string CrLf = "\r\n";
+
+ #endregion
+
+ #region Protected Constructors
+
+ protected HttpBase(Version version, QueryParamCollection headers)
+ {
+ _version = version;
+ _headers = headers;
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ public string EntityBody
+ {
+ get
+ {
+ var data = EntityBodyData;
+
+ return data != null && data.Length > 0
+ ? getEncoding(_headers["Content-Type"]).GetString(data, 0, data.Length)
+ : String.Empty;
+ }
+ }
+
+ public QueryParamCollection Headers
+ {
+ get
+ {
+ return _headers;
+ }
+ }
+
+ public Version ProtocolVersion
+ {
+ get
+ {
+ return _version;
+ }
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private static Encoding getEncoding(string contentType)
+ {
+ if (contentType == null || contentType.Length == 0)
+ return Encoding.UTF8;
+
+ var i = contentType.IndexOf("charset=", StringComparison.Ordinal);
+ if (i == -1)
+ return Encoding.UTF8;
+
+ var charset = contentType.Substring(i + 8);
+ i = charset.IndexOf(';');
+ if (i != -1)
+ charset = charset.Substring(0, i).TrimEnd();
+
+ return Encoding.GetEncoding(charset.Trim('"'));
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ public byte[] ToByteArray()
+ {
+ return Encoding.UTF8.GetBytes(ToString());
+ }
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/SocketHttpListener.Portable/HttpResponse.cs b/SocketHttpListener.Portable/HttpResponse.cs
new file mode 100644
index 0000000000..5aca28c7c3
--- /dev/null
+++ b/SocketHttpListener.Portable/HttpResponse.cs
@@ -0,0 +1,161 @@
+using System;
+using System.Collections.Specialized;
+using System.IO;
+using System.Net;
+using System.Text;
+using HttpStatusCode = SocketHttpListener.Net.HttpStatusCode;
+using HttpVersion = SocketHttpListener.Net.HttpVersion;
+using System.Linq;
+using MediaBrowser.Model.Services;
+
+namespace SocketHttpListener
+{
+ internal class HttpResponse : HttpBase
+ {
+ #region Private Fields
+
+ private string _code;
+ private string _reason;
+
+ #endregion
+
+ #region Private Constructors
+
+ private HttpResponse(string code, string reason, Version version, QueryParamCollection headers)
+ : base(version, headers)
+ {
+ _code = code;
+ _reason = reason;
+ }
+
+ #endregion
+
+ #region Internal Constructors
+
+ internal HttpResponse(HttpStatusCode code)
+ : this(code, code.GetDescription())
+ {
+ }
+
+ internal HttpResponse(HttpStatusCode code, string reason)
+ : this(((int)code).ToString(), reason, HttpVersion.Version11, new QueryParamCollection())
+ {
+ Headers["Server"] = "websocket-sharp/1.0";
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ public CookieCollection Cookies
+ {
+ get
+ {
+ return Headers.GetCookies(true);
+ }
+ }
+
+ public bool IsProxyAuthenticationRequired
+ {
+ get
+ {
+ return _code == "407";
+ }
+ }
+
+ public bool IsUnauthorized
+ {
+ get
+ {
+ return _code == "401";
+ }
+ }
+
+ public bool IsWebSocketResponse
+ {
+ get
+ {
+ var headers = Headers;
+ return ProtocolVersion > HttpVersion.Version10 &&
+ _code == "101" &&
+ headers.Contains("Upgrade", "websocket") &&
+ headers.Contains("Connection", "Upgrade");
+ }
+ }
+
+ public string Reason
+ {
+ get
+ {
+ return _reason;
+ }
+ }
+
+ public string StatusCode
+ {
+ get
+ {
+ return _code;
+ }
+ }
+
+ #endregion
+
+ #region Internal Methods
+
+ internal static HttpResponse CreateCloseResponse(HttpStatusCode code)
+ {
+ var res = new HttpResponse(code);
+ res.Headers["Connection"] = "close";
+
+ 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
+
+ public void SetCookies(CookieCollection cookies)
+ {
+ if (cookies == null || cookies.Count == 0)
+ return;
+
+ var headers = Headers;
+ var sorted = cookies.OfType<Cookie>().OrderBy(i => i.Name).ToList();
+
+ foreach (var cookie in sorted)
+ headers.Add("Set-Cookie", cookie.ToString());
+ }
+
+ public override string ToString()
+ {
+ var output = new StringBuilder(64);
+ output.AppendFormat("HTTP/{0} {1} {2}{3}", ProtocolVersion, _code, _reason, CrLf);
+
+ var headers = Headers;
+ foreach (var key in headers.Keys)
+ output.AppendFormat("{0}: {1}{2}", key, headers[key], CrLf);
+
+ output.Append(CrLf);
+
+ var entity = EntityBody;
+ if (entity.Length > 0)
+ output.Append(entity);
+
+ return output.ToString();
+ }
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/SocketHttpListener.Portable/Mask.cs b/SocketHttpListener.Portable/Mask.cs
new file mode 100644
index 0000000000..adc2f098e9
--- /dev/null
+++ b/SocketHttpListener.Portable/Mask.cs
@@ -0,0 +1,8 @@
+namespace SocketHttpListener
+{
+ internal enum Mask : byte
+ {
+ Unmask = 0x0,
+ Mask = 0x1
+ }
+}
diff --git a/SocketHttpListener.Portable/MessageEventArgs.cs b/SocketHttpListener.Portable/MessageEventArgs.cs
new file mode 100644
index 0000000000..9dbadb9ab2
--- /dev/null
+++ b/SocketHttpListener.Portable/MessageEventArgs.cs
@@ -0,0 +1,96 @@
+using System;
+using System.Text;
+
+namespace SocketHttpListener
+{
+ /// <summary>
+ /// Contains the event data associated with a <see cref="WebSocket.OnMessage"/> event.
+ /// </summary>
+ /// <remarks>
+ /// A <see cref="WebSocket.OnMessage"/> event occurs when the <see cref="WebSocket"/> receives
+ /// a text or binary data frame.
+ /// If you want to get the received data, you access the <see cref="MessageEventArgs.Data"/> or
+ /// <see cref="MessageEventArgs.RawData"/> property.
+ /// </remarks>
+ public class MessageEventArgs : EventArgs
+ {
+ #region Private Fields
+
+ private string _data;
+ private Opcode _opcode;
+ private byte[] _rawData;
+
+ #endregion
+
+ #region Internal Constructors
+
+ internal MessageEventArgs (Opcode opcode, byte[] data)
+ {
+ _opcode = opcode;
+ _rawData = data;
+ _data = convertToString (opcode, data);
+ }
+
+ internal MessageEventArgs (Opcode opcode, PayloadData payload)
+ {
+ _opcode = opcode;
+ _rawData = payload.ApplicationData;
+ _data = convertToString (opcode, _rawData);
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ /// <summary>
+ /// Gets the received data as a <see cref="string"/>.
+ /// </summary>
+ /// <value>
+ /// A <see cref="string"/> that contains the received data.
+ /// </value>
+ public string Data {
+ get {
+ return _data;
+ }
+ }
+
+ /// <summary>
+ /// Gets the received data as an array of <see cref="byte"/>.
+ /// </summary>
+ /// <value>
+ /// An array of <see cref="byte"/> that contains the received data.
+ /// </value>
+ public byte [] RawData {
+ get {
+ return _rawData;
+ }
+ }
+
+ /// <summary>
+ /// Gets the type of the received data.
+ /// </summary>
+ /// <value>
+ /// One of the <see cref="Opcode"/> values, indicates the type of the received data.
+ /// </value>
+ public Opcode Type {
+ get {
+ return _opcode;
+ }
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private static string convertToString (Opcode opcode, byte [] data)
+ {
+ return data.Length == 0
+ ? String.Empty
+ : opcode == Opcode.Text
+ ? Encoding.UTF8.GetString (data, 0, data.Length)
+ : opcode.ToString ();
+ }
+
+ #endregion
+ }
+}
diff --git a/SocketHttpListener.Portable/Net/AuthenticationSchemeSelector.cs b/SocketHttpListener.Portable/Net/AuthenticationSchemeSelector.cs
new file mode 100644
index 0000000000..c6e7e538ec
--- /dev/null
+++ b/SocketHttpListener.Portable/Net/AuthenticationSchemeSelector.cs
@@ -0,0 +1,6 @@
+using System.Net;
+
+namespace SocketHttpListener.Net
+{
+ public delegate AuthenticationSchemes AuthenticationSchemeSelector(HttpListenerRequest httpRequest);
+}
diff --git a/SocketHttpListener.Portable/Net/ChunkStream.cs b/SocketHttpListener.Portable/Net/ChunkStream.cs
new file mode 100644
index 0000000000..3f3b4a667a
--- /dev/null
+++ b/SocketHttpListener.Portable/Net/ChunkStream.cs
@@ -0,0 +1,371 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Net;
+using System.Text;
+
+namespace SocketHttpListener.Net
+{
+ class ChunkStream
+ {
+ enum State
+ {
+ None,
+ PartialSize,
+ Body,
+ BodyFinished,
+ Trailer
+ }
+
+ class Chunk
+ {
+ public byte[] Bytes;
+ public int Offset;
+
+ public Chunk(byte[] chunk)
+ {
+ this.Bytes = chunk;
+ }
+
+ public int Read(byte[] buffer, int offset, int size)
+ {
+ int nread = (size > Bytes.Length - Offset) ? Bytes.Length - Offset : size;
+ Buffer.BlockCopy(Bytes, Offset, buffer, offset, nread);
+ Offset += nread;
+ return nread;
+ }
+ }
+
+ internal WebHeaderCollection headers;
+ int chunkSize;
+ int chunkRead;
+ int totalWritten;
+ State state;
+ //byte [] waitBuffer;
+ StringBuilder saved;
+ bool sawCR;
+ bool gotit;
+ int trailerState;
+ List<Chunk> chunks;
+
+ public ChunkStream(WebHeaderCollection headers)
+ {
+ this.headers = headers;
+ saved = new StringBuilder();
+ chunks = new List<Chunk>();
+ chunkSize = -1;
+ totalWritten = 0;
+ }
+
+ public void ResetBuffer()
+ {
+ chunkSize = -1;
+ chunkRead = 0;
+ totalWritten = 0;
+ chunks.Clear();
+ }
+
+ public void WriteAndReadBack(byte[] buffer, int offset, int size, ref int read)
+ {
+ if (offset + read > 0)
+ Write(buffer, offset, offset + read);
+ read = Read(buffer, offset, size);
+ }
+
+ public int Read(byte[] buffer, int offset, int size)
+ {
+ return ReadFromChunks(buffer, offset, size);
+ }
+
+ int ReadFromChunks(byte[] buffer, int offset, int size)
+ {
+ int count = chunks.Count;
+ int nread = 0;
+
+ var chunksForRemoving = new List<Chunk>(count);
+ for (int i = 0; i < count; i++)
+ {
+ Chunk chunk = (Chunk)chunks[i];
+
+ if (chunk.Offset == chunk.Bytes.Length)
+ {
+ chunksForRemoving.Add(chunk);
+ continue;
+ }
+
+ nread += chunk.Read(buffer, offset + nread, size - nread);
+ if (nread == size)
+ break;
+ }
+
+ foreach (var chunk in chunksForRemoving)
+ chunks.Remove(chunk);
+
+ return nread;
+ }
+
+ public void Write(byte[] buffer, int offset, int size)
+ {
+ if (offset < size)
+ InternalWrite(buffer, ref offset, size);
+ }
+
+ void InternalWrite(byte[] buffer, ref int offset, int size)
+ {
+ if (state == State.None || state == State.PartialSize)
+ {
+ state = GetChunkSize(buffer, ref offset, size);
+ if (state == State.PartialSize)
+ return;
+
+ saved.Length = 0;
+ sawCR = false;
+ gotit = false;
+ }
+
+ if (state == State.Body && offset < size)
+ {
+ state = ReadBody(buffer, ref offset, size);
+ if (state == State.Body)
+ return;
+ }
+
+ if (state == State.BodyFinished && offset < size)
+ {
+ state = ReadCRLF(buffer, ref offset, size);
+ if (state == State.BodyFinished)
+ return;
+
+ sawCR = false;
+ }
+
+ if (state == State.Trailer && offset < size)
+ {
+ state = ReadTrailer(buffer, ref offset, size);
+ if (state == State.Trailer)
+ return;
+
+ saved.Length = 0;
+ sawCR = false;
+ gotit = false;
+ }
+
+ if (offset < size)
+ InternalWrite(buffer, ref offset, size);
+ }
+
+ public bool WantMore
+ {
+ get { return (chunkRead != chunkSize || chunkSize != 0 || state != State.None); }
+ }
+
+ public bool DataAvailable
+ {
+ get
+ {
+ int count = chunks.Count;
+ for (int i = 0; i < count; i++)
+ {
+ Chunk ch = (Chunk)chunks[i];
+ if (ch == null || ch.Bytes == null)
+ continue;
+ if (ch.Bytes.Length > 0 && ch.Offset < ch.Bytes.Length)
+ return (state != State.Body);
+ }
+ return false;
+ }
+ }
+
+ public int TotalDataSize
+ {
+ get { return totalWritten; }
+ }
+
+ public int ChunkLeft
+ {
+ get { return chunkSize - chunkRead; }
+ }
+
+ State ReadBody(byte[] buffer, ref int offset, int size)
+ {
+ if (chunkSize == 0)
+ return State.BodyFinished;
+
+ int diff = size - offset;
+ if (diff + chunkRead > chunkSize)
+ diff = chunkSize - chunkRead;
+
+ byte[] chunk = new byte[diff];
+ Buffer.BlockCopy(buffer, offset, chunk, 0, diff);
+ chunks.Add(new Chunk(chunk));
+ offset += diff;
+ chunkRead += diff;
+ totalWritten += diff;
+ return (chunkRead == chunkSize) ? State.BodyFinished : State.Body;
+
+ }
+
+ State GetChunkSize(byte[] buffer, ref int offset, int size)
+ {
+ chunkRead = 0;
+ chunkSize = 0;
+ char c = '\0';
+ while (offset < size)
+ {
+ c = (char)buffer[offset++];
+ if (c == '\r')
+ {
+ if (sawCR)
+ ThrowProtocolViolation("2 CR found");
+
+ sawCR = true;
+ continue;
+ }
+
+ if (sawCR && c == '\n')
+ break;
+
+ if (c == ' ')
+ gotit = true;
+
+ if (!gotit)
+ saved.Append(c);
+
+ if (saved.Length > 20)
+ ThrowProtocolViolation("chunk size too long.");
+ }
+
+ if (!sawCR || c != '\n')
+ {
+ if (offset < size)
+ ThrowProtocolViolation("Missing \\n");
+
+ try
+ {
+ if (saved.Length > 0)
+ {
+ chunkSize = Int32.Parse(RemoveChunkExtension(saved.ToString()), NumberStyles.HexNumber);
+ }
+ }
+ catch (Exception)
+ {
+ ThrowProtocolViolation("Cannot parse chunk size.");
+ }
+
+ return State.PartialSize;
+ }
+
+ chunkRead = 0;
+ try
+ {
+ chunkSize = Int32.Parse(RemoveChunkExtension(saved.ToString()), NumberStyles.HexNumber);
+ }
+ catch (Exception)
+ {
+ ThrowProtocolViolation("Cannot parse chunk size.");
+ }
+
+ if (chunkSize == 0)
+ {
+ trailerState = 2;
+ return State.Trailer;
+ }
+
+ return State.Body;
+ }
+
+ static string RemoveChunkExtension(string input)
+ {
+ int idx = input.IndexOf(';');
+ if (idx == -1)
+ return input;
+ return input.Substring(0, idx);
+ }
+
+ State ReadCRLF(byte[] buffer, ref int offset, int size)
+ {
+ if (!sawCR)
+ {
+ if ((char)buffer[offset++] != '\r')
+ ThrowProtocolViolation("Expecting \\r");
+
+ sawCR = true;
+ if (offset == size)
+ return State.BodyFinished;
+ }
+
+ if (sawCR && (char)buffer[offset++] != '\n')
+ ThrowProtocolViolation("Expecting \\n");
+
+ return State.None;
+ }
+
+ State ReadTrailer(byte[] buffer, ref int offset, int size)
+ {
+ char c = '\0';
+
+ // short path
+ if (trailerState == 2 && (char)buffer[offset] == '\r' && saved.Length == 0)
+ {
+ offset++;
+ if (offset < size && (char)buffer[offset] == '\n')
+ {
+ offset++;
+ return State.None;
+ }
+ offset--;
+ }
+
+ int st = trailerState;
+ string stString = "\r\n\r";
+ while (offset < size && st < 4)
+ {
+ c = (char)buffer[offset++];
+ if ((st == 0 || st == 2) && c == '\r')
+ {
+ st++;
+ continue;
+ }
+
+ if ((st == 1 || st == 3) && c == '\n')
+ {
+ st++;
+ continue;
+ }
+
+ if (st > 0)
+ {
+ saved.Append(stString.Substring(0, saved.Length == 0 ? st - 2 : st));
+ st = 0;
+ if (saved.Length > 4196)
+ ThrowProtocolViolation("Error reading trailer (too long).");
+ }
+ }
+
+ if (st < 4)
+ {
+ trailerState = st;
+ if (offset < size)
+ ThrowProtocolViolation("Error reading trailer.");
+
+ return State.Trailer;
+ }
+
+ StringReader reader = new StringReader(saved.ToString());
+ string line;
+ while ((line = reader.ReadLine()) != null && line != "")
+ headers.Add(line);
+
+ return State.None;
+ }
+
+ static void ThrowProtocolViolation(string message)
+ {
+ WebException we = new WebException(message, null, WebExceptionStatus.UnknownError, null);
+ //WebException we = new WebException(message, null, WebExceptionStatus.ServerProtocolViolation, null);
+ throw we;
+ }
+ }
+}
diff --git a/SocketHttpListener.Portable/Net/ChunkedInputStream.cs b/SocketHttpListener.Portable/Net/ChunkedInputStream.cs
new file mode 100644
index 0000000000..6dfd8d8a1d
--- /dev/null
+++ b/SocketHttpListener.Portable/Net/ChunkedInputStream.cs
@@ -0,0 +1,160 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using SocketHttpListener.Primitives;
+
+namespace SocketHttpListener.Net
+{
+ class ChunkedInputStream : RequestStream
+ {
+ bool disposed;
+ ChunkStream decoder;
+ HttpListenerContext context;
+ bool no_more_data;
+
+ //class ReadBufferState
+ //{
+ // public byte[] Buffer;
+ // public int Offset;
+ // public int Count;
+ // public int InitialCount;
+ // public HttpStreamAsyncResult Ares;
+ // public ReadBufferState(byte[] buffer, int offset, int count,
+ // HttpStreamAsyncResult ares)
+ // {
+ // Buffer = buffer;
+ // Offset = offset;
+ // Count = count;
+ // InitialCount = count;
+ // Ares = ares;
+ // }
+ //}
+
+ public ChunkedInputStream(HttpListenerContext context, Stream stream,
+ byte[] buffer, int offset, int length)
+ : base(stream, buffer, offset, length)
+ {
+ this.context = context;
+ WebHeaderCollection coll = (WebHeaderCollection)context.Request.Headers;
+ decoder = new ChunkStream(coll);
+ }
+
+ //public ChunkStream Decoder
+ //{
+ // get { return decoder; }
+ // set { decoder = value; }
+ //}
+
+ //public override int Read([In, Out] byte[] buffer, int offset, int count)
+ //{
+ // IAsyncResult ares = BeginRead(buffer, offset, count, null, null);
+ // return EndRead(ares);
+ //}
+
+ //public override IAsyncResult BeginRead(byte[] buffer, int offset, int count,
+ // AsyncCallback cback, object state)
+ //{
+ // if (disposed)
+ // throw new ObjectDisposedException(GetType().ToString());
+
+ // if (buffer == null)
+ // throw new ArgumentNullException("buffer");
+
+ // int len = buffer.Length;
+ // if (offset < 0 || offset > len)
+ // throw new ArgumentOutOfRangeException("offset exceeds the size of buffer");
+
+ // if (count < 0 || offset > len - count)
+ // throw new ArgumentOutOfRangeException("offset+size exceeds the size of buffer");
+
+ // HttpStreamAsyncResult ares = new HttpStreamAsyncResult();
+ // ares.Callback = cback;
+ // ares.State = state;
+ // if (no_more_data)
+ // {
+ // ares.Complete();
+ // return ares;
+ // }
+ // int nread = decoder.Read(buffer, offset, count);
+ // offset += nread;
+ // count -= nread;
+ // if (count == 0)
+ // {
+ // // got all we wanted, no need to bother the decoder yet
+ // ares.Count = nread;
+ // ares.Complete();
+ // return ares;
+ // }
+ // if (!decoder.WantMore)
+ // {
+ // no_more_data = nread == 0;
+ // ares.Count = nread;
+ // ares.Complete();
+ // return ares;
+ // }
+ // ares.Buffer = new byte[8192];
+ // ares.Offset = 0;
+ // ares.Count = 8192;
+ // ReadBufferState rb = new ReadBufferState(buffer, offset, count, ares);
+ // rb.InitialCount += nread;
+ // base.BeginRead(ares.Buffer, ares.Offset, ares.Count, OnRead, rb);
+ // return ares;
+ //}
+
+ //void OnRead(IAsyncResult base_ares)
+ //{
+ // ReadBufferState rb = (ReadBufferState)base_ares.AsyncState;
+ // HttpStreamAsyncResult ares = rb.Ares;
+ // try
+ // {
+ // int nread = base.EndRead(base_ares);
+ // decoder.Write(ares.Buffer, ares.Offset, nread);
+ // nread = decoder.Read(rb.Buffer, rb.Offset, rb.Count);
+ // rb.Offset += nread;
+ // rb.Count -= nread;
+ // if (rb.Count == 0 || !decoder.WantMore || nread == 0)
+ // {
+ // no_more_data = !decoder.WantMore && nread == 0;
+ // ares.Count = rb.InitialCount - rb.Count;
+ // ares.Complete();
+ // return;
+ // }
+ // ares.Offset = 0;
+ // ares.Count = Math.Min(8192, decoder.ChunkLeft + 6);
+ // base.BeginRead(ares.Buffer, ares.Offset, ares.Count, OnRead, rb);
+ // }
+ // catch (Exception e)
+ // {
+ // context.Connection.SendError(e.Message, 400);
+ // ares.Complete(e);
+ // }
+ //}
+
+ //public override int EndRead(IAsyncResult ares)
+ //{
+ // if (disposed)
+ // throw new ObjectDisposedException(GetType().ToString());
+
+ // HttpStreamAsyncResult my_ares = ares as HttpStreamAsyncResult;
+ // if (ares == null)
+ // throw new ArgumentException("Invalid IAsyncResult", "ares");
+
+ // if (!ares.IsCompleted)
+ // ares.AsyncWaitHandle.WaitOne();
+
+ // if (my_ares.Error != null)
+ // throw new HttpListenerException(400, "I/O operation aborted: " + my_ares.Error.Message);
+
+ // return my_ares.Count;
+ //}
+
+ //protected override void Dispose(bool disposing)
+ //{
+ // if (!disposed)
+ // {
+ // disposed = true;
+ // base.Dispose(disposing);
+ // }
+ //}
+ }
+}
diff --git a/SocketHttpListener.Portable/Net/CookieHelper.cs b/SocketHttpListener.Portable/Net/CookieHelper.cs
new file mode 100644
index 0000000000..470507d6b7
--- /dev/null
+++ b/SocketHttpListener.Portable/Net/CookieHelper.cs
@@ -0,0 +1,144 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Net;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SocketHttpListener.Net
+{
+ public static class CookieHelper
+ {
+ internal static CookieCollection Parse(string value, bool response)
+ {
+ return response
+ ? parseResponse(value)
+ : null;
+ }
+
+ private static string[] splitCookieHeaderValue(string value)
+ {
+ return new List<string>(value.SplitHeaderValue(',', ';')).ToArray();
+ }
+
+ private static CookieCollection parseResponse(string value)
+ {
+ var cookies = new CookieCollection();
+
+ Cookie cookie = null;
+ var pairs = splitCookieHeaderValue(value);
+ for (int i = 0; i < pairs.Length; i++)
+ {
+ var pair = pairs[i].Trim();
+ if (pair.Length == 0)
+ continue;
+
+ if (pair.StartsWith("version", StringComparison.OrdinalIgnoreCase))
+ {
+ if (cookie != null)
+ cookie.Version = Int32.Parse(pair.GetValueInternal("=").Trim('"'));
+ }
+ else if (pair.StartsWith("expires", StringComparison.OrdinalIgnoreCase))
+ {
+ var buffer = new StringBuilder(pair.GetValueInternal("="), 32);
+ if (i < pairs.Length - 1)
+ buffer.AppendFormat(", {0}", pairs[++i].Trim());
+
+ DateTime expires;
+ if (!DateTime.TryParseExact(
+ buffer.ToString(),
+ new[] { "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'", "r" },
+ new CultureInfo("en-US"),
+ DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal,
+ out expires))
+ expires = DateTime.Now;
+
+ if (cookie != null && cookie.Expires == DateTime.MinValue)
+ cookie.Expires = expires.ToLocalTime();
+ }
+ else if (pair.StartsWith("max-age", StringComparison.OrdinalIgnoreCase))
+ {
+ var max = Int32.Parse(pair.GetValueInternal("=").Trim('"'));
+ var expires = DateTime.Now.AddSeconds((double)max);
+ if (cookie != null)
+ cookie.Expires = expires;
+ }
+ else if (pair.StartsWith("path", StringComparison.OrdinalIgnoreCase))
+ {
+ if (cookie != null)
+ cookie.Path = pair.GetValueInternal("=");
+ }
+ else if (pair.StartsWith("domain", StringComparison.OrdinalIgnoreCase))
+ {
+ if (cookie != null)
+ cookie.Domain = pair.GetValueInternal("=");
+ }
+ else if (pair.StartsWith("port", StringComparison.OrdinalIgnoreCase))
+ {
+ var port = pair.Equals("port", StringComparison.OrdinalIgnoreCase)
+ ? "\"\""
+ : pair.GetValueInternal("=");
+
+ if (cookie != null)
+ cookie.Port = port;
+ }
+ else if (pair.StartsWith("comment", StringComparison.OrdinalIgnoreCase))
+ {
+ if (cookie != null)
+ cookie.Comment = pair.GetValueInternal("=").UrlDecode();
+ }
+ else if (pair.StartsWith("commenturl", StringComparison.OrdinalIgnoreCase))
+ {
+ if (cookie != null)
+ cookie.CommentUri = pair.GetValueInternal("=").Trim('"').ToUri();
+ }
+ else if (pair.StartsWith("discard", StringComparison.OrdinalIgnoreCase))
+ {
+ if (cookie != null)
+ cookie.Discard = true;
+ }
+ else if (pair.StartsWith("secure", StringComparison.OrdinalIgnoreCase))
+ {
+ if (cookie != null)
+ cookie.Secure = true;
+ }
+ else if (pair.StartsWith("httponly", StringComparison.OrdinalIgnoreCase))
+ {
+ if (cookie != null)
+ cookie.HttpOnly = true;
+ }
+ else
+ {
+ if (cookie != null)
+ cookies.Add(cookie);
+
+ string name;
+ string val = String.Empty;
+
+ var pos = pair.IndexOf('=');
+ if (pos == -1)
+ {
+ name = pair;
+ }
+ else if (pos == pair.Length - 1)
+ {
+ name = pair.Substring(0, pos).TrimEnd(' ');
+ }
+ else
+ {
+ name = pair.Substring(0, pos).TrimEnd(' ');
+ val = pair.Substring(pos + 1).TrimStart(' ');
+ }
+
+ cookie = new Cookie(name, val);
+ }
+ }
+
+ if (cookie != null)
+ cookies.Add(cookie);
+
+ return cookies;
+ }
+ }
+}
diff --git a/SocketHttpListener.Portable/Net/EndPointListener.cs b/SocketHttpListener.Portable/Net/EndPointListener.cs
new file mode 100644
index 0000000000..690dedd092
--- /dev/null
+++ b/SocketHttpListener.Portable/Net/EndPointListener.cs
@@ -0,0 +1,383 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Threading;
+using MediaBrowser.Model.Cryptography;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Text;
+using SocketHttpListener.Primitives;
+
+namespace SocketHttpListener.Net
+{
+ sealed class EndPointListener
+ {
+ HttpListener listener;
+ IpEndPointInfo endpoint;
+ ISocket sock;
+ Dictionary<ListenerPrefix,HttpListener> prefixes; // Dictionary <ListenerPrefix, HttpListener>
+ List<ListenerPrefix> unhandled; // List<ListenerPrefix> unhandled; host = '*'
+ List<ListenerPrefix> all; // List<ListenerPrefix> all; host = '+'
+ ICertificate cert;
+ bool secure;
+ Dictionary<HttpConnection, HttpConnection> unregistered;
+ private readonly ILogger _logger;
+ private bool _closed;
+ private bool _enableDualMode;
+ private readonly ICryptoProvider _cryptoProvider;
+ private readonly IStreamFactory _streamFactory;
+ private readonly ISocketFactory _socketFactory;
+ private readonly ITextEncoding _textEncoding;
+ private readonly IMemoryStreamFactory _memoryStreamFactory;
+
+ public EndPointListener(HttpListener listener, IpAddressInfo addr, int port, bool secure, ICertificate cert, ILogger logger, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding)
+ {
+ this.listener = listener;
+ _logger = logger;
+ _cryptoProvider = cryptoProvider;
+ _streamFactory = streamFactory;
+ _socketFactory = socketFactory;
+ _memoryStreamFactory = memoryStreamFactory;
+ _textEncoding = textEncoding;
+
+ this.secure = secure;
+ this.cert = cert;
+
+ _enableDualMode = addr.Equals(IpAddressInfo.IPv6Any);
+ endpoint = new IpEndPointInfo(addr, port);
+
+ prefixes = new Dictionary<ListenerPrefix, HttpListener>();
+ unregistered = new Dictionary<HttpConnection, HttpConnection>();
+
+ CreateSocket();
+ }
+
+ internal HttpListener Listener
+ {
+ get
+ {
+ return listener;
+ }
+ }
+
+ private void CreateSocket()
+ {
+ try
+ {
+ sock = _socketFactory.CreateSocket(endpoint.IpAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp, _enableDualMode);
+ }
+ catch (SocketCreateException ex)
+ {
+ if (_enableDualMode && endpoint.IpAddress.Equals(IpAddressInfo.IPv6Any) && string.Equals(ex.ErrorCode, "AddressFamilyNotSupported", StringComparison.OrdinalIgnoreCase))
+ {
+ endpoint = new IpEndPointInfo(IpAddressInfo.Any, endpoint.Port);
+ _enableDualMode = false;
+ sock = _socketFactory.CreateSocket(endpoint.IpAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp, _enableDualMode);
+ }
+ else
+ {
+ throw;
+ }
+ }
+
+ sock.Bind(endpoint);
+
+ // This is the number TcpListener uses.
+ sock.Listen(2147483647);
+
+ sock.StartAccept(ProcessAccept, () => _closed);
+ _closed = false;
+ }
+
+ private async void ProcessAccept(ISocket 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, _streamFactory, _memoryStreamFactory, _textEncoding).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.Portable/Net/EndPointManager.cs b/SocketHttpListener.Portable/Net/EndPointManager.cs
new file mode 100644
index 0000000000..797684b3e2
--- /dev/null
+++ b/SocketHttpListener.Portable/Net/EndPointManager.cs
@@ -0,0 +1,165 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Reflection;
+using System.Threading.Tasks;
+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 IpAddressInfo GetIpAnyAddress(HttpListener listener)
+ {
+ return listener.EnableDualMode ? IpAddressInfo.IPv6Any : IpAddressInfo.Any;
+ }
+
+ static async Task<EndPointListener> GetEPListener(ILogger logger, string host, int port, HttpListener listener, bool secure)
+ {
+ var networkManager = listener.NetworkManager;
+
+ IpAddressInfo addr;
+ if (host == "*" || host == "+")
+ addr = GetIpAnyAddress(listener);
+ else if (networkManager.TryParseIpAddress(host, out addr) == false)
+ {
+ try
+ {
+ addr = (await networkManager.GetHostAddressesAsync(host).ConfigureAwait(false)).FirstOrDefault() ??
+ GetIpAnyAddress(listener);
+ }
+ catch
+ {
+ addr = GetIpAnyAddress(listener);
+ }
+ }
+
+ Dictionary<int, EndPointListener> p = null; // Dictionary<int, EndPointListener>
+ if (!ip_to_endpoints.TryGetValue(addr.Address, out p))
+ {
+ p = new Dictionary<int, EndPointListener>();
+ ip_to_endpoints[addr.Address] = 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.StreamFactory, listener.SocketFactory, listener.MemoryStreamFactory, listener.TextEncoding);
+ p[port] = epl;
+ }
+
+ return epl;
+ }
+
+ public static void RemoveEndPoint(EndPointListener epl, IpEndPointInfo ep)
+ {
+ lock (ip_to_endpoints)
+ {
+ // Dictionary<int, EndPointListener> p
+ Dictionary<int, EndPointListener> p;
+ if (ip_to_endpoints.TryGetValue(ep.IpAddress.Address, out p))
+ {
+ p.Remove(ep.Port);
+ if (p.Count == 0)
+ {
+ ip_to_endpoints.Remove(ep.IpAddress.Address);
+ }
+ }
+ 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.Portable/Net/HttpConnection.cs b/SocketHttpListener.Portable/Net/HttpConnection.cs
new file mode 100644
index 0000000000..bc4286dc8c
--- /dev/null
+++ b/SocketHttpListener.Portable/Net/HttpConnection.cs
@@ -0,0 +1,551 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Cryptography;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Text;
+using SocketHttpListener.Primitives;
+
+namespace SocketHttpListener.Net
+{
+ sealed class HttpConnection
+ {
+ const int BufferSize = 8192;
+ ISocket sock;
+ Stream stream;
+ EndPointListener epl;
+ MemoryStream ms;
+ byte[] buffer;
+ HttpListenerContext context;
+ StringBuilder current_line;
+ ListenerPrefix prefix;
+ RequestStream i_stream;
+ Stream o_stream;
+ bool chunked;
+ int reuses;
+ bool context_bound;
+ bool secure;
+ int s_timeout = 300000; // 90k ms for first request, 15k ms from then on
+ IpEndPointInfo local_ep;
+ HttpListener last_listener;
+ int[] client_cert_errors;
+ ICertificate cert;
+ Stream ssl_stream;
+
+ private ILogger _logger;
+ private readonly ICryptoProvider _cryptoProvider;
+ private readonly IMemoryStreamFactory _memoryStreamFactory;
+ private readonly ITextEncoding _textEncoding;
+ private readonly IStreamFactory _streamFactory;
+
+ private HttpConnection(ILogger logger, ISocket sock, EndPointListener epl, bool secure, ICertificate cert, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding)
+ {
+ _logger = logger;
+ this.sock = sock;
+ this.epl = epl;
+ this.secure = secure;
+ this.cert = cert;
+ _cryptoProvider = cryptoProvider;
+ _memoryStreamFactory = memoryStreamFactory;
+ _textEncoding = textEncoding;
+ _streamFactory = streamFactory;
+ }
+
+ private async Task InitStream()
+ {
+ if (secure == false)
+ {
+ stream = _streamFactory.CreateNetworkStream(sock, false);
+ }
+ else
+ {
+ //ssl_stream = epl.Listener.CreateSslStream(new NetworkStream(sock, 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 = _streamFactory.CreateSslStream(_streamFactory.CreateNetworkStream(sock, false), false);
+ await _streamFactory.AuthenticateSslStreamAsServer(ssl_stream, cert).ConfigureAwait(false);
+ stream = ssl_stream;
+ }
+ Init();
+ }
+
+ public static async Task<HttpConnection> Create(ILogger logger, ISocket sock, EndPointListener epl, bool secure, ICertificate cert, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding)
+ {
+ var connection = new HttpConnection(logger, sock, epl, secure, cert, cryptoProvider, streamFactory, memoryStreamFactory, textEncoding);
+
+ await connection.InitStream().ConfigureAwait(false);
+
+ return connection;
+ }
+
+ public Stream Stream
+ {
+ get
+ {
+ return stream;
+ }
+ }
+
+ internal int[] ClientCertificateErrors
+ {
+ get { return client_cert_errors; }
+ }
+
+ void Init()
+ {
+ if (ssl_stream != null)
+ {
+ //ssl_stream.AuthenticateAsServer(client_cert, true, (SslProtocols)ServicePointManager.SecurityProtocol, false);
+ //_streamFactory.AuthenticateSslStreamAsServer(ssl_stream, cert);
+ }
+
+ context_bound = false;
+ i_stream = null;
+ o_stream = null;
+ prefix = null;
+ chunked = false;
+ ms = _memoryStreamFactory.CreateNew();
+ position = 0;
+ input_state = InputState.RequestLine;
+ line_state = LineState.None;
+ context = new HttpListenerContext(this, _logger, _cryptoProvider, _memoryStreamFactory, _textEncoding);
+ }
+
+ public bool IsClosed
+ {
+ get { return (sock == null); }
+ }
+
+ public int Reuses
+ {
+ get { return reuses; }
+ }
+
+ public IpEndPointInfo LocalEndPoint
+ {
+ get
+ {
+ if (local_ep != null)
+ return local_ep;
+
+ local_ep = (IpEndPointInfo)sock.LocalEndPoint;
+ return local_ep;
+ }
+ }
+
+ public IpEndPointInfo RemoteEndPoint
+ {
+ get { return (IpEndPointInfo)sock.RemoteEndPoint; }
+ }
+
+ public bool IsSecure
+ {
+ get { return secure; }
+ }
+
+ public ListenerPrefix Prefix
+ {
+ get { return prefix; }
+ set { prefix = value; }
+ }
+
+ public async Task BeginReadRequest()
+ {
+ if (buffer == null)
+ buffer = new byte[BufferSize];
+
+ try
+ {
+ //if (reuses == 1)
+ // s_timeout = 15000;
+ var nRead = await stream.ReadAsync(buffer, 0, BufferSize).ConfigureAwait(false);
+
+ OnReadInternal(nRead);
+ }
+ catch (Exception ex)
+ {
+ OnReadInternalException(ms, ex);
+ }
+ }
+
+ public RequestStream GetRequestStream(bool chunked, long contentlength)
+ {
+ if (i_stream == null)
+ {
+ byte[] buffer;
+ _memoryStreamFactory.TryGetBuffer(ms, out buffer);
+
+ int length = (int)ms.Length;
+ ms = null;
+ if (chunked)
+ {
+ this.chunked = true;
+ //context.Response.SendChunked = true;
+ i_stream = new ChunkedInputStream(context, stream, buffer, position, length - position);
+ }
+ else
+ {
+ i_stream = new RequestStream(stream, buffer, position, length - position, contentlength);
+ }
+ }
+ return i_stream;
+ }
+
+ public Stream GetResponseStream(bool isExpect100Continue = false)
+ {
+ // TODO: can we get this stream before reading the input?
+ if (o_stream == null)
+ {
+ context.Response.DetermineIfChunked();
+
+ if (context.Response.SendChunked || isExpect100Continue || context.Request.IsWebSocketRequest || true)
+ {
+ o_stream = new ResponseStream(stream, context.Response, _memoryStreamFactory, _textEncoding);
+ }
+ else
+ {
+ o_stream = stream;
+ using (var headerStream = ResponseStream.GetHeaders(context.Response, _memoryStreamFactory, false))
+ {
+ headerStream.CopyTo(o_stream);
+ }
+ }
+ }
+ return o_stream;
+ }
+
+ void OnReadInternal(int nread)
+ {
+ ms.Write(buffer, 0, nread);
+ if (ms.Length > 32768)
+ {
+ SendError("Bad request", 400);
+ Close(true);
+ return;
+ }
+
+ if (nread == 0)
+ {
+ //if (ms.Length > 0)
+ // SendError (); // Why bother?
+ CloseSocket();
+ Unbind();
+ return;
+ }
+
+ if (ProcessInput(ms))
+ {
+ if (!context.HaveError)
+ context.Request.FinishInitialization();
+
+ if (context.HaveError)
+ {
+ SendError();
+ Close(true);
+ return;
+ }
+
+ if (!epl.BindContext(context))
+ {
+ SendError("Invalid host", 400);
+ Close(true);
+ return;
+ }
+ HttpListener listener = epl.Listener;
+ if (last_listener != listener)
+ {
+ RemoveConnection();
+ listener.AddConnection(this);
+ last_listener = listener;
+ }
+
+ context_bound = true;
+ listener.RegisterContext(context);
+ return;
+ }
+
+ BeginReadRequest();
+ }
+
+ private void OnReadInternalException(MemoryStream ms, Exception ex)
+ {
+ //_logger.ErrorException("Error in HttpConnection.OnReadInternal", ex);
+
+ if (ms != null && ms.Length > 0)
+ SendError();
+ if (sock != null)
+ {
+ CloseSocket();
+ Unbind();
+ }
+ }
+
+ void RemoveConnection()
+ {
+ if (last_listener == null)
+ epl.RemoveConnection(this);
+ else
+ last_listener.RemoveConnection(this);
+ }
+
+ enum InputState
+ {
+ RequestLine,
+ Headers
+ }
+
+ enum LineState
+ {
+ None,
+ CR,
+ LF
+ }
+
+ InputState input_state = InputState.RequestLine;
+ LineState line_state = LineState.None;
+ int position;
+
+ // true -> done processing
+ // false -> need more input
+ bool ProcessInput(MemoryStream ms)
+ {
+ byte[] buffer;
+ _memoryStreamFactory.TryGetBuffer(ms, out buffer);
+
+ int len = (int)ms.Length;
+ int used = 0;
+ string line;
+
+ while (true)
+ {
+ if (context.HaveError)
+ return true;
+
+ if (position >= len)
+ break;
+
+ try
+ {
+ line = ReadLine(buffer, position, len - position, ref used);
+ position += used;
+ }
+ catch
+ {
+ context.ErrorMessage = "Bad request";
+ context.ErrorStatus = 400;
+ return true;
+ }
+
+ if (line == null)
+ break;
+
+ if (line == "")
+ {
+ if (input_state == InputState.RequestLine)
+ continue;
+ current_line = null;
+ ms = null;
+ return true;
+ }
+
+ if (input_state == InputState.RequestLine)
+ {
+ context.Request.SetRequestLine(line);
+ input_state = InputState.Headers;
+ }
+ else
+ {
+ try
+ {
+ context.Request.AddHeader(line);
+ }
+ catch (Exception e)
+ {
+ context.ErrorMessage = e.Message;
+ context.ErrorStatus = 400;
+ return true;
+ }
+ }
+ }
+
+ if (used == len)
+ {
+ ms.SetLength(0);
+ position = 0;
+ }
+ return false;
+ }
+
+ string ReadLine(byte[] buffer, int offset, int len, ref int used)
+ {
+ if (current_line == null)
+ current_line = new StringBuilder(128);
+ int last = offset + len;
+ used = 0;
+
+ for (int i = offset; i < last && line_state != LineState.LF; i++)
+ {
+ used++;
+ byte b = buffer[i];
+ if (b == 13)
+ {
+ line_state = LineState.CR;
+ }
+ else if (b == 10)
+ {
+ line_state = LineState.LF;
+ }
+ else
+ {
+ current_line.Append((char)b);
+ }
+ }
+
+ string result = null;
+ if (line_state == LineState.LF)
+ {
+ line_state = LineState.None;
+ result = current_line.ToString();
+ current_line.Length = 0;
+ }
+
+ return result;
+ }
+
+ public void SendError(string msg, int status)
+ {
+ try
+ {
+ HttpListenerResponse response = context.Response;
+ response.StatusCode = status;
+ response.ContentType = "text/html";
+ string description = HttpListenerResponse.GetStatusDescription(status);
+ string str;
+ if (msg != null)
+ str = String.Format("<h1>{0} ({1})</h1>", description, msg);
+ else
+ str = String.Format("<h1>{0}</h1>", description);
+
+ byte[] error = context.Response.ContentEncoding.GetBytes(str);
+ response.ContentLength64 = error.Length;
+ response.OutputStream.Write(error, 0, (int)error.Length);
+ response.Close();
+ }
+ catch
+ {
+ // response was already closed
+ }
+ }
+
+ public void SendError()
+ {
+ SendError(context.ErrorMessage, context.ErrorStatus);
+ }
+
+ void Unbind()
+ {
+ if (context_bound)
+ {
+ epl.UnbindContext(context);
+ context_bound = false;
+ }
+ }
+
+ public void Close()
+ {
+ Close(false);
+ }
+
+ private void CloseSocket()
+ {
+ if (sock == null)
+ return;
+
+ try
+ {
+ sock.Close();
+ }
+ catch
+ {
+ }
+ finally
+ {
+ sock = null;
+ }
+ RemoveConnection();
+ }
+
+ internal void Close(bool force_close)
+ {
+ if (sock != null)
+ {
+ if (!context.Request.IsWebSocketRequest || force_close)
+ {
+ Stream st = GetResponseStream();
+ if (st != null)
+ {
+ st.Dispose();
+ }
+
+ o_stream = null;
+ }
+ }
+
+ if (sock != null)
+ {
+ force_close |= !context.Request.KeepAlive;
+ if (!force_close)
+ force_close = (context.Response.Headers["connection"] == "close");
+ /*
+ if (!force_close) {
+// bool conn_close = (status_code == 400 || status_code == 408 || status_code == 411 ||
+// status_code == 413 || status_code == 414 || status_code == 500 ||
+// status_code == 503);
+ force_close |= (context.Request.ProtocolVersion <= HttpVersion.Version10);
+ }
+ */
+
+ if (!force_close && context.Request.FlushInput())
+ {
+ reuses++;
+ Unbind();
+ Init();
+ BeginReadRequest();
+ return;
+ }
+
+ ISocket s = sock;
+ sock = null;
+ try
+ {
+ if (s != null)
+ s.Shutdown(true);
+ }
+ catch
+ {
+ }
+ finally
+ {
+ if (s != null)
+ s.Close();
+ }
+ Unbind();
+ RemoveConnection();
+ return;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/SocketHttpListener.Portable/Net/HttpListener.cs b/SocketHttpListener.Portable/Net/HttpListener.cs
new file mode 100644
index 0000000000..2b0f75d01f
--- /dev/null
+++ b/SocketHttpListener.Portable/Net/HttpListener.cs
@@ -0,0 +1,288 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Cryptography;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Text;
+using SocketHttpListener.Primitives;
+
+namespace SocketHttpListener.Net
+{
+ public sealed class HttpListener : IDisposable
+ {
+ internal ICryptoProvider CryptoProvider { get; private set; }
+ internal IStreamFactory StreamFactory { get; private set; }
+ internal ISocketFactory SocketFactory { get; private set; }
+ internal ITextEncoding TextEncoding { get; private set; }
+ internal IMemoryStreamFactory MemoryStreamFactory { get; private set; }
+ internal INetworkManager NetworkManager { get; private set; }
+
+ public bool EnableDualMode { get; set; }
+
+ AuthenticationSchemes auth_schemes;
+ HttpListenerPrefixCollection prefixes;
+ AuthenticationSchemeSelector auth_selector;
+ string realm;
+ bool unsafe_ntlm_auth;
+ bool listening;
+ bool disposed;
+
+ Dictionary<HttpListenerContext, HttpListenerContext> registry; // Dictionary<HttpListenerContext,HttpListenerContext>
+ Dictionary<HttpConnection, HttpConnection> connections;
+ private ILogger _logger;
+ private ICertificate _certificate;
+
+ public Action<HttpListenerContext> OnContext { get; set; }
+
+ public HttpListener(ILogger logger, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory)
+ {
+ _logger = logger;
+ CryptoProvider = cryptoProvider;
+ StreamFactory = streamFactory;
+ SocketFactory = socketFactory;
+ NetworkManager = networkManager;
+ TextEncoding = textEncoding;
+ MemoryStreamFactory = memoryStreamFactory;
+ prefixes = new HttpListenerPrefixCollection(logger, this);
+ registry = new Dictionary<HttpListenerContext, HttpListenerContext>();
+ connections = new Dictionary<HttpConnection, HttpConnection>();
+ auth_schemes = AuthenticationSchemes.Anonymous;
+ }
+
+ public HttpListener(ICertificate certificate, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory)
+ :this(new NullLogger(), certificate, cryptoProvider, streamFactory, socketFactory, networkManager, textEncoding, memoryStreamFactory)
+ {
+ }
+
+ public HttpListener(ILogger logger, ICertificate certificate, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory)
+ : this(logger, cryptoProvider, streamFactory, socketFactory, networkManager, textEncoding, memoryStreamFactory)
+ {
+ _certificate = certificate;
+ }
+
+ public void LoadCert(ICertificate cert)
+ {
+ _certificate = cert;
+ }
+
+ // TODO: Digest, NTLM and Negotiate require ControlPrincipal
+ public AuthenticationSchemes AuthenticationSchemes
+ {
+ get { return auth_schemes; }
+ set
+ {
+ CheckDisposed();
+ auth_schemes = value;
+ }
+ }
+
+ public AuthenticationSchemeSelector AuthenticationSchemeSelectorDelegate
+ {
+ get { return auth_selector; }
+ set
+ {
+ CheckDisposed();
+ auth_selector = value;
+ }
+ }
+
+ public bool IsListening
+ {
+ get { return listening; }
+ }
+
+ public static bool IsSupported
+ {
+ get { return true; }
+ }
+
+ public HttpListenerPrefixCollection Prefixes
+ {
+ get
+ {
+ CheckDisposed();
+ return prefixes;
+ }
+ }
+
+ // TODO: use this
+ public string Realm
+ {
+ get { return realm; }
+ set
+ {
+ CheckDisposed();
+ realm = value;
+ }
+ }
+
+ public bool UnsafeConnectionNtlmAuthentication
+ {
+ get { return unsafe_ntlm_auth; }
+ set
+ {
+ CheckDisposed();
+ unsafe_ntlm_auth = value;
+ }
+ }
+
+ //internal IMonoSslStream CreateSslStream(Stream innerStream, bool ownsStream, MSI.MonoRemoteCertificateValidationCallback callback)
+ //{
+ // lock (registry)
+ // {
+ // if (tlsProvider == null)
+ // tlsProvider = MonoTlsProviderFactory.GetProviderInternal();
+ // if (tlsSettings == null)
+ // tlsSettings = MSI.MonoTlsSettings.CopyDefaultSettings();
+ // if (tlsSettings.RemoteCertificateValidationCallback == null)
+ // tlsSettings.RemoteCertificateValidationCallback = callback;
+ // return tlsProvider.CreateSslStream(innerStream, ownsStream, tlsSettings);
+ // }
+ //}
+
+ internal ICertificate Certificate
+ {
+ get { return _certificate; }
+ }
+
+ public void Abort()
+ {
+ if (disposed)
+ return;
+
+ if (!listening)
+ {
+ return;
+ }
+
+ Close(true);
+ }
+
+ public void Close()
+ {
+ if (disposed)
+ return;
+
+ if (!listening)
+ {
+ disposed = true;
+ return;
+ }
+
+ Close(true);
+ disposed = true;
+ }
+
+ void Close(bool force)
+ {
+ CheckDisposed();
+ EndPointManager.RemoveListener(_logger, this);
+ Cleanup(force);
+ }
+
+ void Cleanup(bool close_existing)
+ {
+ lock (registry)
+ {
+ if (close_existing)
+ {
+ // Need to copy this since closing will call UnregisterContext
+ ICollection keys = registry.Keys;
+ var all = new HttpListenerContext[keys.Count];
+ keys.CopyTo(all, 0);
+ registry.Clear();
+ for (int i = all.Length - 1; i >= 0; i--)
+ all[i].Connection.Close(true);
+ }
+
+ lock (connections)
+ {
+ ICollection keys = connections.Keys;
+ var conns = new HttpConnection[keys.Count];
+ keys.CopyTo(conns, 0);
+ connections.Clear();
+ for (int i = conns.Length - 1; i >= 0; i--)
+ conns[i].Close(true);
+ }
+ }
+ }
+
+ internal AuthenticationSchemes SelectAuthenticationScheme(HttpListenerContext context)
+ {
+ if (AuthenticationSchemeSelectorDelegate != null)
+ return AuthenticationSchemeSelectorDelegate(context.Request);
+ else
+ return auth_schemes;
+ }
+
+ public void Start()
+ {
+ CheckDisposed();
+ if (listening)
+ return;
+
+ EndPointManager.AddListener(_logger, this);
+ listening = true;
+ }
+
+ public void Stop()
+ {
+ CheckDisposed();
+ listening = false;
+ Close(false);
+ }
+
+ void IDisposable.Dispose()
+ {
+ if (disposed)
+ return;
+
+ Close(true); //TODO: Should we force here or not?
+ disposed = true;
+ }
+
+ internal void CheckDisposed()
+ {
+ if (disposed)
+ throw new ObjectDisposedException(GetType().ToString());
+ }
+
+ internal void RegisterContext(HttpListenerContext context)
+ {
+ if (OnContext != null && IsListening)
+ {
+ OnContext(context);
+ }
+
+ lock (registry)
+ registry[context] = context;
+ }
+
+ internal void UnregisterContext(HttpListenerContext context)
+ {
+ lock (registry)
+ registry.Remove(context);
+ }
+
+ internal void AddConnection(HttpConnection cnc)
+ {
+ lock (connections)
+ {
+ connections[cnc] = cnc;
+ }
+ }
+
+ internal void RemoveConnection(HttpConnection cnc)
+ {
+ lock (connections)
+ {
+ connections.Remove(cnc);
+ }
+ }
+ }
+}
diff --git a/SocketHttpListener.Portable/Net/HttpListenerBasicIdentity.cs b/SocketHttpListener.Portable/Net/HttpListenerBasicIdentity.cs
new file mode 100644
index 0000000000..faa26693d7
--- /dev/null
+++ b/SocketHttpListener.Portable/Net/HttpListenerBasicIdentity.cs
@@ -0,0 +1,70 @@
+using System.Security.Principal;
+
+namespace SocketHttpListener.Net
+{
+ public class HttpListenerBasicIdentity : GenericIdentity
+ {
+ string password;
+
+ public HttpListenerBasicIdentity(string username, string password)
+ : base(username, "Basic")
+ {
+ this.password = password;
+ }
+
+ public virtual string Password
+ {
+ get { return password; }
+ }
+ }
+
+ public class GenericIdentity : IIdentity
+ {
+ private string m_name;
+ private string m_type;
+
+ public GenericIdentity(string name)
+ {
+ if (name == null)
+ throw new System.ArgumentNullException("name");
+
+ m_name = name;
+ m_type = "";
+ }
+
+ public GenericIdentity(string name, string type)
+ {
+ if (name == null)
+ throw new System.ArgumentNullException("name");
+ if (type == null)
+ throw new System.ArgumentNullException("type");
+
+ m_name = name;
+ m_type = type;
+ }
+
+ public virtual string Name
+ {
+ get
+ {
+ return m_name;
+ }
+ }
+
+ public virtual string AuthenticationType
+ {
+ get
+ {
+ return m_type;
+ }
+ }
+
+ public virtual bool IsAuthenticated
+ {
+ get
+ {
+ return !m_name.Equals("");
+ }
+ }
+ }
+}
diff --git a/SocketHttpListener.Portable/Net/HttpListenerContext.cs b/SocketHttpListener.Portable/Net/HttpListenerContext.cs
new file mode 100644
index 0000000000..182fd2d2a4
--- /dev/null
+++ b/SocketHttpListener.Portable/Net/HttpListenerContext.cs
@@ -0,0 +1,200 @@
+using System;
+using System.Net;
+using System.Security.Principal;
+using MediaBrowser.Model.Cryptography;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Text;
+using SocketHttpListener.Net.WebSockets;
+using SocketHttpListener.Primitives;
+
+namespace SocketHttpListener.Net
+{
+ public sealed class HttpListenerContext
+ {
+ HttpListenerRequest request;
+ HttpListenerResponse response;
+ IPrincipal user;
+ HttpConnection cnc;
+ string error;
+ int err_status = 400;
+ private readonly ILogger _logger;
+ private readonly ICryptoProvider _cryptoProvider;
+ private readonly IMemoryStreamFactory _memoryStreamFactory;
+ private readonly ITextEncoding _textEncoding;
+
+ internal HttpListenerContext(HttpConnection cnc, ILogger logger, ICryptoProvider cryptoProvider, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding)
+ {
+ this.cnc = cnc;
+ _logger = logger;
+ _cryptoProvider = cryptoProvider;
+ _memoryStreamFactory = memoryStreamFactory;
+ _textEncoding = textEncoding;
+ request = new HttpListenerRequest(this, _textEncoding);
+ response = new HttpListenerResponse(this, _logger, _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); }
+ }
+
+ internal HttpConnection Connection
+ {
+ get { return cnc; }
+ }
+
+ public HttpListenerRequest Request
+ {
+ get { return request; }
+ }
+
+ 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))
+ {
+ user = ParseBasicAuthentication(authenticationData[1]);
+ }
+ // TODO: throw if malformed -> 400 bad request
+ }
+
+ internal IPrincipal ParseBasicAuthentication(string authData)
+ {
+ 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;
+ }
+ }
+
+ public HttpListenerWebSocketContext AcceptWebSocket(string protocol)
+ {
+ 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);
+ }
+ }
+
+ public class GenericPrincipal : IPrincipal
+ {
+ private IIdentity m_identity;
+ private string[] m_roles;
+
+ public GenericPrincipal(IIdentity identity, string[] roles)
+ {
+ if (identity == null)
+ throw new ArgumentNullException("identity");
+
+ m_identity = identity;
+ if (roles != null)
+ {
+ m_roles = new string[roles.Length];
+ for (int i = 0; i < roles.Length; ++i)
+ {
+ m_roles[i] = roles[i];
+ }
+ }
+ else
+ {
+ m_roles = null;
+ }
+ }
+
+ public virtual IIdentity Identity
+ {
+ get
+ {
+ return m_identity;
+ }
+ }
+
+ public virtual bool IsInRole(string role)
+ {
+ if (role == null || m_roles == null)
+ return false;
+
+ for (int i = 0; i < m_roles.Length; ++i)
+ {
+ if (m_roles[i] != null && String.Compare(m_roles[i], role, StringComparison.OrdinalIgnoreCase) == 0)
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/SocketHttpListener.Portable/Net/HttpListenerPrefixCollection.cs b/SocketHttpListener.Portable/Net/HttpListenerPrefixCollection.cs
new file mode 100644
index 0000000000..0b05539eea
--- /dev/null
+++ b/SocketHttpListener.Portable/Net/HttpListenerPrefixCollection.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using MediaBrowser.Model.Logging;
+
+namespace SocketHttpListener.Net
+{
+ public class HttpListenerPrefixCollection : ICollection<string>, IEnumerable<string>, IEnumerable
+ {
+ List<string> prefixes = new List<string>();
+ HttpListener listener;
+
+ private ILogger _logger;
+
+ internal HttpListenerPrefixCollection(ILogger logger, HttpListener listener)
+ {
+ _logger = logger;
+ this.listener = listener;
+ }
+
+ public int Count
+ {
+ get { return prefixes.Count; }
+ }
+
+ public bool IsReadOnly
+ {
+ get { return false; }
+ }
+
+ public bool IsSynchronized
+ {
+ get { return false; }
+ }
+
+ public void Add(string uriPrefix)
+ {
+ listener.CheckDisposed();
+ ListenerPrefix.CheckUri(uriPrefix);
+ if (prefixes.Contains(uriPrefix))
+ return;
+
+ prefixes.Add(uriPrefix);
+ if (listener.IsListening)
+ EndPointManager.AddPrefix(_logger, uriPrefix, listener);
+ }
+
+ public void Clear()
+ {
+ listener.CheckDisposed();
+ prefixes.Clear();
+ if (listener.IsListening)
+ EndPointManager.RemoveListener(_logger, listener);
+ }
+
+ public bool Contains(string uriPrefix)
+ {
+ listener.CheckDisposed();
+ return prefixes.Contains(uriPrefix);
+ }
+
+ public void CopyTo(string[] array, int offset)
+ {
+ listener.CheckDisposed();
+ prefixes.CopyTo(array, offset);
+ }
+
+ public void CopyTo(Array array, int offset)
+ {
+ listener.CheckDisposed();
+ ((ICollection)prefixes).CopyTo(array, offset);
+ }
+
+ public IEnumerator<string> GetEnumerator()
+ {
+ return prefixes.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return prefixes.GetEnumerator();
+ }
+
+ public bool Remove(string uriPrefix)
+ {
+ listener.CheckDisposed();
+ if (uriPrefix == null)
+ throw new ArgumentNullException("uriPrefix");
+
+ bool result = prefixes.Remove(uriPrefix);
+ if (result && listener.IsListening)
+ EndPointManager.RemovePrefix(_logger, uriPrefix, listener);
+
+ return result;
+ }
+ }
+}
diff --git a/SocketHttpListener.Portable/Net/HttpListenerRequest.cs b/SocketHttpListener.Portable/Net/HttpListenerRequest.cs
new file mode 100644
index 0000000000..811cc6ddb6
--- /dev/null
+++ b/SocketHttpListener.Portable/Net/HttpListenerRequest.cs
@@ -0,0 +1,654 @@
+using System;
+using System.Collections.Specialized;
+using System.Globalization;
+using System.IO;
+using System.Net;
+using System.Text;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Services;
+using MediaBrowser.Model.Text;
+using SocketHttpListener.Primitives;
+
+namespace SocketHttpListener.Net
+{
+ public sealed 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 keep_alive;
+
+ private readonly ITextEncoding _textEncoding;
+
+ internal HttpListenerRequest(HttpListenerContext context, ITextEncoding textEncoding)
+ {
+ this.context = context;
+ _textEncoding = textEncoding;
+ headers = new WebHeaderCollection();
+ version = HttpVersion.Version10;
+ }
+
+ static char[] separators = new char[] { ' ' };
+
+ internal void SetRequestLine(string req)
+ {
+ string[] parts = req.Split(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;
+ }
+
+ 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;
+ }
+ }
+
+ void CreateQueryString(string query)
+ {
+ if (query == null || query.Length == 0)
+ {
+ query_string = new QueryParamCollection();
+ return;
+ }
+
+ query_string = new QueryParamCollection();
+ if (query[0] == '?')
+ query = query.Substring(1);
+ string[] components = query.Split('&');
+ foreach (string kv in components)
+ {
+ int pos = kv.IndexOf('=');
+ if (pos == -1)
+ {
+ query_string.Add(null, WebUtility.UrlDecode(kv));
+ }
+ else
+ {
+ string key = WebUtility.UrlDecode(kv.Substring(0, pos));
+ string val = WebUtility.UrlDecode(kv.Substring(pos + 1));
+
+ query_string.Add(key, val);
+ }
+ }
+ }
+
+ 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);
+
+ string base_uri = String.Format("{0}://{1}:{2}",
+ (IsSecureConnection) ? (IsWebSocketRequest ? "wss" : "https") : (IsWebSocketRequest ? "ws" : "http"),
+ host, LocalEndPoint.Port);
+
+ 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)
+ {
+ 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)
+ {
+ context.Connection.SendError(null, 501);
+ return;
+ }
+ }
+
+ if (!is_chunked && !cl_set)
+ {
+ if (String.Compare(method, "POST", StringComparison.OrdinalIgnoreCase) == 0 ||
+ String.Compare(method, "PUT", StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ context.Connection.SendError(null, 411);
+ return;
+ }
+ }
+
+ if (String.Compare(Headers["Expect"], "100-continue", StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ var output = (ResponseStream)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));
+ }
+
+ //
+ // 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;
+
+ char c = scheme[0];
+ if (c == 'h')
+ return (scheme == "http" || scheme == "https");
+ if (c == 'f')
+ return (scheme == "file" || scheme == "ftp");
+
+ 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)
+ {
+ 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)
+ {
+ 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
+ {
+ referrer = new Uri("http://someone.is.screwing.with.the.headers.com/");
+ }
+ 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)
+ {
+ 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 (current != null)
+ current.Port = str.Substring(str.IndexOf('=') + 1).Trim();
+ }
+ 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;
+ }
+ }
+ 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)
+ {
+ return true;
+ }
+ }
+ catch (ObjectDisposedException e)
+ {
+ input_stream = null;
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+ }
+
+ public string[] AcceptTypes
+ {
+ get { return accept_types; }
+ }
+
+ public int ClientCertificateError
+ {
+ 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;
+ }
+ }
+
+ public Encoding ContentEncoding
+ {
+ get
+ {
+ if (content_encoding == null)
+ content_encoding = _textEncoding.GetDefaultEncoding();
+ return content_encoding;
+ }
+ }
+
+ public long ContentLength64
+ {
+ get { return content_length; }
+ }
+
+ public string ContentType
+ {
+ get { return headers["content-type"]; }
+ }
+
+ public CookieCollection Cookies
+ {
+ get
+ {
+ // TODO: check if the collection is read-only
+ if (cookies == null)
+ cookies = new CookieCollection();
+ return cookies;
+ }
+ }
+
+ public bool HasEntityBody
+ {
+ get { return (content_length > 0 || is_chunked); }
+ }
+
+ public QueryParamCollection Headers
+ {
+ get { return headers; }
+ }
+
+ public string HttpMethod
+ {
+ get { return method; }
+ }
+
+ public Stream InputStream
+ {
+ get
+ {
+ if (input_stream == null)
+ {
+ if (is_chunked || content_length > 0)
+ input_stream = context.Connection.GetRequestStream(is_chunked, content_length);
+ else
+ input_stream = Stream.Null;
+ }
+
+ return input_stream;
+ }
+ }
+
+ public bool IsAuthenticated
+ {
+ get { return false; }
+ }
+
+ public bool IsLocal
+ {
+ get { return RemoteEndPoint.IpAddress.Equals(IpAddressInfo.Loopback) || RemoteEndPoint.IpAddress.Equals(IpAddressInfo.IPv6Loopback) || LocalEndPoint.IpAddress.Equals(RemoteEndPoint.IpAddress); }
+ }
+
+ public bool IsSecureConnection
+ {
+ get { return context.Connection.IsSecure; }
+ }
+
+ public bool KeepAlive
+ {
+ get
+ {
+ if (ka_set)
+ return keep_alive;
+
+ ka_set = true;
+ // 1. Connection header
+ // 2. Protocol (1.1 == keep-alive by default)
+ // 3. Keep-Alive header
+ string cnc = headers["Connection"];
+ if (!String.IsNullOrEmpty(cnc))
+ {
+ keep_alive = (0 == String.Compare(cnc, "keep-alive", StringComparison.OrdinalIgnoreCase));
+ }
+ else if (version == HttpVersion.Version11)
+ {
+ keep_alive = true;
+ }
+ else
+ {
+ cnc = headers["keep-alive"];
+ if (!String.IsNullOrEmpty(cnc))
+ keep_alive = (0 != String.Compare(cnc, "closed", StringComparison.OrdinalIgnoreCase));
+ }
+ return keep_alive;
+ }
+ }
+
+ public IpEndPointInfo LocalEndPoint
+ {
+ get { return context.Connection.LocalEndPoint; }
+ }
+
+ public Version ProtocolVersion
+ {
+ get { return version; }
+ }
+
+ public QueryParamCollection QueryString
+ {
+ get { return query_string; }
+ }
+
+ public string RawUrl
+ {
+ get { return raw_url; }
+ }
+
+ public IpEndPointInfo RemoteEndPoint
+ {
+ get { return context.Connection.RemoteEndPoint; }
+ }
+
+ public Guid RequestTraceIdentifier
+ {
+ get { return Guid.Empty; }
+ }
+
+ public Uri Url
+ {
+ get { return url; }
+ }
+
+ public Uri UrlReferrer
+ {
+ get { return referrer; }
+ }
+
+ public string UserAgent
+ {
+ get { return headers["user-agent"]; }
+ }
+
+ public string UserHostAddress
+ {
+ get { return LocalEndPoint.ToString(); }
+ }
+
+ public string UserHostName
+ {
+ get { return headers["host"]; }
+ }
+
+ public string[] UserLanguages
+ {
+ get { return user_languages; }
+ }
+
+ public string ServiceName
+ {
+ get
+ {
+ return null;
+ }
+ }
+
+ 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
+ {
+ if (!_websocketRequestWasSet)
+ {
+ _websocketRequest = method == "GET" &&
+ version > HttpVersion.Version10 &&
+ headers.Contains("Upgrade", "websocket") &&
+ headers.Contains("Connection", "Upgrade");
+
+ _websocketRequestWasSet = true;
+ }
+
+ return _websocketRequest;
+ }
+ }
+
+ public Task<ICertificate> GetClientCertificateAsync()
+ {
+ return Task.FromResult<ICertificate>(null);
+ }
+ }
+}
diff --git a/SocketHttpListener.Portable/Net/HttpListenerResponse.cs b/SocketHttpListener.Portable/Net/HttpListenerResponse.cs
new file mode 100644
index 0000000000..c1182de343
--- /dev/null
+++ b/SocketHttpListener.Portable/Net/HttpListenerResponse.cs
@@ -0,0 +1,512 @@
+using System;
+using System.Globalization;
+using System.IO;
+using System.Net;
+using System.Text;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Text;
+using SocketHttpListener.Primitives;
+
+namespace SocketHttpListener.Net
+{
+ public sealed class HttpListenerResponse : IDisposable
+ {
+ bool disposed;
+ Encoding content_encoding;
+ long content_length;
+ bool cl_set;
+ string content_type;
+ CookieCollection cookies;
+ WebHeaderCollection headers = new WebHeaderCollection();
+ bool keep_alive = true;
+ Stream output_stream;
+ Version version = HttpVersion.Version11;
+ string location;
+ int status_code = 200;
+ string status_description = "OK";
+ bool chunked;
+ HttpListenerContext context;
+
+ internal bool HeadersSent;
+ internal object headers_lock = new object();
+
+ private readonly ILogger _logger;
+ private readonly ITextEncoding _textEncoding;
+
+ internal HttpListenerResponse(HttpListenerContext context, ILogger logger, ITextEncoding textEncoding)
+ {
+ this.context = context;
+ _logger = logger;
+ _textEncoding = textEncoding;
+ }
+
+ internal bool CloseConnection
+ {
+ get
+ {
+ return headers["Connection"] == "close";
+ }
+ }
+
+ public Encoding ContentEncoding
+ {
+ get
+ {
+ if (content_encoding == null)
+ content_encoding = _textEncoding.GetDefaultEncoding();
+ return content_encoding;
+ }
+ set
+ {
+ if (disposed)
+ throw new ObjectDisposedException(GetType().ToString());
+
+ content_encoding = value;
+ }
+ }
+
+ public long ContentLength64
+ {
+ get { return content_length; }
+ set
+ {
+ if (disposed)
+ throw new ObjectDisposedException(GetType().ToString());
+
+ if (HeadersSent)
+ throw new InvalidOperationException("Cannot be changed after headers are sent.");
+
+ if (value < 0)
+ throw new ArgumentOutOfRangeException("Must be >= 0", "value");
+
+ cl_set = true;
+ content_length = value;
+ }
+ }
+
+ public string ContentType
+ {
+ get { return content_type; }
+ set
+ {
+ // TODO: is null ok?
+ if (disposed)
+ throw new ObjectDisposedException(GetType().ToString());
+
+ content_type = value;
+ }
+ }
+
+ // RFC 2109, 2965 + the netscape specification at http://wp.netscape.com/newsref/std/cookie_spec.html
+ public CookieCollection Cookies
+ {
+ get
+ {
+ if (cookies == null)
+ cookies = new CookieCollection();
+ return cookies;
+ }
+ set { cookies = value; } // null allowed?
+ }
+
+ public WebHeaderCollection Headers
+ {
+ get { return headers; }
+ set
+ {
+ /**
+ * "If you attempt to set a Content-Length, Keep-Alive, Transfer-Encoding, or
+ * WWW-Authenticate header using the Headers property, an exception will be
+ * thrown. Use the KeepAlive or ContentLength64 properties to set these headers.
+ * You cannot set the Transfer-Encoding or WWW-Authenticate headers manually."
+ */
+ // TODO: check if this is marked readonly after headers are sent.
+ headers = value;
+ }
+ }
+
+ public bool KeepAlive
+ {
+ get { return keep_alive; }
+ set
+ {
+ if (disposed)
+ throw new ObjectDisposedException(GetType().ToString());
+
+ keep_alive = value;
+ }
+ }
+
+ public Stream OutputStream
+ {
+ get
+ {
+ if (output_stream == null)
+ output_stream = context.Connection.GetResponseStream();
+ return output_stream;
+ }
+ }
+
+ public Version ProtocolVersion
+ {
+ get { return version; }
+ set
+ {
+ if (disposed)
+ throw new ObjectDisposedException(GetType().ToString());
+
+ if (value == null)
+ throw new ArgumentNullException("value");
+
+ if (value.Major != 1 || (value.Minor != 0 && value.Minor != 1))
+ throw new ArgumentException("Must be 1.0 or 1.1", "value");
+
+ if (disposed)
+ throw new ObjectDisposedException(GetType().ToString());
+
+ version = value;
+ }
+ }
+
+ public string RedirectLocation
+ {
+ get { return location; }
+ set
+ {
+ if (disposed)
+ throw new ObjectDisposedException(GetType().ToString());
+
+ location = value;
+ }
+ }
+
+ public bool SendChunked
+ {
+ get { return chunked; }
+ set
+ {
+ if (disposed)
+ throw new ObjectDisposedException(GetType().ToString());
+
+ chunked = value;
+ }
+ }
+
+ public int StatusCode
+ {
+ get { return status_code; }
+ set
+ {
+ if (disposed)
+ throw new ObjectDisposedException(GetType().ToString());
+
+ if (value < 100 || value > 999)
+ throw new ProtocolViolationException("StatusCode must be between 100 and 999.");
+ status_code = value;
+ status_description = GetStatusDescription(value);
+ }
+ }
+
+ internal static string GetStatusDescription(int code)
+ {
+ switch (code)
+ {
+ case 100: return "Continue";
+ case 101: return "Switching Protocols";
+ case 102: return "Processing";
+ case 200: return "OK";
+ case 201: return "Created";
+ case 202: return "Accepted";
+ case 203: return "Non-Authoritative Information";
+ case 204: return "No Content";
+ case 205: return "Reset Content";
+ case 206: return "Partial Content";
+ case 207: return "Multi-Status";
+ case 300: return "Multiple Choices";
+ case 301: return "Moved Permanently";
+ case 302: return "Found";
+ case 303: return "See Other";
+ case 304: return "Not Modified";
+ case 305: return "Use Proxy";
+ case 307: return "Temporary Redirect";
+ case 400: return "Bad Request";
+ case 401: return "Unauthorized";
+ case 402: return "Payment Required";
+ case 403: return "Forbidden";
+ case 404: return "Not Found";
+ case 405: return "Method Not Allowed";
+ case 406: return "Not Acceptable";
+ case 407: return "Proxy Authentication Required";
+ case 408: return "Request Timeout";
+ case 409: return "Conflict";
+ case 410: return "Gone";
+ case 411: return "Length Required";
+ case 412: return "Precondition Failed";
+ case 413: return "Request Entity Too Large";
+ case 414: return "Request-Uri Too Long";
+ case 415: return "Unsupported Media Type";
+ case 416: return "Requested Range Not Satisfiable";
+ case 417: return "Expectation Failed";
+ case 422: return "Unprocessable Entity";
+ case 423: return "Locked";
+ case 424: return "Failed Dependency";
+ case 500: return "Internal Server Error";
+ case 501: return "Not Implemented";
+ case 502: return "Bad Gateway";
+ case 503: return "Service Unavailable";
+ case 504: return "Gateway Timeout";
+ case 505: return "Http Version Not Supported";
+ case 507: return "Insufficient Storage";
+ }
+ return "";
+ }
+
+ public string StatusDescription
+ {
+ get { return status_description; }
+ set
+ {
+ status_description = value;
+ }
+ }
+
+ void IDisposable.Dispose()
+ {
+ Close(true); //TODO: Abort or Close?
+ }
+
+ public void Abort()
+ {
+ if (disposed)
+ return;
+
+ Close(true);
+ }
+
+ public void AddHeader(string name, string value)
+ {
+ if (name == null)
+ throw new ArgumentNullException("name");
+
+ if (name == "")
+ throw new ArgumentException("'name' cannot be empty", "name");
+
+ //TODO: check for forbidden headers and invalid characters
+ if (value.Length > 65535)
+ throw new ArgumentOutOfRangeException("value");
+
+ headers.Set(name, value);
+ }
+
+ public void AppendCookie(Cookie cookie)
+ {
+ if (cookie == null)
+ throw new ArgumentNullException("cookie");
+
+ Cookies.Add(cookie);
+ }
+
+ public void AppendHeader(string name, string value)
+ {
+ if (name == null)
+ throw new ArgumentNullException("name");
+
+ if (name == "")
+ throw new ArgumentException("'name' cannot be empty", "name");
+
+ if (value.Length > 65535)
+ throw new ArgumentOutOfRangeException("value");
+
+ headers.Add(name, value);
+ }
+
+ private void Close(bool force)
+ {
+ if (force)
+ {
+ _logger.Debug("HttpListenerResponse force closing HttpConnection");
+ }
+ disposed = true;
+ context.Connection.Close(force);
+ }
+
+ public void Close()
+ {
+ if (disposed)
+ return;
+
+ Close(false);
+ }
+
+ public void Redirect(string url)
+ {
+ StatusCode = 302; // Found
+ location = url;
+ }
+
+ bool FindCookie(Cookie cookie)
+ {
+ string name = cookie.Name;
+ string domain = cookie.Domain;
+ string path = cookie.Path;
+ foreach (Cookie c in cookies)
+ {
+ if (name != c.Name)
+ continue;
+ if (domain != c.Domain)
+ continue;
+ if (path == c.Path)
+ return true;
+ }
+
+ return false;
+ }
+
+ public void DetermineIfChunked()
+ {
+ if (chunked)
+ {
+ return ;
+ }
+
+ Version v = context.Request.ProtocolVersion;
+ if (!cl_set && !chunked && v >= HttpVersion.Version11)
+ chunked = true;
+ if (!chunked && string.Equals(headers["Transfer-Encoding"], "chunked"))
+ {
+ chunked = true;
+ }
+ }
+
+ internal void SendHeaders(bool closing, MemoryStream ms)
+ {
+ Encoding encoding = content_encoding;
+ if (encoding == null)
+ encoding = _textEncoding.GetDefaultEncoding();
+
+ if (content_type != null)
+ {
+ if (content_encoding != null && content_type.IndexOf("charset=", StringComparison.Ordinal) == -1)
+ {
+ string enc_name = content_encoding.WebName;
+ headers.SetInternal("Content-Type", content_type + "; charset=" + enc_name);
+ }
+ else
+ {
+ headers.SetInternal("Content-Type", content_type);
+ }
+ }
+
+ if (headers["Server"] == null)
+ headers.SetInternal("Server", "Mono-HTTPAPI/1.0");
+
+ CultureInfo inv = CultureInfo.InvariantCulture;
+ if (headers["Date"] == null)
+ headers.SetInternal("Date", DateTime.UtcNow.ToString("r", inv));
+
+ if (!chunked)
+ {
+ if (!cl_set && closing)
+ {
+ cl_set = true;
+ content_length = 0;
+ }
+
+ if (cl_set)
+ headers.SetInternal("Content-Length", content_length.ToString(inv));
+ }
+
+ Version v = context.Request.ProtocolVersion;
+ if (!cl_set && !chunked && v >= HttpVersion.Version11)
+ chunked = true;
+
+ /* Apache forces closing the connection for these status codes:
+ * HttpStatusCode.BadRequest 400
+ * HttpStatusCode.RequestTimeout 408
+ * HttpStatusCode.LengthRequired 411
+ * HttpStatusCode.RequestEntityTooLarge 413
+ * HttpStatusCode.RequestUriTooLong 414
+ * HttpStatusCode.InternalServerError 500
+ * HttpStatusCode.ServiceUnavailable 503
+ */
+ bool conn_close = (status_code == 408 || status_code == 411 ||
+ status_code == 413 || status_code == 414 ||
+ status_code == 503);
+
+ if (conn_close == false)
+ conn_close = !context.Request.KeepAlive;
+
+ // They sent both KeepAlive: true and Connection: close!?
+ if (!keep_alive || conn_close)
+ {
+ headers.SetInternal("Connection", "close");
+ conn_close = true;
+ }
+
+ if (chunked)
+ headers.SetInternal("Transfer-Encoding", "chunked");
+
+ //int reuses = context.Connection.Reuses;
+ //if (reuses >= 100)
+ //{
+ // _logger.Debug("HttpListenerResponse - keep alive has exceeded 100 uses and will be closed.");
+
+ // force_close_chunked = true;
+ // if (!conn_close)
+ // {
+ // headers.SetInternal("Connection", "close");
+ // conn_close = true;
+ // }
+ //}
+
+ if (!conn_close)
+ {
+ if (context.Request.ProtocolVersion <= HttpVersion.Version10)
+ headers.SetInternal("Connection", "keep-alive");
+ }
+
+ if (location != null)
+ headers.SetInternal("Location", location);
+
+ if (cookies != null)
+ {
+ foreach (Cookie cookie in cookies)
+ headers.SetInternal("Set-Cookie", cookie.ToString());
+ }
+
+ using (StreamWriter writer = new StreamWriter(ms, encoding, 256, true))
+ {
+ writer.Write("HTTP/{0} {1} {2}\r\n", version, status_code, status_description);
+ string headers_str = headers.ToStringMultiValue();
+ writer.Write(headers_str);
+ writer.Flush();
+ }
+
+ int preamble = encoding.GetPreamble().Length;
+ if (output_stream == null)
+ output_stream = context.Connection.GetResponseStream();
+
+ /* Assumes that the ms was at position 0 */
+ ms.Position = preamble;
+ HeadersSent = true;
+ }
+
+ public void SetCookie(Cookie cookie)
+ {
+ if (cookie == null)
+ throw new ArgumentNullException("cookie");
+
+ if (cookies != null)
+ {
+ if (FindCookie(cookie))
+ throw new ArgumentException("The cookie already exists.");
+ }
+ else
+ {
+ cookies = new CookieCollection();
+ }
+
+ cookies.Add(cookie);
+ }
+ }
+} \ No newline at end of file
diff --git a/SocketHttpListener.Portable/Net/HttpStatusCode.cs b/SocketHttpListener.Portable/Net/HttpStatusCode.cs
new file mode 100644
index 0000000000..93da82ba08
--- /dev/null
+++ b/SocketHttpListener.Portable/Net/HttpStatusCode.cs
@@ -0,0 +1,321 @@
+namespace SocketHttpListener.Net
+{
+ /// <summary>
+ /// Contains the values of the HTTP status codes.
+ /// </summary>
+ /// <remarks>
+ /// The HttpStatusCode enumeration contains the values of the HTTP status codes defined in
+ /// <see href="http://tools.ietf.org/html/rfc2616#section-10">RFC 2616</see> for HTTP 1.1.
+ /// </remarks>
+ public enum HttpStatusCode
+ {
+ /// <summary>
+ /// Equivalent to status code 100.
+ /// Indicates that the client should continue with its request.
+ /// </summary>
+ Continue = 100,
+ /// <summary>
+ /// Equivalent to status code 101.
+ /// Indicates that the server is switching the HTTP version or protocol on the connection.
+ /// </summary>
+ SwitchingProtocols = 101,
+ /// <summary>
+ /// Equivalent to status code 200.
+ /// Indicates that the client's request has succeeded.
+ /// </summary>
+ OK = 200,
+ /// <summary>
+ /// Equivalent to status code 201.
+ /// Indicates that the client's request has been fulfilled and resulted in a new resource being
+ /// created.
+ /// </summary>
+ Created = 201,
+ /// <summary>
+ /// Equivalent to status code 202.
+ /// Indicates that the client's request has been accepted for processing, but the processing
+ /// hasn't been completed.
+ /// </summary>
+ Accepted = 202,
+ /// <summary>
+ /// Equivalent to status code 203.
+ /// Indicates that the returned metainformation is from a local or a third-party copy instead of
+ /// the origin server.
+ /// </summary>
+ NonAuthoritativeInformation = 203,
+ /// <summary>
+ /// Equivalent to status code 204.
+ /// Indicates that the server has fulfilled the client's request but doesn't need to return
+ /// an entity-body.
+ /// </summary>
+ NoContent = 204,
+ /// <summary>
+ /// Equivalent to status code 205.
+ /// Indicates that the server has fulfilled the client's request, and the user agent should
+ /// reset the document view which caused the request to be sent.
+ /// </summary>
+ ResetContent = 205,
+ /// <summary>
+ /// Equivalent to status code 206.
+ /// Indicates that the server has fulfilled the partial GET request for the resource.
+ /// </summary>
+ PartialContent = 206,
+ /// <summary>
+ /// <para>
+ /// Equivalent to status code 300.
+ /// Indicates that the requested resource corresponds to any of multiple representations.
+ /// </para>
+ /// <para>
+ /// MultipleChoices is a synonym for Ambiguous.
+ /// </para>
+ /// </summary>
+ MultipleChoices = 300,
+ /// <summary>
+ /// <para>
+ /// Equivalent to status code 300.
+ /// Indicates that the requested resource corresponds to any of multiple representations.
+ /// </para>
+ /// <para>
+ /// Ambiguous is a synonym for MultipleChoices.
+ /// </para>
+ /// </summary>
+ Ambiguous = 300,
+ /// <summary>
+ /// <para>
+ /// Equivalent to status code 301.
+ /// Indicates that the requested resource has been assigned a new permanent URI and
+ /// any future references to this resource should use one of the returned URIs.
+ /// </para>
+ /// <para>
+ /// MovedPermanently is a synonym for Moved.
+ /// </para>
+ /// </summary>
+ MovedPermanently = 301,
+ /// <summary>
+ /// <para>
+ /// Equivalent to status code 301.
+ /// Indicates that the requested resource has been assigned a new permanent URI and
+ /// any future references to this resource should use one of the returned URIs.
+ /// </para>
+ /// <para>
+ /// Moved is a synonym for MovedPermanently.
+ /// </para>
+ /// </summary>
+ Moved = 301,
+ /// <summary>
+ /// <para>
+ /// Equivalent to status code 302.
+ /// Indicates that the requested resource is located temporarily under a different URI.
+ /// </para>
+ /// <para>
+ /// Found is a synonym for Redirect.
+ /// </para>
+ /// </summary>
+ Found = 302,
+ /// <summary>
+ /// <para>
+ /// Equivalent to status code 302.
+ /// Indicates that the requested resource is located temporarily under a different URI.
+ /// </para>
+ /// <para>
+ /// Redirect is a synonym for Found.
+ /// </para>
+ /// </summary>
+ Redirect = 302,
+ /// <summary>
+ /// <para>
+ /// Equivalent to status code 303.
+ /// Indicates that the response to the request can be found under a different URI and
+ /// should be retrieved using a GET method on that resource.
+ /// </para>
+ /// <para>
+ /// SeeOther is a synonym for RedirectMethod.
+ /// </para>
+ /// </summary>
+ SeeOther = 303,
+ /// <summary>
+ /// <para>
+ /// Equivalent to status code 303.
+ /// Indicates that the response to the request can be found under a different URI and
+ /// should be retrieved using a GET method on that resource.
+ /// </para>
+ /// <para>
+ /// RedirectMethod is a synonym for SeeOther.
+ /// </para>
+ /// </summary>
+ RedirectMethod = 303,
+ /// <summary>
+ /// Equivalent to status code 304.
+ /// Indicates that the client has performed a conditional GET request and access is allowed,
+ /// but the document hasn't been modified.
+ /// </summary>
+ NotModified = 304,
+ /// <summary>
+ /// Equivalent to status code 305.
+ /// Indicates that the requested resource must be accessed through the proxy given by
+ /// the Location field.
+ /// </summary>
+ UseProxy = 305,
+ /// <summary>
+ /// Equivalent to status code 306.
+ /// This status code was used in a previous version of the specification, is no longer used,
+ /// and is reserved for future use.
+ /// </summary>
+ Unused = 306,
+ /// <summary>
+ /// <para>
+ /// Equivalent to status code 307.
+ /// Indicates that the requested resource is located temporarily under a different URI.
+ /// </para>
+ /// <para>
+ /// TemporaryRedirect is a synonym for RedirectKeepVerb.
+ /// </para>
+ /// </summary>
+ TemporaryRedirect = 307,
+ /// <summary>
+ /// <para>
+ /// Equivalent to status code 307.
+ /// Indicates that the requested resource is located temporarily under a different URI.
+ /// </para>
+ /// <para>
+ /// RedirectKeepVerb is a synonym for TemporaryRedirect.
+ /// </para>
+ /// </summary>
+ RedirectKeepVerb = 307,
+ /// <summary>
+ /// Equivalent to status code 400.
+ /// Indicates that the client's request couldn't be understood by the server due to
+ /// malformed syntax.
+ /// </summary>
+ BadRequest = 400,
+ /// <summary>
+ /// Equivalent to status code 401.
+ /// Indicates that the client's request requires user authentication.
+ /// </summary>
+ Unauthorized = 401,
+ /// <summary>
+ /// Equivalent to status code 402.
+ /// This status code is reserved for future use.
+ /// </summary>
+ PaymentRequired = 402,
+ /// <summary>
+ /// Equivalent to status code 403.
+ /// Indicates that the server understood the client's request but is refusing to fulfill it.
+ /// </summary>
+ Forbidden = 403,
+ /// <summary>
+ /// Equivalent to status code 404.
+ /// Indicates that the server hasn't found anything matching the request URI.
+ /// </summary>
+ NotFound = 404,
+ /// <summary>
+ /// Equivalent to status code 405.
+ /// Indicates that the method specified in the request line isn't allowed for the resource
+ /// identified by the request URI.
+ /// </summary>
+ MethodNotAllowed = 405,
+ /// <summary>
+ /// Equivalent to status code 406.
+ /// Indicates that the server doesn't have the appropriate resource to respond to the Accept
+ /// headers in the client's request.
+ /// </summary>
+ NotAcceptable = 406,
+ /// <summary>
+ /// Equivalent to status code 407.
+ /// Indicates that the client must first authenticate itself with the proxy.
+ /// </summary>
+ ProxyAuthenticationRequired = 407,
+ /// <summary>
+ /// Equivalent to status code 408.
+ /// Indicates that the client didn't produce a request within the time that the server was
+ /// prepared to wait.
+ /// </summary>
+ RequestTimeout = 408,
+ /// <summary>
+ /// Equivalent to status code 409.
+ /// Indicates that the client's request couldn't be completed due to a conflict on the server.
+ /// </summary>
+ Conflict = 409,
+ /// <summary>
+ /// Equivalent to status code 410.
+ /// Indicates that the requested resource is no longer available at the server and
+ /// no forwarding address is known.
+ /// </summary>
+ Gone = 410,
+ /// <summary>
+ /// Equivalent to status code 411.
+ /// Indicates that the server refuses to accept the client's request without a defined
+ /// Content-Length.
+ /// </summary>
+ LengthRequired = 411,
+ /// <summary>
+ /// Equivalent to status code 412.
+ /// Indicates that the precondition given in one or more of the request headers evaluated to
+ /// false when it was tested on the server.
+ /// </summary>
+ PreconditionFailed = 412,
+ /// <summary>
+ /// Equivalent to status code 413.
+ /// Indicates that the entity of the client's request is larger than the server is willing or
+ /// able to process.
+ /// </summary>
+ RequestEntityTooLarge = 413,
+ /// <summary>
+ /// Equivalent to status code 414.
+ /// Indicates that the request URI is longer than the server is willing to interpret.
+ /// </summary>
+ RequestUriTooLong = 414,
+ /// <summary>
+ /// Equivalent to status code 415.
+ /// Indicates that the entity of the client's request is in a format not supported by
+ /// the requested resource for the requested method.
+ /// </summary>
+ UnsupportedMediaType = 415,
+ /// <summary>
+ /// Equivalent to status code 416.
+ /// Indicates that none of the range specifier values in a Range request header overlap
+ /// the current extent of the selected resource.
+ /// </summary>
+ RequestedRangeNotSatisfiable = 416,
+ /// <summary>
+ /// Equivalent to status code 417.
+ /// Indicates that the expectation given in an Expect request header couldn't be met by
+ /// the server.
+ /// </summary>
+ ExpectationFailed = 417,
+ /// <summary>
+ /// Equivalent to status code 500.
+ /// Indicates that the server encountered an unexpected condition which prevented it from
+ /// fulfilling the client's request.
+ /// </summary>
+ InternalServerError = 500,
+ /// <summary>
+ /// Equivalent to status code 501.
+ /// Indicates that the server doesn't support the functionality required to fulfill the client's
+ /// request.
+ /// </summary>
+ NotImplemented = 501,
+ /// <summary>
+ /// Equivalent to status code 502.
+ /// Indicates that a gateway or proxy server received an invalid response from the upstream
+ /// server.
+ /// </summary>
+ BadGateway = 502,
+ /// <summary>
+ /// Equivalent to status code 503.
+ /// Indicates that the server is currently unable to handle the client's request due to
+ /// a temporary overloading or maintenance of the server.
+ /// </summary>
+ ServiceUnavailable = 503,
+ /// <summary>
+ /// Equivalent to status code 504.
+ /// Indicates that a gateway or proxy server didn't receive a timely response from the upstream
+ /// server or some other auxiliary server.
+ /// </summary>
+ GatewayTimeout = 504,
+ /// <summary>
+ /// Equivalent to status code 505.
+ /// Indicates that the server doesn't support the HTTP version used in the client's request.
+ /// </summary>
+ HttpVersionNotSupported = 505,
+ }
+}
diff --git a/SocketHttpListener.Portable/Net/HttpStreamAsyncResult.cs b/SocketHttpListener.Portable/Net/HttpStreamAsyncResult.cs
new file mode 100644
index 0000000000..518c45acba
--- /dev/null
+++ b/SocketHttpListener.Portable/Net/HttpStreamAsyncResult.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Threading;
+
+namespace SocketHttpListener.Net
+{
+ class HttpStreamAsyncResult : IAsyncResult
+ {
+ object locker = new object();
+ ManualResetEvent handle;
+ bool completed;
+
+ internal byte[] Buffer;
+ internal int Offset;
+ internal int Count;
+ internal AsyncCallback Callback;
+ internal object State;
+ internal int SynchRead;
+ internal Exception Error;
+
+ public void Complete(Exception e)
+ {
+ Error = e;
+ Complete();
+ }
+
+ public void Complete()
+ {
+ lock (locker)
+ {
+ if (completed)
+ return;
+
+ completed = true;
+ if (handle != null)
+ handle.Set();
+
+ if (Callback != null)
+ Callback.BeginInvoke(this, null, null);
+ }
+ }
+
+ public object AsyncState
+ {
+ get { return State; }
+ }
+
+ public WaitHandle AsyncWaitHandle
+ {
+ get
+ {
+ lock (locker)
+ {
+ if (handle == null)
+ handle = new ManualResetEvent(completed);
+ }
+
+ return handle;
+ }
+ }
+
+ public bool CompletedSynchronously
+ {
+ get { return (SynchRead == Count); }
+ }
+
+ public bool IsCompleted
+ {
+ get
+ {
+ lock (locker)
+ {
+ return completed;
+ }
+ }
+ }
+ }
+}
diff --git a/SocketHttpListener.Portable/Net/HttpVersion.cs b/SocketHttpListener.Portable/Net/HttpVersion.cs
new file mode 100644
index 0000000000..c0839b46d5
--- /dev/null
+++ b/SocketHttpListener.Portable/Net/HttpVersion.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace SocketHttpListener.Net
+{
+ // <remarks>
+ // </remarks>
+ public class HttpVersion
+ {
+
+ public static readonly Version Version10 = new Version(1, 0);
+ public static readonly Version Version11 = new Version(1, 1);
+
+ // pretty useless..
+ public HttpVersion() { }
+ }
+}
diff --git a/SocketHttpListener.Portable/Net/ListenerPrefix.cs b/SocketHttpListener.Portable/Net/ListenerPrefix.cs
new file mode 100644
index 0000000000..2c314da506
--- /dev/null
+++ b/SocketHttpListener.Portable/Net/ListenerPrefix.cs
@@ -0,0 +1,148 @@
+using System;
+using System.Net;
+using MediaBrowser.Model.Net;
+
+namespace SocketHttpListener.Net
+{
+ sealed class ListenerPrefix
+ {
+ string original;
+ string host;
+ ushort port;
+ string path;
+ bool secure;
+ IpAddressInfo[] addresses;
+ public HttpListener Listener;
+
+ public ListenerPrefix(string prefix)
+ {
+ this.original = prefix;
+ Parse(prefix);
+ }
+
+ public override string ToString()
+ {
+ return original;
+ }
+
+ public IpAddressInfo[] Addresses
+ {
+ get { return addresses; }
+ set { addresses = value; }
+ }
+ public bool Secure
+ {
+ get { return secure; }
+ }
+
+ public string Host
+ {
+ get { return host; }
+ }
+
+ public int Port
+ {
+ get { return (int)port; }
+ }
+
+ public string Path
+ {
+ get { return path; }
+ }
+
+ // Equals and GetHashCode are required to detect duplicates in HttpListenerPrefixCollection.
+ public override bool Equals(object o)
+ {
+ ListenerPrefix other = o as ListenerPrefix;
+ if (other == null)
+ return false;
+
+ return (original == other.original);
+ }
+
+ public override int GetHashCode()
+ {
+ return original.GetHashCode();
+ }
+
+ void Parse(string uri)
+ {
+ ushort default_port = 80;
+ if (uri.StartsWith("https://"))
+ {
+ default_port = 443;
+ secure = true;
+ }
+
+ 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);
+ int root;
+ if (colon > 0)
+ {
+ 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);
+ }
+ else
+ {
+ root = uri.IndexOf('/', start_host, length - start_host);
+ 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 '/'");
+ }
+ }
+}
diff --git a/SocketHttpListener.Portable/Net/RequestStream.cs b/SocketHttpListener.Portable/Net/RequestStream.cs
new file mode 100644
index 0000000000..58030500d1
--- /dev/null
+++ b/SocketHttpListener.Portable/Net/RequestStream.cs
@@ -0,0 +1,231 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace SocketHttpListener.Net
+{
+ class RequestStream : Stream
+ {
+ byte[] buffer;
+ int offset;
+ int length;
+ long remaining_body;
+ bool disposed;
+ Stream stream;
+
+ internal RequestStream(Stream stream, byte[] buffer, int offset, int length)
+ : this(stream, buffer, offset, length, -1)
+ {
+ }
+
+ internal RequestStream(Stream stream, byte[] buffer, int offset, int length, long contentlength)
+ {
+ this.stream = stream;
+ this.buffer = buffer;
+ this.offset = offset;
+ this.length = length;
+ this.remaining_body = contentlength;
+ }
+
+ public override bool CanRead
+ {
+ get { return true; }
+ }
+
+ public override bool CanSeek
+ {
+ get { return false; }
+ }
+
+ public override bool CanWrite
+ {
+ get { return false; }
+ }
+
+ public override long Length
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ public override long Position
+ {
+ get { throw new NotSupportedException(); }
+ set { throw new NotSupportedException(); }
+ }
+
+
+ protected override void Dispose(bool disposing)
+ {
+ disposed = true;
+ }
+
+ public override void Flush()
+ {
+ }
+
+
+ // Returns 0 if we can keep reading from the base stream,
+ // > 0 if we read something from the buffer.
+ // -1 if we had a content length set and we finished reading that many bytes.
+ int FillFromBuffer(byte[] buffer, int off, int count)
+ {
+ if (buffer == null)
+ throw new ArgumentNullException("buffer");
+ if (off < 0)
+ throw new ArgumentOutOfRangeException("offset", "< 0");
+ if (count < 0)
+ throw new ArgumentOutOfRangeException("count", "< 0");
+ int len = buffer.Length;
+ if (off > len)
+ throw new ArgumentException("destination offset is beyond array size");
+ if (off > len - count)
+ throw new ArgumentException("Reading would overrun buffer");
+
+ if (this.remaining_body == 0)
+ return -1;
+
+ if (this.length == 0)
+ return 0;
+
+ int size = Math.Min(this.length, count);
+ if (this.remaining_body > 0)
+ size = (int)Math.Min(size, this.remaining_body);
+
+ if (this.offset > this.buffer.Length - size)
+ {
+ size = Math.Min(size, this.buffer.Length - this.offset);
+ }
+ if (size == 0)
+ return 0;
+
+ Buffer.BlockCopy(this.buffer, this.offset, buffer, off, size);
+ this.offset += size;
+ this.length -= size;
+ if (this.remaining_body > 0)
+ remaining_body -= size;
+ return size;
+ }
+
+ public override int Read([In, Out] byte[] buffer, int offset, int count)
+ {
+ if (disposed)
+ throw new ObjectDisposedException(typeof(RequestStream).ToString());
+
+ // Call FillFromBuffer to check for buffer boundaries even when remaining_body is 0
+ int nread = FillFromBuffer(buffer, offset, count);
+ if (nread == -1)
+ { // No more bytes available (Content-Length)
+ return 0;
+ }
+ else if (nread > 0)
+ {
+ return nread;
+ }
+
+ nread = stream.Read(buffer, offset, count);
+ if (nread > 0 && remaining_body > 0)
+ remaining_body -= nread;
+ return nread;
+ }
+
+ public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ if (disposed)
+ throw new ObjectDisposedException(typeof(RequestStream).ToString());
+
+ int nread = FillFromBuffer(buffer, offset, count);
+ if (nread > 0 || nread == -1)
+ {
+ return Math.Max(0, nread);
+ }
+
+ // Avoid reading past the end of the request to allow
+ // for HTTP pipelining
+ if (remaining_body >= 0 && count > remaining_body)
+ count = (int)Math.Min(Int32.MaxValue, remaining_body);
+
+ nread = await stream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
+ if (remaining_body > 0 && nread > 0)
+ remaining_body -= nread;
+ return nread;
+ }
+
+ //public override IAsyncResult BeginRead(byte[] buffer, int offset, int count,
+ // AsyncCallback cback, object state)
+ //{
+ // if (disposed)
+ // throw new ObjectDisposedException(typeof(RequestStream).ToString());
+
+ // int nread = FillFromBuffer(buffer, offset, count);
+ // if (nread > 0 || nread == -1)
+ // {
+ // HttpStreamAsyncResult ares = new HttpStreamAsyncResult();
+ // ares.Buffer = buffer;
+ // ares.Offset = offset;
+ // ares.Count = count;
+ // ares.Callback = cback;
+ // ares.State = state;
+ // ares.SynchRead = Math.Max(0, nread);
+ // ares.Complete();
+ // return ares;
+ // }
+
+ // // Avoid reading past the end of the request to allow
+ // // for HTTP pipelining
+ // if (remaining_body >= 0 && count > remaining_body)
+ // count = (int)Math.Min(Int32.MaxValue, remaining_body);
+ // return stream.BeginRead(buffer, offset, count, cback, state);
+ //}
+
+ //public override int EndRead(IAsyncResult ares)
+ //{
+ // if (disposed)
+ // throw new ObjectDisposedException(typeof(RequestStream).ToString());
+
+ // if (ares == null)
+ // throw new ArgumentNullException("async_result");
+
+ // if (ares is HttpStreamAsyncResult)
+ // {
+ // HttpStreamAsyncResult r = (HttpStreamAsyncResult)ares;
+ // if (!ares.IsCompleted)
+ // ares.AsyncWaitHandle.WaitOne();
+ // return r.SynchRead;
+ // }
+
+ // // Close on exception?
+ // int nread = stream.EndRead(ares);
+ // if (remaining_body > 0 && nread > 0)
+ // remaining_body -= nread;
+ // return nread;
+ //}
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ throw new NotSupportedException();
+ }
+
+ //public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count,
+ // AsyncCallback cback, object state)
+ //{
+ // throw new NotSupportedException();
+ //}
+
+ //public override void EndWrite(IAsyncResult async_result)
+ //{
+ // throw new NotSupportedException();
+ //}
+ }
+}
diff --git a/SocketHttpListener.Portable/Net/ResponseStream.cs b/SocketHttpListener.Portable/Net/ResponseStream.cs
new file mode 100644
index 0000000000..6067a89ec6
--- /dev/null
+++ b/SocketHttpListener.Portable/Net/ResponseStream.cs
@@ -0,0 +1,293 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Text;
+using SocketHttpListener.Primitives;
+
+namespace SocketHttpListener.Net
+{
+ // FIXME: Does this buffer the response until Close?
+ // Update: we send a single packet for the first non-chunked Write
+ // What happens when we set content-length to X and write X-1 bytes then close?
+ // what if we don't set content-length at all?
+ public class ResponseStream : Stream
+ {
+ HttpListenerResponse response;
+ bool disposed;
+ bool trailer_sent;
+ Stream stream;
+ private readonly IMemoryStreamFactory _memoryStreamFactory;
+ private readonly ITextEncoding _textEncoding;
+
+ internal ResponseStream(Stream stream, HttpListenerResponse response, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding)
+ {
+ this.response = response;
+ _memoryStreamFactory = memoryStreamFactory;
+ _textEncoding = textEncoding;
+ this.stream = stream;
+ }
+
+ public override bool CanRead
+ {
+ get { return false; }
+ }
+
+ public override bool CanSeek
+ {
+ get { return false; }
+ }
+
+ public override bool CanWrite
+ {
+ get { return true; }
+ }
+
+ public override long Length
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ public override long Position
+ {
+ get { throw new NotSupportedException(); }
+ set { throw new NotSupportedException(); }
+ }
+
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposed == false)
+ {
+ disposed = true;
+ byte[] bytes = null;
+ MemoryStream ms = GetHeaders(response, _memoryStreamFactory, false);
+ bool chunked = response.SendChunked;
+ if (stream.CanWrite)
+ {
+ try
+ {
+ if (ms != null)
+ {
+ long start = ms.Position;
+ if (chunked && !trailer_sent)
+ {
+ bytes = GetChunkSizeBytes(0, true);
+ ms.Position = ms.Length;
+ ms.Write(bytes, 0, bytes.Length);
+ }
+ byte[] msBuffer;
+ _memoryStreamFactory.TryGetBuffer(ms, out msBuffer);
+ InternalWrite(msBuffer, (int)start, (int)(ms.Length - start));
+ trailer_sent = true;
+ }
+ else if (chunked && !trailer_sent)
+ {
+ bytes = GetChunkSizeBytes(0, true);
+ InternalWrite(bytes, 0, bytes.Length);
+ trailer_sent = true;
+ }
+ }
+ catch (IOException ex)
+ {
+ // Ignore error due to connection reset by peer
+ }
+ }
+ response.Close();
+ }
+
+ base.Dispose(disposing);
+ }
+
+ internal static MemoryStream GetHeaders(HttpListenerResponse response, IMemoryStreamFactory memoryStreamFactory, bool closing)
+ {
+ // SendHeaders works on shared headers
+ lock (response.headers_lock)
+ {
+ if (response.HeadersSent)
+ return null;
+ MemoryStream ms = memoryStreamFactory.CreateNew();
+ response.SendHeaders(closing, ms);
+ return ms;
+ }
+ }
+
+ public override void Flush()
+ {
+ }
+
+ static byte[] crlf = new byte[] { 13, 10 };
+ byte[] GetChunkSizeBytes(int size, bool final)
+ {
+ string str = String.Format("{0:x}\r\n{1}", size, final ? "\r\n" : "");
+ return _textEncoding.GetASCIIEncoding().GetBytes(str);
+ }
+
+ internal void InternalWrite(byte[] buffer, int offset, int count)
+ {
+ stream.Write(buffer, offset, count);
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ if (disposed)
+ throw new ObjectDisposedException(GetType().ToString());
+
+ byte[] bytes = null;
+ MemoryStream ms = GetHeaders(response, _memoryStreamFactory, false);
+ bool chunked = response.SendChunked;
+ if (ms != null)
+ {
+ long start = ms.Position; // After the possible preamble for the encoding
+ ms.Position = ms.Length;
+ if (chunked)
+ {
+ bytes = GetChunkSizeBytes(count, false);
+ ms.Write(bytes, 0, bytes.Length);
+ }
+
+ int new_count = Math.Min(count, 16384 - (int)ms.Position + (int)start);
+ ms.Write(buffer, offset, new_count);
+ count -= new_count;
+ offset += new_count;
+ byte[] msBuffer;
+ _memoryStreamFactory.TryGetBuffer(ms, out msBuffer);
+ InternalWrite(msBuffer, (int)start, (int)(ms.Length - start));
+ ms.SetLength(0);
+ ms.Capacity = 0; // 'dispose' the buffer in ms.
+ }
+ else if (chunked)
+ {
+ bytes = GetChunkSizeBytes(count, false);
+ InternalWrite(bytes, 0, bytes.Length);
+ }
+
+ if (count > 0)
+ InternalWrite(buffer, offset, count);
+ if (chunked)
+ InternalWrite(crlf, 0, 2);
+ }
+
+ public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ if (disposed)
+ throw new ObjectDisposedException(GetType().ToString());
+
+ byte[] bytes = null;
+ MemoryStream ms = GetHeaders(response, _memoryStreamFactory, false);
+ bool chunked = response.SendChunked;
+ if (ms != null)
+ {
+ long start = ms.Position;
+ ms.Position = ms.Length;
+ if (chunked)
+ {
+ bytes = GetChunkSizeBytes(count, false);
+ ms.Write(bytes, 0, bytes.Length);
+ }
+ ms.Write(buffer, offset, count);
+ byte[] msBuffer;
+ _memoryStreamFactory.TryGetBuffer(ms, out msBuffer);
+ buffer = msBuffer;
+ offset = (int)start;
+ count = (int)(ms.Position - start);
+ }
+ else if (chunked)
+ {
+ bytes = GetChunkSizeBytes(count, false);
+ InternalWrite(bytes, 0, bytes.Length);
+ }
+
+ if (count > 0)
+ {
+ await stream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
+ }
+
+ if (response.SendChunked)
+ stream.Write(crlf, 0, 2);
+ }
+
+ //public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count,
+ // AsyncCallback cback, object state)
+ //{
+ // if (disposed)
+ // throw new ObjectDisposedException(GetType().ToString());
+
+ // byte[] bytes = null;
+ // MemoryStream ms = GetHeaders(false);
+ // bool chunked = response.SendChunked;
+ // if (ms != null)
+ // {
+ // long start = ms.Position;
+ // ms.Position = ms.Length;
+ // if (chunked)
+ // {
+ // bytes = GetChunkSizeBytes(count, false);
+ // ms.Write(bytes, 0, bytes.Length);
+ // }
+ // ms.Write(buffer, offset, count);
+ // buffer = ms.ToArray();
+ // offset = (int)start;
+ // count = (int)(ms.Position - start);
+ // }
+ // else if (chunked)
+ // {
+ // bytes = GetChunkSizeBytes(count, false);
+ // InternalWrite(bytes, 0, bytes.Length);
+ // }
+
+ // return stream.BeginWrite(buffer, offset, count, cback, state);
+ //}
+
+ //public override void EndWrite(IAsyncResult ares)
+ //{
+ // if (disposed)
+ // throw new ObjectDisposedException(GetType().ToString());
+
+ // if (ignore_errors)
+ // {
+ // try
+ // {
+ // stream.EndWrite(ares);
+ // if (response.SendChunked)
+ // stream.Write(crlf, 0, 2);
+ // }
+ // catch { }
+ // }
+ // else {
+ // stream.EndWrite(ares);
+ // if (response.SendChunked)
+ // stream.Write(crlf, 0, 2);
+ // }
+ //}
+
+ public override int Read([In, Out] byte[] buffer, int offset, int count)
+ {
+ throw new NotSupportedException();
+ }
+
+ //public override IAsyncResult BeginRead(byte[] buffer, int offset, int count,
+ // AsyncCallback cback, object state)
+ //{
+ // throw new NotSupportedException();
+ //}
+
+ //public override int EndRead(IAsyncResult ares)
+ //{
+ // throw new NotSupportedException();
+ //}
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException();
+ }
+ }
+}
diff --git a/SocketHttpListener.Portable/Net/WebHeaderCollection.cs b/SocketHttpListener.Portable/Net/WebHeaderCollection.cs
new file mode 100644
index 0000000000..d20f99b9b8
--- /dev/null
+++ b/SocketHttpListener.Portable/Net/WebHeaderCollection.cs
@@ -0,0 +1,391 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Net;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+using System.Text;
+using MediaBrowser.Model.Services;
+
+namespace SocketHttpListener.Net
+{
+ [ComVisible(true)]
+ public class WebHeaderCollection : QueryParamCollection
+ {
+ [Flags]
+ internal enum HeaderInfo
+ {
+ Request = 1,
+ Response = 1 << 1,
+ MultiValue = 1 << 10
+ }
+
+ static readonly bool[] allowed_chars = {
+ false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, false, false, false, false, false, false,
+ false, false, false, false, false, true, false, true, true, true, true, false, false, false, true,
+ true, false, true, true, false, true, true, true, true, true, true, true, true, true, true, false,
+ false, false, false, false, false, false, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ false, false, false, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
+ false, true, false
+ };
+
+ static readonly Dictionary<string, HeaderInfo> headers;
+ HeaderInfo? headerRestriction;
+ HeaderInfo? headerConsistency;
+
+ static WebHeaderCollection()
+ {
+ headers = new Dictionary<string, HeaderInfo>(StringComparer.OrdinalIgnoreCase) {
+ { "Allow", HeaderInfo.MultiValue },
+ { "Accept", HeaderInfo.Request | HeaderInfo.MultiValue },
+ { "Accept-Charset", HeaderInfo.MultiValue },
+ { "Accept-Encoding", HeaderInfo.MultiValue },
+ { "Accept-Language", HeaderInfo.MultiValue },
+ { "Accept-Ranges", HeaderInfo.MultiValue },
+ { "Age", HeaderInfo.Response },
+ { "Authorization", HeaderInfo.MultiValue },
+ { "Cache-Control", HeaderInfo.MultiValue },
+ { "Cookie", HeaderInfo.MultiValue },
+ { "Connection", HeaderInfo.Request | HeaderInfo.MultiValue },
+ { "Content-Encoding", HeaderInfo.MultiValue },
+ { "Content-Length", HeaderInfo.Request | HeaderInfo.Response },
+ { "Content-Type", HeaderInfo.Request },
+ { "Content-Language", HeaderInfo.MultiValue },
+ { "Date", HeaderInfo.Request },
+ { "Expect", HeaderInfo.Request | HeaderInfo.MultiValue},
+ { "Host", HeaderInfo.Request },
+ { "If-Match", HeaderInfo.MultiValue },
+ { "If-Modified-Since", HeaderInfo.Request },
+ { "If-None-Match", HeaderInfo.MultiValue },
+ { "Keep-Alive", HeaderInfo.Response },
+ { "Pragma", HeaderInfo.MultiValue },
+ { "Proxy-Authenticate", HeaderInfo.MultiValue },
+ { "Proxy-Authorization", HeaderInfo.MultiValue },
+ { "Proxy-Connection", HeaderInfo.Request | HeaderInfo.MultiValue },
+ { "Range", HeaderInfo.Request | HeaderInfo.MultiValue },
+ { "Referer", HeaderInfo.Request },
+ { "Set-Cookie", HeaderInfo.MultiValue },
+ { "Set-Cookie2", HeaderInfo.MultiValue },
+ { "Server", HeaderInfo.Response },
+ { "TE", HeaderInfo.MultiValue },
+ { "Trailer", HeaderInfo.MultiValue },
+ { "Transfer-Encoding", HeaderInfo.Request | HeaderInfo.Response | HeaderInfo.MultiValue },
+ { "Translate", HeaderInfo.Request | HeaderInfo.Response },
+ { "Upgrade", HeaderInfo.MultiValue },
+ { "User-Agent", HeaderInfo.Request },
+ { "Vary", HeaderInfo.MultiValue },
+ { "Via", HeaderInfo.MultiValue },
+ { "Warning", HeaderInfo.MultiValue },
+ { "WWW-Authenticate", HeaderInfo.Response | HeaderInfo. MultiValue },
+ { "SecWebSocketAccept", HeaderInfo.Response },
+ { "SecWebSocketExtensions", HeaderInfo.Request | HeaderInfo.Response | HeaderInfo. MultiValue },
+ { "SecWebSocketKey", HeaderInfo.Request },
+ { "Sec-WebSocket-Protocol", HeaderInfo.Request | HeaderInfo.Response | HeaderInfo. MultiValue },
+ { "SecWebSocketVersion", HeaderInfo.Response | HeaderInfo. MultiValue }
+ };
+ }
+
+ // Methods
+
+ public void Add(string header)
+ {
+ if (header == null)
+ throw new ArgumentNullException("header");
+ int pos = header.IndexOf(':');
+ if (pos == -1)
+ throw new ArgumentException("no colon found", "header");
+
+ this.Add(header.Substring(0, pos), header.Substring(pos + 1));
+ }
+
+ public override void Add(string name, string value)
+ {
+ if (name == null)
+ throw new ArgumentNullException("name");
+
+ ThrowIfRestricted(name);
+ this.AddWithoutValidate(name, value);
+ }
+
+ protected void AddWithoutValidate(string headerName, string headerValue)
+ {
+ if (!IsHeaderName(headerName))
+ throw new ArgumentException("invalid header name: " + headerName, "headerName");
+ if (headerValue == null)
+ headerValue = String.Empty;
+ else
+ headerValue = headerValue.Trim();
+ if (!IsHeaderValue(headerValue))
+ throw new ArgumentException("invalid header value: " + headerValue, "headerValue");
+
+ AddValue(headerName, headerValue);
+ }
+
+ internal void AddValue(string headerName, string headerValue)
+ {
+ base.Add(headerName, headerValue);
+ }
+
+ internal string[] GetValues_internal(string header, bool split)
+ {
+ if (header == null)
+ throw new ArgumentNullException("header");
+
+ string[] values = base.GetValues(header);
+ if (values == null || values.Length == 0)
+ return null;
+
+ if (split && IsMultiValue(header))
+ {
+ List<string> separated = null;
+ foreach (var value in values)
+ {
+ if (value.IndexOf(',') < 0)
+ {
+ if (separated != null)
+ separated.Add(value);
+
+ continue;
+ }
+
+ if (separated == null)
+ {
+ separated = new List<string>(values.Length + 1);
+ foreach (var v in values)
+ {
+ if (v == value)
+ break;
+
+ separated.Add(v);
+ }
+ }
+
+ var slices = value.Split(',');
+ var slices_length = slices.Length;
+ if (value[value.Length - 1] == ',')
+ --slices_length;
+
+ for (int i = 0; i < slices_length; ++i)
+ {
+ separated.Add(slices[i].Trim());
+ }
+ }
+
+ if (separated != null)
+ return separated.ToArray();
+ }
+
+ return values;
+ }
+
+ public override string[] GetValues(string header)
+ {
+ return GetValues_internal(header, true);
+ }
+
+ public override string[] GetValues(int index)
+ {
+ string[] values = base.GetValues(index);
+
+ if (values == null || values.Length == 0)
+ {
+ return null;
+ }
+
+ return values;
+ }
+
+ public static bool IsRestricted(string headerName)
+ {
+ return IsRestricted(headerName, false);
+ }
+
+ public static bool IsRestricted(string headerName, bool response)
+ {
+ if (headerName == null)
+ throw new ArgumentNullException("headerName");
+
+ if (headerName.Length == 0)
+ throw new ArgumentException("empty string", "headerName");
+
+ if (!IsHeaderName(headerName))
+ throw new ArgumentException("Invalid character in header");
+
+ HeaderInfo info;
+ if (!headers.TryGetValue(headerName, out info))
+ return false;
+
+ var flag = response ? HeaderInfo.Response : HeaderInfo.Request;
+ return (info & flag) != 0;
+ }
+
+ public override void Set(string name, string value)
+ {
+ if (name == null)
+ throw new ArgumentNullException("name");
+ if (!IsHeaderName(name))
+ throw new ArgumentException("invalid header name");
+ if (value == null)
+ value = String.Empty;
+ else
+ value = value.Trim();
+ if (!IsHeaderValue(value))
+ throw new ArgumentException("invalid header value");
+
+ ThrowIfRestricted(name);
+ base.Set(name, value);
+ }
+
+ internal string ToStringMultiValue()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ int count = base.Count;
+ for (int i = 0; i < count; i++)
+ {
+ string key = GetKey(i);
+ if (IsMultiValue(key))
+ {
+ foreach (string v in GetValues(i))
+ {
+ sb.Append(key)
+ .Append(": ")
+ .Append(v)
+ .Append("\r\n");
+ }
+ }
+ else
+ {
+ sb.Append(key)
+ .Append(": ")
+ .Append(Get(i))
+ .Append("\r\n");
+ }
+ }
+ return sb.Append("\r\n").ToString();
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ int count = base.Count;
+ for (int i = 0; i < count; i++)
+ sb.Append(GetKey(i))
+ .Append(": ")
+ .Append(Get(i))
+ .Append("\r\n");
+
+ return sb.Append("\r\n").ToString();
+ }
+
+
+ // Internal Methods
+
+ // With this we don't check for invalid characters in header. See bug #55994.
+ internal void SetInternal(string header)
+ {
+ int pos = header.IndexOf(':');
+ if (pos == -1)
+ throw new ArgumentException("no colon found", "header");
+
+ SetInternal(header.Substring(0, pos), header.Substring(pos + 1));
+ }
+
+ internal void SetInternal(string name, string value)
+ {
+ if (value == null)
+ value = String.Empty;
+ else
+ value = value.Trim();
+ if (!IsHeaderValue(value))
+ throw new ArgumentException("invalid header value");
+
+ if (IsMultiValue(name))
+ {
+ base.Add(name, value);
+ }
+ else
+ {
+ base.Remove(name);
+ base.Set(name, value);
+ }
+ }
+
+ // 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)
+ return false;
+
+ HeaderInfo info;
+ return headers.TryGetValue(headerName, out info) && (info & HeaderInfo.MultiValue) != 0;
+ }
+
+ internal static bool IsHeaderValue(string value)
+ {
+ // TEXT any 8 bit value except CTL's (0-31 and 127)
+ // but including \r\n space and \t
+ // after a newline at least one space or \t must follow
+ // certain header fields allow comments ()
+
+ int len = value.Length;
+ for (int i = 0; i < len; i++)
+ {
+ char c = value[i];
+ if (c == 127)
+ return false;
+ if (c < 0x20 && (c != '\r' && c != '\n' && c != '\t'))
+ return false;
+ if (c == '\n' && ++i < len)
+ {
+ c = value[i];
+ if (c != ' ' && c != '\t')
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ internal static bool IsHeaderName(string name)
+ {
+ if (name == null || name.Length == 0)
+ return false;
+
+ int len = name.Length;
+ for (int i = 0; i < len; i++)
+ {
+ char c = name[i];
+ if (c > 126 || !allowed_chars[c])
+ return false;
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/SocketHttpListener.Portable/Net/WebSockets/HttpListenerWebSocketContext.cs b/SocketHttpListener.Portable/Net/WebSockets/HttpListenerWebSocketContext.cs
new file mode 100644
index 0000000000..034ac17d20
--- /dev/null
+++ b/SocketHttpListener.Portable/Net/WebSockets/HttpListenerWebSocketContext.cs
@@ -0,0 +1,348 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.IO;
+using System.Net;
+using System.Security.Principal;
+using MediaBrowser.Model.Cryptography;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Services;
+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 HttpListenerContext _context;
+ private WebSocket _websocket;
+
+ #endregion
+
+ #region Internal Constructors
+
+ internal HttpListenerWebSocketContext(
+ HttpListenerContext context, string protocol, ICryptoProvider cryptoProvider, IMemoryStreamFactory memoryStreamFactory)
+ {
+ _context = context;
+ _websocket = new WebSocket(this, protocol, cryptoProvider, memoryStreamFactory);
+ }
+
+ #endregion
+
+ #region Internal Properties
+
+ internal Stream Stream
+ {
+ get
+ {
+ return _context.Connection.Stream;
+ }
+ }
+
+ #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 override CookieCollection CookieCollection
+ {
+ get
+ {
+ return _context.Request.Cookies;
+ }
+ }
+
+ /// <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;
+ }
+ }
+
+ /// <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"];
+ }
+ }
+
+ /// <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;
+ }
+ }
+
+ /// <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;
+ }
+ }
+
+ /// <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;
+ }
+ }
+
+ /// <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;
+ }
+ }
+
+ /// <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"];
+ }
+ }
+
+ /// <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;
+ }
+ }
+
+ /// <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;
+ }
+ }
+
+ /// <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
+ {
+ get
+ {
+ return _context.Request.Headers["Sec-WebSocket-Key"];
+ }
+ }
+
+ /// <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 IpEndPointInfo 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 IpEndPointInfo UserEndPoint
+ {
+ get
+ {
+ return _context.Connection.RemoteEndPoint;
+ }
+ }
+
+ /// <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.Portable/Net/WebSockets/WebSocketContext.cs b/SocketHttpListener.Portable/Net/WebSockets/WebSocketContext.cs
new file mode 100644
index 0000000000..3ffa6e639a
--- /dev/null
+++ b/SocketHttpListener.Portable/Net/WebSockets/WebSocketContext.cs
@@ -0,0 +1,183 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Net;
+using System.Security.Principal;
+using MediaBrowser.Model.Net;
+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 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 IpEndPointInfo 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 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 IpEndPointInfo 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 WebSocket WebSocket { get; }
+
+ #endregion
+ }
+}
diff --git a/SocketHttpListener.Portable/Opcode.cs b/SocketHttpListener.Portable/Opcode.cs
new file mode 100644
index 0000000000..62b7d8585c
--- /dev/null
+++ b/SocketHttpListener.Portable/Opcode.cs
@@ -0,0 +1,43 @@
+namespace SocketHttpListener
+{
+ /// <summary>
+ /// Contains the values of the opcode that indicates the type of a WebSocket frame.
+ /// </summary>
+ /// <remarks>
+ /// The values of the opcode are defined in
+ /// <see href="http://tools.ietf.org/html/rfc6455#section-5.2">Section 5.2</see> of RFC 6455.
+ /// </remarks>
+ public enum Opcode : byte
+ {
+ /// <summary>
+ /// Equivalent to numeric value 0.
+ /// Indicates a continuation frame.
+ /// </summary>
+ Cont = 0x0,
+ /// <summary>
+ /// Equivalent to numeric value 1.
+ /// Indicates a text frame.
+ /// </summary>
+ Text = 0x1,
+ /// <summary>
+ /// Equivalent to numeric value 2.
+ /// Indicates a binary frame.
+ /// </summary>
+ Binary = 0x2,
+ /// <summary>
+ /// Equivalent to numeric value 8.
+ /// Indicates a connection close frame.
+ /// </summary>
+ Close = 0x8,
+ /// <summary>
+ /// Equivalent to numeric value 9.
+ /// Indicates a ping frame.
+ /// </summary>
+ Ping = 0x9,
+ /// <summary>
+ /// Equivalent to numeric value 10.
+ /// Indicates a pong frame.
+ /// </summary>
+ Pong = 0xa
+ }
+}
diff --git a/SocketHttpListener.Portable/PayloadData.cs b/SocketHttpListener.Portable/PayloadData.cs
new file mode 100644
index 0000000000..a6318da2b8
--- /dev/null
+++ b/SocketHttpListener.Portable/PayloadData.cs
@@ -0,0 +1,149 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+
+namespace SocketHttpListener
+{
+ internal class PayloadData : IEnumerable<byte>
+ {
+ #region Private Fields
+
+ private byte [] _applicationData;
+ private byte [] _extensionData;
+ private bool _masked;
+
+ #endregion
+
+ #region Public Const Fields
+
+ public const ulong MaxLength = long.MaxValue;
+
+ #endregion
+
+ #region Public Constructors
+
+ public PayloadData ()
+ : this (new byte [0], new byte [0], false)
+ {
+ }
+
+ public PayloadData (byte [] applicationData)
+ : this (new byte [0], applicationData, false)
+ {
+ }
+
+ public PayloadData (string applicationData)
+ : this (new byte [0], Encoding.UTF8.GetBytes (applicationData), false)
+ {
+ }
+
+ public PayloadData (byte [] applicationData, bool masked)
+ : this (new byte [0], applicationData, masked)
+ {
+ }
+
+ public PayloadData (byte [] extensionData, byte [] applicationData, bool masked)
+ {
+ _extensionData = extensionData;
+ _applicationData = applicationData;
+ _masked = masked;
+ }
+
+ #endregion
+
+ #region Internal Properties
+
+ internal bool ContainsReservedCloseStatusCode {
+ get {
+ return _applicationData.Length > 1 &&
+ _applicationData.SubArray (0, 2).ToUInt16 (ByteOrder.Big).IsReserved ();
+ }
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ public byte [] ApplicationData {
+ get {
+ return _applicationData;
+ }
+ }
+
+ public byte [] ExtensionData {
+ get {
+ return _extensionData;
+ }
+ }
+
+ public bool IsMasked {
+ get {
+ return _masked;
+ }
+ }
+
+ public ulong Length {
+ get {
+ return (ulong) (_extensionData.Length + _applicationData.Length);
+ }
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private static void mask (byte [] src, byte [] key)
+ {
+ for (long i = 0; i < src.Length; i++)
+ src [i] = (byte) (src [i] ^ key [i % 4]);
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ public IEnumerator<byte> GetEnumerator ()
+ {
+ foreach (byte b in _extensionData)
+ yield return b;
+
+ foreach (byte b in _applicationData)
+ yield return b;
+ }
+
+ public void Mask (byte [] maskingKey)
+ {
+ if (_extensionData.Length > 0)
+ mask (_extensionData, maskingKey);
+
+ if (_applicationData.Length > 0)
+ mask (_applicationData, maskingKey);
+
+ _masked = !_masked;
+ }
+
+ public byte [] ToByteArray ()
+ {
+ return _extensionData.Length > 0
+ ? new List<byte> (this).ToArray ()
+ : _applicationData;
+ }
+
+ public override string ToString ()
+ {
+ return BitConverter.ToString (ToByteArray ());
+ }
+
+ #endregion
+
+ #region Explicitly Implemented Interface Members
+
+ IEnumerator IEnumerable.GetEnumerator ()
+ {
+ return GetEnumerator ();
+ }
+
+ #endregion
+ }
+}
diff --git a/SocketHttpListener.Portable/Primitives/HttpListenerException.cs b/SocketHttpListener.Portable/Primitives/HttpListenerException.cs
new file mode 100644
index 0000000000..7b383fd230
--- /dev/null
+++ b/SocketHttpListener.Portable/Primitives/HttpListenerException.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SocketHttpListener.Primitives
+{
+ public class HttpListenerException : Exception
+ {
+ public HttpListenerException(int statusCode, string message)
+ : base(message)
+ {
+
+ }
+ }
+}
diff --git a/SocketHttpListener.Portable/Primitives/ICertificate.cs b/SocketHttpListener.Portable/Primitives/ICertificate.cs
new file mode 100644
index 0000000000..1289da13d7
--- /dev/null
+++ b/SocketHttpListener.Portable/Primitives/ICertificate.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SocketHttpListener.Primitives
+{
+ public interface ICertificate
+ {
+ }
+}
diff --git a/SocketHttpListener.Portable/Primitives/IStreamFactory.cs b/SocketHttpListener.Portable/Primitives/IStreamFactory.cs
new file mode 100644
index 0000000000..f189b95b47
--- /dev/null
+++ b/SocketHttpListener.Portable/Primitives/IStreamFactory.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Net;
+
+namespace SocketHttpListener.Primitives
+{
+ public interface IStreamFactory
+ {
+ Stream CreateNetworkStream(ISocket socket, bool ownsSocket);
+ Stream CreateSslStream(Stream innerStream, bool leaveInnerStreamOpen);
+
+ Task AuthenticateSslStreamAsServer(Stream stream, ICertificate certificate);
+ }
+}
diff --git a/SocketHttpListener.Portable/Primitives/ITextEncoding.cs b/SocketHttpListener.Portable/Primitives/ITextEncoding.cs
new file mode 100644
index 0000000000..b10145687b
--- /dev/null
+++ b/SocketHttpListener.Portable/Primitives/ITextEncoding.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Text;
+
+namespace SocketHttpListener.Primitives
+{
+ public static class TextEncodingExtensions
+ {
+ public static Encoding GetDefaultEncoding(this ITextEncoding encoding)
+ {
+ return Encoding.UTF8;
+ }
+ }
+}
diff --git a/SocketHttpListener.Portable/Properties/AssemblyInfo.cs b/SocketHttpListener.Portable/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..8704264603
--- /dev/null
+++ b/SocketHttpListener.Portable/Properties/AssemblyInfo.cs
@@ -0,0 +1,30 @@
+using System.Resources;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("SocketHttpListener.Portable")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("SocketHttpListener.Portable")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[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
+//
+// 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/SocketHttpListener.Portable/Rsv.cs b/SocketHttpListener.Portable/Rsv.cs
new file mode 100644
index 0000000000..668059b8a3
--- /dev/null
+++ b/SocketHttpListener.Portable/Rsv.cs
@@ -0,0 +1,8 @@
+namespace SocketHttpListener
+{
+ internal enum Rsv : byte
+ {
+ Off = 0x0,
+ On = 0x1
+ }
+}
diff --git a/SocketHttpListener.Portable/SocketHttpListener.Portable.csproj b/SocketHttpListener.Portable/SocketHttpListener.Portable.csproj
new file mode 100644
index 0000000000..ee902462b0
--- /dev/null
+++ b/SocketHttpListener.Portable/SocketHttpListener.Portable.csproj
@@ -0,0 +1,108 @@
+<?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>{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>SocketHttpListener.Portable</RootNamespace>
+ <AssemblyName>SocketHttpListener.Portable</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="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\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\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.cs" />
+ <Compile Include="Net\HttpStatusCode.cs" />
+ <Compile Include="Net\HttpStreamAsyncResult.cs" />
+ <Compile Include="Net\HttpVersion.cs" />
+ <Compile Include="Net\ListenerPrefix.cs" />
+ <Compile Include="Net\RequestStream.cs" />
+ <Compile Include="Net\ResponseStream.cs" />
+ <Compile Include="Net\WebHeaderCollection.cs" />
+ <Compile Include="Net\WebSockets\HttpListenerWebSocketContext.cs" />
+ <Compile Include="Net\WebSockets\WebSocketContext.cs" />
+ <Compile Include="Opcode.cs" />
+ <Compile Include="PayloadData.cs" />
+ <Compile Include="Primitives\HttpListenerException.cs" />
+ <Compile Include="Primitives\ICertificate.cs" />
+ <Compile Include="Primitives\IStreamFactory.cs" />
+ <Compile Include="Primitives\ITextEncoding.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Rsv.cs" />
+ <Compile Include="WebSocket.cs" />
+ <Compile Include="WebSocketException.cs" />
+ <Compile Include="WebSocketFrame.cs" />
+ <Compile Include="WebSocketState.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="packages.config" />
+ </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>
+ </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/SocketHttpListener.Portable/SocketHttpListener.Portable.nuget.targets b/SocketHttpListener.Portable/SocketHttpListener.Portable.nuget.targets
new file mode 100644
index 0000000000..e69ce0e64f
--- /dev/null
+++ b/SocketHttpListener.Portable/SocketHttpListener.Portable.nuget.targets
@@ -0,0 +1,6 @@
+<?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/SocketHttpListener.Portable/WebSocket.cs b/SocketHttpListener.Portable/WebSocket.cs
new file mode 100644
index 0000000000..9966d3fcf9
--- /dev/null
+++ b/SocketHttpListener.Portable/WebSocket.cs
@@ -0,0 +1,887 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Cryptography;
+using MediaBrowser.Model.IO;
+using SocketHttpListener.Net.WebSockets;
+using SocketHttpListener.Primitives;
+using HttpStatusCode = SocketHttpListener.Net.HttpStatusCode;
+
+namespace SocketHttpListener
+{
+ /// <summary>
+ /// Implements the WebSocket interface.
+ /// </summary>
+ /// <remarks>
+ /// The WebSocket class provides a set of methods and properties for two-way communication using
+ /// the WebSocket protocol (<see href="http://tools.ietf.org/html/rfc6455">RFC 6455</see>).
+ /// </remarks>
+ public class WebSocket : IDisposable
+ {
+ #region Private Fields
+
+ private string _base64Key;
+ private Action _closeContext;
+ private CompressionMethod _compression;
+ private WebSocketContext _context;
+ private CookieCollection _cookies;
+ private string _extensions;
+ private AutoResetEvent _exitReceiving;
+ private object _forConn;
+ private object _forEvent;
+ private object _forMessageEventQueue;
+ private object _forSend;
+ private const string _guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+ private Func<WebSocketContext, string>
+ _handshakeRequestChecker;
+ private Queue<MessageEventArgs> _messageEventQueue;
+ private uint _nonceCount;
+ private string _origin;
+ private bool _preAuth;
+ private string _protocol;
+ private string[] _protocols;
+ private Uri _proxyUri;
+ private volatile WebSocketState _readyState;
+ private AutoResetEvent _receivePong;
+ private bool _secure;
+ private Stream _stream;
+ private Uri _uri;
+ private const string _version = "13";
+ private readonly IMemoryStreamFactory _memoryStreamFactory;
+
+ private readonly ICryptoProvider _cryptoProvider;
+
+ #endregion
+
+ #region Internal Fields
+
+ internal const int FragmentLength = 1016; // Max value is int.MaxValue - 14.
+
+ #endregion
+
+ #region Internal Constructors
+
+ // As server
+ internal WebSocket(HttpListenerWebSocketContext context, string protocol, ICryptoProvider cryptoProvider, IMemoryStreamFactory memoryStreamFactory)
+ {
+ _context = context;
+ _protocol = protocol;
+ _cryptoProvider = cryptoProvider;
+ _memoryStreamFactory = memoryStreamFactory;
+
+ _closeContext = context.Close;
+ _secure = context.IsSecureConnection;
+ _stream = context.Stream;
+
+ init();
+ }
+
+ #endregion
+
+ // As server
+ internal Func<WebSocketContext, string> CustomHandshakeRequestChecker
+ {
+ get
+ {
+ return _handshakeRequestChecker ?? (context => null);
+ }
+
+ set
+ {
+ _handshakeRequestChecker = value;
+ }
+ }
+
+ internal bool IsConnected
+ {
+ get
+ {
+ return _readyState == WebSocketState.Open || _readyState == WebSocketState.Closing;
+ }
+ }
+
+ /// <summary>
+ /// Gets the state of the WebSocket connection.
+ /// </summary>
+ /// <value>
+ /// One of the <see cref="WebSocketState"/> enum values, indicates the state of the WebSocket
+ /// connection. The default value is <see cref="WebSocketState.Connecting"/>.
+ /// </value>
+ public WebSocketState ReadyState
+ {
+ get
+ {
+ return _readyState;
+ }
+ }
+
+ #region Public Events
+
+ /// <summary>
+ /// Occurs when the WebSocket connection has been closed.
+ /// </summary>
+ public event EventHandler<CloseEventArgs> OnClose;
+
+ /// <summary>
+ /// Occurs when the <see cref="WebSocket"/> gets an error.
+ /// </summary>
+ public event EventHandler<ErrorEventArgs> OnError;
+
+ /// <summary>
+ /// Occurs when the <see cref="WebSocket"/> receives a message.
+ /// </summary>
+ public event EventHandler<MessageEventArgs> OnMessage;
+
+ /// <summary>
+ /// Occurs when the WebSocket connection has been established.
+ /// </summary>
+ public event EventHandler OnOpen;
+
+ #endregion
+
+ #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);
+ }
+
+ private void close(PayloadData payload, bool send, bool wait)
+ {
+ lock (_forConn)
+ {
+ if (_readyState == WebSocketState.Closing || _readyState == WebSocketState.Closed)
+ {
+ return;
+ }
+
+ _readyState = WebSocketState.Closing;
+ }
+
+ var e = new CloseEventArgs(payload);
+ e.WasClean =
+ closeHandshake(
+ send ? WebSocketFrame.CreateCloseFrame(Mask.Unmask, payload).ToByteArray() : null,
+ wait ? 1000 : 0,
+ closeServerResources);
+
+ _readyState = WebSocketState.Closed;
+ try
+ {
+ OnClose.Emit(this, e);
+ }
+ catch (Exception ex)
+ {
+ error("An exception has occurred while OnClose.", ex);
+ }
+ }
+
+ private bool closeHandshake(byte[] frameAsBytes, int millisecondsTimeout, Action release)
+ {
+ var sent = frameAsBytes != null && writeBytes(frameAsBytes);
+ var received =
+ millisecondsTimeout == 0 ||
+ (sent && _exitReceiving != null && _exitReceiving.WaitOne(millisecondsTimeout));
+
+ release();
+ if (_receivePong != null)
+ {
+ _receivePong.Dispose();
+ _receivePong = null;
+ }
+
+ if (_exitReceiving != null)
+ {
+ _exitReceiving.Dispose();
+ _exitReceiving = null;
+ }
+
+ var result = sent && received;
+
+ return result;
+ }
+
+ // As server
+ private void closeServerResources()
+ {
+ if (_closeContext == null)
+ return;
+
+ _closeContext();
+ _closeContext = null;
+ _stream = null;
+ _context = null;
+ }
+
+ private bool concatenateFragmentsInto(Stream dest)
+ {
+ while (true)
+ {
+ var frame = WebSocketFrame.Read(_stream, true);
+ if (frame.IsFinal)
+ {
+ /* FINAL */
+
+ // CONT
+ if (frame.IsContinuation)
+ {
+ dest.WriteBytes(frame.PayloadData.ApplicationData);
+ break;
+ }
+
+ // PING
+ if (frame.IsPing)
+ {
+ processPingFrame(frame);
+ continue;
+ }
+
+ // PONG
+ if (frame.IsPong)
+ {
+ processPongFrame(frame);
+ continue;
+ }
+
+ // CLOSE
+ if (frame.IsClose)
+ return processCloseFrame(frame);
+ }
+ else
+ {
+ /* MORE */
+
+ // CONT
+ if (frame.IsContinuation)
+ {
+ dest.WriteBytes(frame.PayloadData.ApplicationData);
+ continue;
+ }
+ }
+
+ // ?
+ return processUnsupportedFrame(
+ frame,
+ CloseStatusCode.IncorrectData,
+ "An incorrect data has been received while receiving fragmented data.");
+ }
+
+ return true;
+ }
+
+ // As server
+ private HttpResponse createHandshakeCloseResponse(HttpStatusCode code)
+ {
+ var res = HttpResponse.CreateCloseResponse(code);
+ res.Headers["Sec-WebSocket-Version"] = _version;
+
+ 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)
+ return _messageEventQueue.Count > 0
+ ? _messageEventQueue.Dequeue()
+ : null;
+ }
+
+ private void enqueueToMessageEventQueue(MessageEventArgs e)
+ {
+ lock (_forMessageEventQueue)
+ _messageEventQueue.Enqueue(e);
+ }
+
+ private void error(string message, Exception exception)
+ {
+ try
+ {
+ if (exception != null)
+ {
+ message += ". Exception.Message: " + exception.Message;
+ }
+ OnError.Emit(this, new ErrorEventArgs(message));
+ }
+ catch (Exception ex)
+ {
+ }
+ }
+
+ private void error(string message)
+ {
+ try
+ {
+ OnError.Emit(this, new ErrorEventArgs(message));
+ }
+ catch (Exception ex)
+ {
+ }
+ }
+
+ private void init()
+ {
+ _compression = CompressionMethod.None;
+ _cookies = new CookieCollection();
+ _forConn = new object();
+ _forEvent = new object();
+ _forSend = new object();
+ _messageEventQueue = new Queue<MessageEventArgs>();
+ _forMessageEventQueue = ((ICollection)_messageEventQueue).SyncRoot;
+ _readyState = WebSocketState.Connecting;
+ }
+
+ private void open()
+ {
+ try
+ {
+ startReceiving();
+
+ lock (_forEvent)
+ {
+ try
+ {
+ OnOpen.Emit(this, EventArgs.Empty);
+ }
+ catch (Exception ex)
+ {
+ processException(ex, "An exception has occurred while OnOpen.");
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ processException(ex, "An exception has occurred while opening.");
+ }
+ }
+
+ private bool processCloseFrame(WebSocketFrame frame)
+ {
+ var payload = frame.PayloadData;
+ close(payload, !payload.ContainsReservedCloseStatusCode, false);
+
+ return false;
+ }
+
+ private bool processDataFrame(WebSocketFrame frame)
+ {
+ var e = frame.IsCompressed
+ ? new MessageEventArgs(
+ frame.Opcode, frame.PayloadData.ApplicationData.Decompress(_compression))
+ : new MessageEventArgs(frame.Opcode, frame.PayloadData);
+
+ enqueueToMessageEventQueue(e);
+ return true;
+ }
+
+ private void processException(Exception exception, string message)
+ {
+ var code = CloseStatusCode.Abnormal;
+ var reason = message;
+ if (exception is WebSocketException)
+ {
+ var wsex = (WebSocketException)exception;
+ code = wsex.Code;
+ reason = wsex.Message;
+ }
+
+ error(message ?? code.GetMessage(), exception);
+ if (_readyState == WebSocketState.Connecting)
+ Close(HttpStatusCode.BadRequest);
+ else
+ close(code, reason ?? code.GetMessage(), false);
+ }
+
+ private bool processFragmentedFrame(WebSocketFrame frame)
+ {
+ return frame.IsContinuation // Not first fragment
+ ? true
+ : processFragments(frame);
+ }
+
+ private bool processFragments(WebSocketFrame first)
+ {
+ using (var buff = _memoryStreamFactory.CreateNew())
+ {
+ buff.WriteBytes(first.PayloadData.ApplicationData);
+ if (!concatenateFragmentsInto(buff))
+ return false;
+
+ byte[] data;
+ if (_compression != CompressionMethod.None)
+ {
+ data = buff.DecompressToArray(_compression);
+ }
+ else
+ {
+ data = buff.ToArray();
+ }
+
+ enqueueToMessageEventQueue(new MessageEventArgs(first.Opcode, data));
+ return true;
+ }
+ }
+
+ private bool processPingFrame(WebSocketFrame frame)
+ {
+ var mask = Mask.Unmask;
+
+ return true;
+ }
+
+ private bool processPongFrame(WebSocketFrame frame)
+ {
+ _receivePong.Set();
+
+ return true;
+ }
+
+ private bool processUnsupportedFrame(WebSocketFrame frame, CloseStatusCode code, string reason)
+ {
+ processException(new WebSocketException(code, reason), null);
+
+ return false;
+ }
+
+ private bool processWebSocketFrame(WebSocketFrame frame)
+ {
+ return frame.IsCompressed && _compression == CompressionMethod.None
+ ? processUnsupportedFrame(
+ frame,
+ CloseStatusCode.IncorrectData,
+ "A compressed data has been received without available decompression method.")
+ : frame.IsFragmented
+ ? processFragmentedFrame(frame)
+ : frame.IsData
+ ? processDataFrame(frame)
+ : frame.IsPing
+ ? processPingFrame(frame)
+ : frame.IsPong
+ ? processPongFrame(frame)
+ : frame.IsClose
+ ? processCloseFrame(frame)
+ : processUnsupportedFrame(frame, CloseStatusCode.PolicyViolation, null);
+ }
+
+ private bool send(Opcode opcode, Stream stream)
+ {
+ lock (_forSend)
+ {
+ var src = stream;
+ var compressed = false;
+ var sent = false;
+ try
+ {
+ if (_compression != CompressionMethod.None)
+ {
+ stream = stream.Compress(_compression);
+ compressed = true;
+ }
+
+ sent = send(opcode, Mask.Unmask, stream, compressed);
+ if (!sent)
+ error("Sending a data has been interrupted.");
+ }
+ catch (Exception ex)
+ {
+ error("An exception has occurred while sending a data.", ex);
+ }
+ finally
+ {
+ if (compressed)
+ stream.Dispose();
+
+ src.Dispose();
+ }
+
+ return sent;
+ }
+ }
+
+ private bool send(Opcode opcode, Mask mask, Stream stream, bool compressed)
+ {
+ var len = stream.Length;
+
+ /* Not fragmented */
+
+ if (len == 0)
+ return send(Fin.Final, opcode, mask, new byte[0], compressed);
+
+ var quo = len / FragmentLength;
+ var rem = (int)(len % FragmentLength);
+
+ byte[] buff = null;
+ if (quo == 0)
+ {
+ buff = new byte[rem];
+ return stream.Read(buff, 0, rem) == rem &&
+ send(Fin.Final, opcode, mask, buff, compressed);
+ }
+
+ buff = new byte[FragmentLength];
+ if (quo == 1 && rem == 0)
+ return stream.Read(buff, 0, FragmentLength) == FragmentLength &&
+ send(Fin.Final, opcode, mask, buff, compressed);
+
+ /* Send fragmented */
+
+ // Begin
+ if (stream.Read(buff, 0, FragmentLength) != FragmentLength ||
+ !send(Fin.More, opcode, mask, buff, compressed))
+ return false;
+
+ var n = rem == 0 ? quo - 2 : quo - 1;
+ for (long i = 0; i < n; i++)
+ if (stream.Read(buff, 0, FragmentLength) != FragmentLength ||
+ !send(Fin.More, Opcode.Cont, mask, buff, compressed))
+ return false;
+
+ // End
+ if (rem == 0)
+ rem = FragmentLength;
+ else
+ buff = new byte[rem];
+
+ return stream.Read(buff, 0, rem) == rem &&
+ send(Fin.Final, Opcode.Cont, mask, buff, compressed);
+ }
+
+ private bool send(Fin fin, Opcode opcode, Mask mask, byte[] data, bool compressed)
+ {
+ lock (_forConn)
+ {
+ if (_readyState != WebSocketState.Open)
+ {
+ return false;
+ }
+
+ return writeBytes(
+ WebSocketFrame.CreateWebSocketFrame(fin, opcode, mask, data, compressed).ToByteArray());
+ }
+ }
+
+ private Task sendAsync(Opcode opcode, Stream stream)
+ {
+ var completionSource = new TaskCompletionSource<bool>();
+ Task.Run(() =>
+ {
+ try
+ {
+ send(opcode, stream);
+ completionSource.TrySetResult(true);
+ }
+ catch (Exception ex)
+ {
+ completionSource.TrySetException(ex);
+ }
+ });
+ return completionSource.Task;
+ }
+
+ // As server
+ private bool sendHttpResponse(HttpResponse response)
+ {
+ return writeBytes(response.ToByteArray());
+ }
+
+ private void startReceiving()
+ {
+ if (_messageEventQueue.Count > 0)
+ _messageEventQueue.Clear();
+
+ _exitReceiving = new AutoResetEvent(false);
+ _receivePong = new AutoResetEvent(false);
+
+ Action receive = null;
+ receive = () => WebSocketFrame.ReadAsync(
+ _stream,
+ true,
+ frame =>
+ {
+ if (processWebSocketFrame(frame) && _readyState != WebSocketState.Closed)
+ {
+ receive();
+
+ if (!frame.IsData)
+ return;
+
+ lock (_forEvent)
+ {
+ try
+ {
+ var e = dequeueFromMessageEventQueue();
+ if (e != null && _readyState == WebSocketState.Open)
+ OnMessage.Emit(this, e);
+ }
+ catch (Exception ex)
+ {
+ processException(ex, "An exception has occurred while OnMessage.");
+ }
+ }
+ }
+ else if (_exitReceiving != null)
+ {
+ _exitReceiving.Set();
+ }
+ },
+ ex => processException(ex, "An exception has occurred while receiving a message."));
+
+ 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
+ {
+ _stream.Write(data, 0, data.Length);
+ return true;
+ }
+ catch (Exception ex)
+ {
+ return false;
+ }
+ }
+
+ #endregion
+
+ #region Internal Methods
+
+ // As server
+ internal void Close(HttpResponse response)
+ {
+ _readyState = WebSocketState.Closing;
+
+ sendHttpResponse(response);
+ closeServerResources();
+
+ _readyState = WebSocketState.Closed;
+ }
+
+ // As server
+ internal void Close(HttpStatusCode code)
+ {
+ Close(createHandshakeCloseResponse(code));
+ }
+
+ // As server
+ public void ConnectAsServer()
+ {
+ try
+ {
+ if (acceptHandshake())
+ {
+ _readyState = WebSocketState.Open;
+ open();
+ }
+ }
+ catch (Exception ex)
+ {
+ processException(ex, "An exception has occurred while connecting.");
+ }
+ }
+
+ 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
+
+ /// <summary>
+ /// Closes the WebSocket connection, and releases all associated resources.
+ /// </summary>
+ public void Close()
+ {
+ var msg = _readyState.CheckIfClosable();
+ if (msg != null)
+ {
+ error(msg);
+
+ return;
+ }
+
+ var send = _readyState == WebSocketState.Open;
+ close(new PayloadData(), send, send);
+ }
+
+ /// <summary>
+ /// Closes the WebSocket connection with the specified <see cref="CloseStatusCode"/>
+ /// and <see cref="string"/>, and releases all associated resources.
+ /// </summary>
+ /// <remarks>
+ /// This method emits a <see cref="OnError"/> event if the size
+ /// of <paramref name="reason"/> is greater than 123 bytes.
+ /// </remarks>
+ /// <param name="code">
+ /// One of the <see cref="CloseStatusCode"/> enum values, represents the status code
+ /// indicating the reason for the close.
+ /// </param>
+ /// <param name="reason">
+ /// A <see cref="string"/> that represents the reason for the close.
+ /// </param>
+ public void Close(CloseStatusCode code, string reason)
+ {
+ byte[] data = null;
+ var msg = _readyState.CheckIfClosable() ??
+ (data = ((ushort)code).Append(reason)).CheckIfValidControlData("reason");
+
+ if (msg != null)
+ {
+ error(msg);
+
+ return;
+ }
+
+ var send = _readyState == WebSocketState.Open && !code.IsReserved();
+ close(new PayloadData(data), send, send);
+ }
+
+ /// <summary>
+ /// Sends a binary <paramref name="data"/> asynchronously using the WebSocket connection.
+ /// </summary>
+ /// <remarks>
+ /// This method doesn't wait for the send to be complete.
+ /// </remarks>
+ /// <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 (msg != null)
+ {
+ throw new Exception(msg);
+ }
+
+ return sendAsync(Opcode.Binary, _memoryStreamFactory.CreateNew(data));
+ }
+
+ /// <summary>
+ /// Sends a text <paramref name="data"/> asynchronously using the WebSocket connection.
+ /// </summary>
+ /// <remarks>
+ /// This method doesn't wait for the send to be complete.
+ /// </remarks>
+ /// <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 (msg != null)
+ {
+ throw new Exception(msg);
+ }
+
+ return sendAsync(Opcode.Text, _memoryStreamFactory.CreateNew(Encoding.UTF8.GetBytes(data)));
+ }
+
+ #endregion
+
+ #region Explicit Interface Implementation
+
+ /// <summary>
+ /// Closes the WebSocket connection, and releases all associated resources.
+ /// </summary>
+ /// <remarks>
+ /// This method closes the WebSocket connection with <see cref="CloseStatusCode.Away"/>.
+ /// </remarks>
+ void IDisposable.Dispose()
+ {
+ Close(CloseStatusCode.Away, null);
+ }
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/SocketHttpListener.Portable/WebSocketException.cs b/SocketHttpListener.Portable/WebSocketException.cs
new file mode 100644
index 0000000000..260721317c
--- /dev/null
+++ b/SocketHttpListener.Portable/WebSocketException.cs
@@ -0,0 +1,60 @@
+using System;
+
+namespace SocketHttpListener
+{
+ /// <summary>
+ /// The exception that is thrown when a <see cref="WebSocket"/> gets a fatal error.
+ /// </summary>
+ public class WebSocketException : Exception
+ {
+ #region Internal Constructors
+
+ internal WebSocketException ()
+ : this (CloseStatusCode.Abnormal, null, null)
+ {
+ }
+
+ internal WebSocketException (string message)
+ : this (CloseStatusCode.Abnormal, message, null)
+ {
+ }
+
+ internal WebSocketException (CloseStatusCode code)
+ : this (code, null, null)
+ {
+ }
+
+ internal WebSocketException (string message, Exception innerException)
+ : this (CloseStatusCode.Abnormal, message, innerException)
+ {
+ }
+
+ internal WebSocketException (CloseStatusCode code, string message)
+ : this (code, message, null)
+ {
+ }
+
+ internal WebSocketException (CloseStatusCode code, string message, Exception innerException)
+ : base (message ?? code.GetMessage (), innerException)
+ {
+ Code = code;
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ /// <summary>
+ /// Gets the status code indicating the cause for the exception.
+ /// </summary>
+ /// <value>
+ /// One of the <see cref="CloseStatusCode"/> enum values, represents the status code indicating
+ /// the cause for the exception.
+ /// </value>
+ public CloseStatusCode Code {
+ get; private set;
+ }
+
+ #endregion
+ }
+}
diff --git a/SocketHttpListener.Portable/WebSocketFrame.cs b/SocketHttpListener.Portable/WebSocketFrame.cs
new file mode 100644
index 0000000000..44fa4a5dc3
--- /dev/null
+++ b/SocketHttpListener.Portable/WebSocketFrame.cs
@@ -0,0 +1,578 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace SocketHttpListener
+{
+ internal class WebSocketFrame : IEnumerable<byte>
+ {
+ #region Private Fields
+
+ private byte[] _extPayloadLength;
+ private Fin _fin;
+ private Mask _mask;
+ private byte[] _maskingKey;
+ private Opcode _opcode;
+ private PayloadData _payloadData;
+ private byte _payloadLength;
+ private Rsv _rsv1;
+ private Rsv _rsv2;
+ private Rsv _rsv3;
+
+ #endregion
+
+ #region Internal Fields
+
+ internal static readonly byte[] EmptyUnmaskPingData;
+
+ #endregion
+
+ #region Static Constructor
+
+ static WebSocketFrame()
+ {
+ EmptyUnmaskPingData = CreatePingFrame(Mask.Unmask).ToByteArray();
+ }
+
+ #endregion
+
+ #region Private Constructors
+
+ private WebSocketFrame()
+ {
+ }
+
+ #endregion
+
+ #region Internal Constructors
+
+ internal WebSocketFrame(Opcode opcode, PayloadData payload)
+ : this(Fin.Final, opcode, Mask.Mask, payload, false)
+ {
+ }
+
+ internal WebSocketFrame(Opcode opcode, Mask mask, PayloadData payload)
+ : this(Fin.Final, opcode, mask, payload, false)
+ {
+ }
+
+ internal WebSocketFrame(Fin fin, Opcode opcode, Mask mask, PayloadData payload)
+ : this(fin, opcode, mask, payload, false)
+ {
+ }
+
+ internal WebSocketFrame(
+ Fin fin, Opcode opcode, Mask mask, PayloadData payload, bool compressed)
+ {
+ _fin = fin;
+ _rsv1 = isData(opcode) && compressed ? Rsv.On : Rsv.Off;
+ _rsv2 = Rsv.Off;
+ _rsv3 = Rsv.Off;
+ _opcode = opcode;
+ _mask = mask;
+
+ var len = payload.Length;
+ if (len < 126)
+ {
+ _payloadLength = (byte)len;
+ _extPayloadLength = new byte[0];
+ }
+ else if (len < 0x010000)
+ {
+ _payloadLength = (byte)126;
+ _extPayloadLength = ((ushort)len).ToByteArrayInternally(ByteOrder.Big);
+ }
+ else
+ {
+ _payloadLength = (byte)127;
+ _extPayloadLength = len.ToByteArrayInternally(ByteOrder.Big);
+ }
+
+ if (mask == Mask.Mask)
+ {
+ _maskingKey = createMaskingKey();
+ payload.Mask(_maskingKey);
+ }
+ else
+ {
+ _maskingKey = new byte[0];
+ }
+
+ _payloadData = payload;
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ public byte[] ExtendedPayloadLength
+ {
+ get
+ {
+ return _extPayloadLength;
+ }
+ }
+
+ public Fin Fin
+ {
+ get
+ {
+ return _fin;
+ }
+ }
+
+ public bool IsBinary
+ {
+ get
+ {
+ return _opcode == Opcode.Binary;
+ }
+ }
+
+ public bool IsClose
+ {
+ get
+ {
+ return _opcode == Opcode.Close;
+ }
+ }
+
+ public bool IsCompressed
+ {
+ get
+ {
+ return _rsv1 == Rsv.On;
+ }
+ }
+
+ public bool IsContinuation
+ {
+ get
+ {
+ return _opcode == Opcode.Cont;
+ }
+ }
+
+ public bool IsControl
+ {
+ get
+ {
+ return _opcode == Opcode.Close || _opcode == Opcode.Ping || _opcode == Opcode.Pong;
+ }
+ }
+
+ public bool IsData
+ {
+ get
+ {
+ return _opcode == Opcode.Binary || _opcode == Opcode.Text;
+ }
+ }
+
+ public bool IsFinal
+ {
+ get
+ {
+ return _fin == Fin.Final;
+ }
+ }
+
+ public bool IsFragmented
+ {
+ get
+ {
+ return _fin == Fin.More || _opcode == Opcode.Cont;
+ }
+ }
+
+ public bool IsMasked
+ {
+ get
+ {
+ return _mask == Mask.Mask;
+ }
+ }
+
+ public bool IsPerMessageCompressed
+ {
+ get
+ {
+ return (_opcode == Opcode.Binary || _opcode == Opcode.Text) && _rsv1 == Rsv.On;
+ }
+ }
+
+ public bool IsPing
+ {
+ get
+ {
+ return _opcode == Opcode.Ping;
+ }
+ }
+
+ public bool IsPong
+ {
+ get
+ {
+ return _opcode == Opcode.Pong;
+ }
+ }
+
+ public bool IsText
+ {
+ get
+ {
+ return _opcode == Opcode.Text;
+ }
+ }
+
+ public ulong Length
+ {
+ get
+ {
+ return 2 + (ulong)(_extPayloadLength.Length + _maskingKey.Length) + _payloadData.Length;
+ }
+ }
+
+ public Mask Mask
+ {
+ get
+ {
+ return _mask;
+ }
+ }
+
+ public byte[] MaskingKey
+ {
+ get
+ {
+ return _maskingKey;
+ }
+ }
+
+ public Opcode Opcode
+ {
+ get
+ {
+ return _opcode;
+ }
+ }
+
+ public PayloadData PayloadData
+ {
+ get
+ {
+ return _payloadData;
+ }
+ }
+
+ public byte PayloadLength
+ {
+ get
+ {
+ return _payloadLength;
+ }
+ }
+
+ public Rsv Rsv1
+ {
+ get
+ {
+ return _rsv1;
+ }
+ }
+
+ public Rsv Rsv2
+ {
+ get
+ {
+ return _rsv2;
+ }
+ }
+
+ public Rsv Rsv3
+ {
+ get
+ {
+ return _rsv3;
+ }
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private byte[] createMaskingKey()
+ {
+ var key = new byte[4];
+ var rand = new Random();
+ rand.NextBytes(key);
+
+ return key;
+ }
+
+ private static bool isControl(Opcode opcode)
+ {
+ return opcode == Opcode.Close || opcode == Opcode.Ping || opcode == Opcode.Pong;
+ }
+
+ private static bool isData(Opcode opcode)
+ {
+ return opcode == Opcode.Text || opcode == Opcode.Binary;
+ }
+
+ private static WebSocketFrame read(byte[] header, Stream stream, bool unmask)
+ {
+ /* Header */
+
+ // FIN
+ var fin = (header[0] & 0x80) == 0x80 ? Fin.Final : Fin.More;
+ // RSV1
+ var rsv1 = (header[0] & 0x40) == 0x40 ? Rsv.On : Rsv.Off;
+ // RSV2
+ var rsv2 = (header[0] & 0x20) == 0x20 ? Rsv.On : Rsv.Off;
+ // RSV3
+ var rsv3 = (header[0] & 0x10) == 0x10 ? Rsv.On : Rsv.Off;
+ // Opcode
+ var opcode = (Opcode)(header[0] & 0x0f);
+ // MASK
+ var mask = (header[1] & 0x80) == 0x80 ? Mask.Mask : Mask.Unmask;
+ // Payload Length
+ var payloadLen = (byte)(header[1] & 0x7f);
+
+ // Check if correct frame.
+ var incorrect = isControl(opcode) && fin == Fin.More
+ ? "A control frame is fragmented."
+ : !isData(opcode) && rsv1 == Rsv.On
+ ? "A non data frame is compressed."
+ : null;
+
+ if (incorrect != null)
+ throw new WebSocketException(CloseStatusCode.IncorrectData, incorrect);
+
+ // Check if consistent frame.
+ if (isControl(opcode) && payloadLen > 125)
+ throw new WebSocketException(
+ CloseStatusCode.InconsistentData,
+ "The length of payload data of a control frame is greater than 125 bytes.");
+
+ var frame = new WebSocketFrame();
+ frame._fin = fin;
+ frame._rsv1 = rsv1;
+ frame._rsv2 = rsv2;
+ frame._rsv3 = rsv3;
+ frame._opcode = opcode;
+ frame._mask = mask;
+ frame._payloadLength = payloadLen;
+
+ /* Extended Payload Length */
+
+ var size = payloadLen < 126
+ ? 0
+ : payloadLen == 126
+ ? 2
+ : 8;
+
+ var extPayloadLen = size > 0 ? stream.ReadBytes(size) : new byte[0];
+ if (size > 0 && extPayloadLen.Length != size)
+ throw new WebSocketException(
+ "The 'Extended Payload Length' of a frame cannot be read from the data source.");
+
+ frame._extPayloadLength = extPayloadLen;
+
+ /* Masking Key */
+
+ var masked = mask == Mask.Mask;
+ var maskingKey = masked ? stream.ReadBytes(4) : new byte[0];
+ if (masked && maskingKey.Length != 4)
+ throw new WebSocketException(
+ "The 'Masking Key' of a frame cannot be read from the data source.");
+
+ frame._maskingKey = maskingKey;
+
+ /* Payload Data */
+
+ ulong len = payloadLen < 126
+ ? payloadLen
+ : payloadLen == 126
+ ? extPayloadLen.ToUInt16(ByteOrder.Big)
+ : extPayloadLen.ToUInt64(ByteOrder.Big);
+
+ byte[] data = null;
+ if (len > 0)
+ {
+ // Check if allowable payload data length.
+ if (payloadLen > 126 && len > PayloadData.MaxLength)
+ throw new WebSocketException(
+ CloseStatusCode.TooBig,
+ "The length of 'Payload Data' of a frame is greater than the allowable length.");
+
+ data = payloadLen > 126
+ ? stream.ReadBytes((long)len, 1024)
+ : stream.ReadBytes((int)len);
+
+ //if (data.LongLength != (long)len)
+ // throw new WebSocketException(
+ // "The 'Payload Data' of a frame cannot be read from the data source.");
+ }
+ else
+ {
+ data = new byte[0];
+ }
+
+ var payload = new PayloadData(data, masked);
+ if (masked && unmask)
+ {
+ payload.Mask(maskingKey);
+ frame._mask = Mask.Unmask;
+ frame._maskingKey = new byte[0];
+ }
+
+ frame._payloadData = payload;
+ return frame;
+ }
+
+ #endregion
+
+ #region Internal Methods
+
+ internal static WebSocketFrame CreateCloseFrame(Mask mask, byte[] data)
+ {
+ return new WebSocketFrame(Opcode.Close, mask, new PayloadData(data));
+ }
+
+ internal static WebSocketFrame CreateCloseFrame(Mask mask, PayloadData payload)
+ {
+ return new WebSocketFrame(Opcode.Close, mask, payload);
+ }
+
+ internal static WebSocketFrame CreateCloseFrame(Mask mask, CloseStatusCode code, string reason)
+ {
+ return new WebSocketFrame(
+ Opcode.Close, mask, new PayloadData(((ushort)code).Append(reason)));
+ }
+
+ internal static WebSocketFrame CreatePingFrame(Mask mask)
+ {
+ return new WebSocketFrame(Opcode.Ping, mask, new PayloadData());
+ }
+
+ internal static WebSocketFrame CreatePingFrame(Mask mask, byte[] data)
+ {
+ return new WebSocketFrame(Opcode.Ping, mask, new PayloadData(data));
+ }
+
+ internal static WebSocketFrame CreatePongFrame(Mask mask, PayloadData payload)
+ {
+ return new WebSocketFrame(Opcode.Pong, mask, payload);
+ }
+
+ internal static WebSocketFrame CreateWebSocketFrame(
+ Fin fin, Opcode opcode, Mask mask, byte[] data, bool compressed)
+ {
+ return new WebSocketFrame(fin, opcode, mask, new PayloadData(data), compressed);
+ }
+
+ internal static WebSocketFrame Read(Stream stream)
+ {
+ return Read(stream, true);
+ }
+
+ internal static WebSocketFrame Read(Stream stream, bool unmask)
+ {
+ var header = stream.ReadBytes(2);
+ if (header.Length != 2)
+ throw new WebSocketException(
+ "The header part of a frame cannot be read from the data source.");
+
+ return read(header, stream, unmask);
+ }
+
+ internal static async void ReadAsync(
+ Stream stream, bool unmask, Action<WebSocketFrame> completed, Action<Exception> error)
+ {
+ try
+ {
+ var header = await stream.ReadBytesAsync(2).ConfigureAwait(false);
+ if (header.Length != 2)
+ throw new WebSocketException(
+ "The header part of a frame cannot be read from the data source.");
+
+ var frame = read(header, stream, unmask);
+ if (completed != null)
+ completed(frame);
+ }
+ catch (Exception ex)
+ {
+ if (error != null)
+ {
+ error(ex);
+ }
+ }
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ public IEnumerator<byte> GetEnumerator()
+ {
+ foreach (var b in ToByteArray())
+ yield return b;
+ }
+
+ public void Print(bool dumped)
+ {
+ //Console.WriteLine(dumped ? dump(this) : print(this));
+ }
+
+ public byte[] ToByteArray()
+ {
+ using (var buff = new MemoryStream())
+ {
+ var header = (int)_fin;
+ header = (header << 1) + (int)_rsv1;
+ header = (header << 1) + (int)_rsv2;
+ header = (header << 1) + (int)_rsv3;
+ header = (header << 4) + (int)_opcode;
+ header = (header << 1) + (int)_mask;
+ header = (header << 7) + (int)_payloadLength;
+ buff.Write(((ushort)header).ToByteArrayInternally(ByteOrder.Big), 0, 2);
+
+ if (_payloadLength > 125)
+ buff.Write(_extPayloadLength, 0, _extPayloadLength.Length);
+
+ if (_mask == Mask.Mask)
+ buff.Write(_maskingKey, 0, _maskingKey.Length);
+
+ if (_payloadLength > 0)
+ {
+ var payload = _payloadData.ToByteArray();
+ if (_payloadLength < 127)
+ buff.Write(payload, 0, payload.Length);
+ else
+ buff.WriteBytes(payload);
+ }
+
+ return buff.ToArray();
+ }
+ }
+
+ public override string ToString()
+ {
+ return BitConverter.ToString(ToByteArray());
+ }
+
+ #endregion
+
+ #region Explicitly Implemented Interface Members
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/SocketHttpListener.Portable/WebSocketState.cs b/SocketHttpListener.Portable/WebSocketState.cs
new file mode 100644
index 0000000000..73b3a49ddc
--- /dev/null
+++ b/SocketHttpListener.Portable/WebSocketState.cs
@@ -0,0 +1,35 @@
+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
+ }
+}
diff --git a/SocketHttpListener.Portable/packages.config b/SocketHttpListener.Portable/packages.config
new file mode 100644
index 0000000000..2aae715b5a
--- /dev/null
+++ b/SocketHttpListener.Portable/packages.config
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="MediaBrowser.Common" version="3.0.689" targetFramework="portable45-net45+win8" />
+ <package id="Patterns.Logging" version="1.0.0.6" targetFramework="portable45-net45+win8" />
+</packages> \ No newline at end of file
diff --git a/SocketHttpListener.Portable/project.json b/SocketHttpListener.Portable/project.json
new file mode 100644
index 0000000000..fbbe9eaf32
--- /dev/null
+++ b/SocketHttpListener.Portable/project.json
@@ -0,0 +1,17 @@
+{
+ "frameworks":{
+ "netstandard1.6":{
+ "dependencies":{
+ "NETStandard.Library":"1.6.0",
+ }
+ },
+ ".NETPortable,Version=v4.5,Profile=Profile7":{
+ "buildOptions": {
+ "define": [ ]
+ },
+ "frameworkAssemblies":{
+
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/global.json b/global.json
new file mode 100644
index 0000000000..6bc8a74b39
--- /dev/null
+++ b/global.json
@@ -0,0 +1,6 @@
+{
+ "projects": [ "src", "test", "." ],
+ "sdk": {
+ "version": "1.0.0-preview2-003131"
+ }
+}
diff --git a/src/Emby.Server/ApplicationPathHelper.cs b/src/Emby.Server/ApplicationPathHelper.cs
new file mode 100644
index 0000000000..c611ff3727
--- /dev/null
+++ b/src/Emby.Server/ApplicationPathHelper.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Emby.Server
+{
+ public class ApplicationPathHelper
+ {
+ public static string GetProgramDataPath(string appDirectory)
+ {
+ var useDebugPath = false;
+
+#if DEBUG
+ useDebugPath = true;
+#endif
+
+ var programDataPath = useDebugPath ?
+ "programdata" :
+ "programdata";
+
+ programDataPath = programDataPath
+ .Replace('/', Path.DirectorySeparatorChar)
+ .Replace('\\', Path.DirectorySeparatorChar);
+
+ // If it's a relative path, e.g. "..\"
+ if (!Path.IsPathRooted(programDataPath))
+ {
+ programDataPath = Path.Combine(appDirectory, programDataPath);
+
+ programDataPath = Path.GetFullPath(programDataPath);
+ }
+
+ Directory.CreateDirectory(programDataPath);
+
+ return programDataPath;
+ }
+ }
+}
diff --git a/src/Emby.Server/CoreAppHost.cs b/src/Emby.Server/CoreAppHost.cs
new file mode 100644
index 0000000000..09df664faa
--- /dev/null
+++ b/src/Emby.Server/CoreAppHost.cs
@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
+using Emby.Server.Core;
+using Emby.Server.Implementations.FFMpeg;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.System;
+using Emby.Server.Implementations;
+
+namespace Emby.Server
+{
+ public class CoreAppHost : ApplicationHost
+ {
+ public CoreAppHost(ServerApplicationPaths applicationPaths, ILogManager logManager, StartupOptions options, IFileSystem fileSystem, IPowerManagement powerManagement, string releaseAssetFilename, IEnvironmentInfo environmentInfo, MediaBrowser.Controller.Drawing.IImageEncoder imageEncoder, ISystemEvents systemEvents, IMemoryStreamFactory memoryStreamFactory, MediaBrowser.Common.Net.INetworkManager networkManager, Action<string, string> certificateGenerator, Func<string> defaultUsernameFactory)
+ : base(applicationPaths, logManager, options, fileSystem, powerManagement, releaseAssetFilename, environmentInfo, imageEncoder, systemEvents, memoryStreamFactory, networkManager, certificateGenerator, defaultUsernameFactory)
+ {
+ }
+
+ public override bool IsRunningAsService
+ {
+ get { return false; }
+ }
+
+ protected override void RestartInternal()
+ {
+ Program.Restart();
+ }
+
+ protected override void ShutdownInternal()
+ {
+ Program.Shutdown();
+ }
+
+ protected override FFMpegInstallInfo GetFfmpegInstallInfo()
+ {
+ var info = new FFMpegInstallInfo();
+
+ if (EnvironmentInfo.OperatingSystem == OperatingSystem.Windows)
+ {
+ info.FFMpegFilename = "ffmpeg.exe";
+ info.FFProbeFilename = "ffprobe.exe";
+ info.Version = "20160410";
+ info.ArchiveType = "7z";
+ info.DownloadUrls = GetDownloadUrls();
+ }
+
+ return info;
+ }
+
+ private string[] GetDownloadUrls()
+ {
+ switch (EnvironmentInfo.SystemArchitecture)
+ {
+ case Architecture.X64:
+ return new[]
+ {
+ "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20160410-win64.7z"
+ };
+ case Architecture.X86:
+ return new[]
+ {
+ "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20160410-win32.7z"
+ };
+ }
+
+ return new string[] { };
+ }
+
+ protected override List<Assembly> GetAssembliesWithPartsInternal()
+ {
+ var list = new List<Assembly>();
+
+ list.Add(GetType().GetTypeInfo().Assembly);
+
+ return list;
+ }
+
+ protected override void AuthorizeServer()
+ {
+ }
+
+ protected override void ConfigureAutoRunInternal(bool autorun)
+ {
+ }
+
+ protected override void EnableLoopbackInternal(string appName)
+ {
+ }
+
+ public override bool SupportsRunningAsService
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ public override bool CanSelfRestart
+ {
+ get
+ {
+ return Program.CanSelfRestart;
+ }
+ }
+
+ public override bool SupportsAutoRunAtStartup
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ public override bool CanSelfUpdate
+ {
+ get
+ {
+ return Program.CanSelfUpdate;
+ }
+ }
+
+ protected override bool SupportsDualModeSockets
+ {
+ get
+ {
+ return true;
+ }
+ }
+ }
+}
diff --git a/src/Emby.Server/CoreSystemEvents.cs b/src/Emby.Server/CoreSystemEvents.cs
new file mode 100644
index 0000000000..7afb94160d
--- /dev/null
+++ b/src/Emby.Server/CoreSystemEvents.cs
@@ -0,0 +1,13 @@
+using System;
+using MediaBrowser.Model.System;
+
+namespace Emby.Server
+{
+ public class CoreSystemEvents : ISystemEvents
+ {
+ public event EventHandler Resume;
+ public event EventHandler Suspend;
+ public event EventHandler SessionLogoff;
+ public event EventHandler SystemShutdown;
+ }
+}
diff --git a/src/Emby.Server/Emby.Server.xproj b/src/Emby.Server/Emby.Server.xproj
new file mode 100644
index 0000000000..2462c5179f
--- /dev/null
+++ b/src/Emby.Server/Emby.Server.xproj
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
+ <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+ </PropertyGroup>
+ <Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>ddaff431-0b3d-4857-8762-990a32dc8472</ProjectGuid>
+ <RootNamespace>Emby.Server</RootNamespace>
+ <BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
+ <OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
+ <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
+ </PropertyGroup>
+ <PropertyGroup>
+ <SchemaVersion>2.0</SchemaVersion>
+ </PropertyGroup>
+ <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.Api\MediaBrowser.Api.csproj" />
+ <ProjectReference Include="..\..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
+ <ProjectReference Include="..\..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
+ <ProjectReference Include="..\..\MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj" />
+ <ProjectReference Include="..\..\MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj" />
+ <ProjectReference Include="..\..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
+ <ProjectReference Include="..\..\MediaBrowser.Providers\MediaBrowser.Providers.csproj" />
+ <ProjectReference Include="..\..\MediaBrowser.Server.Implementations\MediaBrowser.Server.Implementations.csproj" />
+ <ProjectReference Include="..\..\MediaBrowser.WebDashboard\MediaBrowser.WebDashboard.csproj" />
+ <ProjectReference Include="..\..\MediaBrowser.XbmcMetadata\MediaBrowser.XbmcMetadata.csproj" />
+ <ProjectReference Include="..\..\OpenSubtitlesHandler\OpenSubtitlesHandler.csproj" />
+ <ProjectReference Include="..\..\RSSDP\RSSDP.csproj" />
+ <ProjectReference Include="..\..\ServiceStack\ServiceStack.csproj" />
+ <ProjectReference Include="..\..\SocketHttpListener.Portable\SocketHttpListener.Portable.csproj" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="..\..\SharedVersion.cs">
+ <Link>Properties\SharedVersion.cs</Link>
+ </Compile>
+ </ItemGroup>
+ <Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
+</Project> \ No newline at end of file
diff --git a/src/Emby.Server/IO/MemoryStreamFactory.cs b/src/Emby.Server/IO/MemoryStreamFactory.cs
new file mode 100644
index 0000000000..37ac2959ed
--- /dev/null
+++ b/src/Emby.Server/IO/MemoryStreamFactory.cs
@@ -0,0 +1,33 @@
+using System;
+using System.IO;
+using MediaBrowser.Model.IO;
+
+namespace Emby.Server.IO
+{
+ public class MemoryStreamFactory : 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)
+ {
+ ArraySegment<byte> arrayBuffer;
+ stream.TryGetBuffer(out arrayBuffer);
+
+ buffer = arrayBuffer.Array;
+ return true;
+ }
+ }
+}
diff --git a/src/Emby.Server/PowerManagement.cs b/src/Emby.Server/PowerManagement.cs
new file mode 100644
index 0000000000..85e3b72a69
--- /dev/null
+++ b/src/Emby.Server/PowerManagement.cs
@@ -0,0 +1,15 @@
+using MediaBrowser.Model.System;
+
+namespace Emby.Server
+{
+ public class PowerManagement : IPowerManagement
+ {
+ public void PreventSystemStandby()
+ {
+ }
+
+ public void AllowSystemStandby()
+ {
+ }
+ }
+}
diff --git a/src/Emby.Server/Program.cs b/src/Emby.Server/Program.cs
new file mode 100644
index 0000000000..26141a0ce9
--- /dev/null
+++ b/src/Emby.Server/Program.cs
@@ -0,0 +1,346 @@
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Server.Implementations;
+using Microsoft.Win32;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Emby.Common.Implementations.EnvironmentInfo;
+using Emby.Common.Implementations.IO;
+using Emby.Common.Implementations.Logging;
+using Emby.Common.Implementations.Networking;
+using Emby.Drawing;
+using Emby.Server.Core;
+using Emby.Server.Implementations.Browser;
+using Emby.Server.Implementations.IO;
+using MediaBrowser.Common.Net;
+using Emby.Server.IO;
+using Emby.Server.Implementations;
+
+namespace Emby.Server
+{
+ public class Program
+ {
+ private static ApplicationHost _appHost;
+
+ private static ILogger _logger;
+
+ private static bool _appHostDisposed;
+
+ [DllImport("kernel32.dll", SetLastError = true)]
+ static extern bool SetDllDirectory(string lpPathName);
+
+ /// <summary>
+ /// Defines the entry point of the application.
+ /// </summary>
+ public static void Main(string[] args)
+ {
+ var options = new StartupOptions(Environment.GetCommandLineArgs());
+
+ var environmentInfo = new EnvironmentInfo();
+
+ var baseDirectory = System.AppContext.BaseDirectory;
+ string archPath = baseDirectory;
+ if (environmentInfo.SystemArchitecture == MediaBrowser.Model.System.Architecture.X64)
+ {
+ archPath = Path.Combine(archPath, "x64");
+ }
+ else if (environmentInfo.SystemArchitecture == MediaBrowser.Model.System.Architecture.X86)
+ {
+ archPath = Path.Combine(archPath, "x86");
+ }
+ else
+ {
+ archPath = Path.Combine(archPath, "arm");
+ }
+
+ //Wand.SetMagickCoderModulePath(architecturePath);
+
+ if (environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows)
+ {
+ SetDllDirectory(archPath);
+ }
+
+ var appPaths = CreateApplicationPaths(baseDirectory);
+ SetSqliteProvider();
+
+ var logManager = new NlogManager(appPaths.LogDirectoryPath, "server");
+ logManager.ReloadLogger(LogSeverity.Debug);
+ logManager.AddConsoleOutput();
+
+ var logger = _logger = logManager.GetLogger("Main");
+
+ ApplicationHost.LogEnvironmentInfo(logger, appPaths, true);
+
+ 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, options, environmentInfo);
+ }
+
+ private static void SetSqliteProvider()
+ {
+ SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_sqlite3());
+ }
+
+ /// <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(30000))
+ {
+ _logger.Info("The duplicate process did not exit.");
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Creates the application paths.
+ /// </summary>
+ private static ServerApplicationPaths CreateApplicationPaths(string appDirectory)
+ {
+ var resourcesPath = appDirectory;
+
+ return new ServerApplicationPaths(ApplicationPathHelper.GetProgramDataPath(appDirectory), appDirectory, 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
+ {
+ 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
+ {
+ return false;
+ }
+ }
+
+ private static readonly TaskCompletionSource<bool> ApplicationTaskCompletionSource = new TaskCompletionSource<bool>();
+
+ /// <summary>
+ /// Runs the application.
+ /// </summary>
+ /// <param name="appPaths">The app paths.</param>
+ /// <param name="logManager">The log manager.</param>
+ /// <param name="options">The options.</param>
+ private static void RunApplication(ServerApplicationPaths appPaths, ILogManager logManager, StartupOptions options, EnvironmentInfo environmentInfo)
+ {
+ var fileSystem = new ManagedFileSystem(logManager.GetLogger("FileSystem"), true, true, true);
+
+ fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
+
+ var imageEncoder = new NullImageEncoder();
+
+ _appHost = new CoreAppHost(appPaths,
+ logManager,
+ options,
+ fileSystem,
+ new PowerManagement(),
+ "emby.windows.zip",
+ environmentInfo,
+ imageEncoder,
+ new CoreSystemEvents(),
+ new MemoryStreamFactory(),
+ new NetworkManager(logManager.GetLogger("NetworkManager")),
+ GenerateCertificate,
+ () => "EmbyUser");
+
+ var initProgress = new Progress<double>();
+
+ if (environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows)
+ {
+ // 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);
+
+ task = task.ContinueWith(new Action<Task>(a => _appHost.RunStartupTasks()), TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.AttachedToParent);
+
+ Task.WaitAll(task);
+
+ task = ApplicationTaskCompletionSource.Task;
+ Task.WaitAll(task);
+ }
+
+ private static void GenerateCertificate(string certPath, string certHost)
+ {
+ //CertificateGenerator.CreateSelfSignCertificatePfx(certPath, certHost, _logger);
+ }
+
+ /// <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(_appHost.ServerConfigurationManager.ApplicationPaths, _logger, _appHost.LogManager).Log(exception);
+
+ ShowMessageBox("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)
+ {
+ return false;
+ }
+
+ private static void ShowMessageBox(string msg)
+ {
+
+ }
+
+ public static void Shutdown()
+ {
+ DisposeAppHost();
+
+ //_logger.Info("Calling Application.Exit");
+ //Application.Exit();
+
+ _logger.Info("Calling Environment.Exit");
+ Environment.Exit(0);
+
+ _logger.Info("Calling ApplicationTaskCompletionSource.SetResult");
+ ApplicationTaskCompletionSource.SetResult(true);
+ }
+
+ public static void Restart()
+ {
+ DisposeAppHost();
+
+ // todo: start new instance
+
+ Shutdown();
+ }
+
+ private static void DisposeAppHost()
+ {
+ if (!_appHostDisposed)
+ {
+ _logger.Info("Disposing app host");
+
+ _appHostDisposed = true;
+ _appHost.Dispose();
+ }
+ }
+
+ /// <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/src/Emby.Server/Properties/AssemblyInfo.cs b/src/Emby.Server/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..2674312bb4
--- /dev/null
+++ b/src/Emby.Server/Properties/AssemblyInfo.cs
@@ -0,0 +1,19 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Emby.Server")]
+[assembly: AssemblyTrademark("")]
+
+// 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("ddaff431-0b3d-4857-8762-990a32dc8472")]
diff --git a/src/Emby.Server/project.json b/src/Emby.Server/project.json
new file mode 100644
index 0000000000..e77506e927
--- /dev/null
+++ b/src/Emby.Server/project.json
@@ -0,0 +1,125 @@
+{
+ "version": "3.1.0-*",
+ "buildOptions": {
+ "emitEntryPoint": true
+ },
+
+ "dependencies": {
+ "Emby.Common.Implementations": "1.0.0-*",
+ "Emby.Server.Core": "1.0.0-*",
+ "Microsoft.NETCore.App": {
+ "type": "platform",
+ "version": "1.1.0"
+ },
+ "Mono.Nat": "1.0.0-*",
+ "System.Runtime.Extensions": "4.3.0",
+ "System.Diagnostics.Process": "4.3.0",
+ "Microsoft.Win32.Registry": "4.3.0",
+ "SQLitePCLRaw.provider.sqlite3.netstandard11": "1.1.1"
+ },
+
+ "runtimes": {
+ "win7-x86": {},
+ "win7-x64": {},
+ "win8-x86": {},
+ "win8-x64": {},
+ "win8-arm": {},
+ "win81-x86": {},
+ "win81-x64": {},
+ "win81-arm": {},
+ "win10-x86": {},
+ "win10-x64": {},
+ "win10-arm": {},
+ "win10-arm64": {},
+ "osx.10.10-x64": {},
+ "osx.10.11-x64": {},
+ "osx.10.12-x64": ,
+ "rhel.7.0-x64": {},
+ "rhel.7.1-x64": {},
+ "rhel.7.2-x64": {},
+ "ubuntu.14.04-x64": {},
+ "ubuntu.14.10-x64": {},
+ "ubuntu.15.04-x64": {},
+ "ubuntu.15.10-x64": {},
+ "ubuntu.16.04-x64": {},
+ "ubuntu.16.10-x64": {},
+ "centos.7-x64": {},
+ "debian.8-x64": {},
+ "fedora.23-x64": {},
+ "fedora.24-x64": {},
+ "opensuse.13.2-x64": {},
+ "opensuse.42.1-x64": {},
+ "ol.7-x64": {},
+ "ol.7.0-x64": {},
+ "ol.7.1-x64": {},
+ "ol.7.2-x64": {}
+ },
+
+ "frameworks": {
+ "netcoreapp1.1": {
+ "imports": "dnxcore50",
+ "dependencies": {
+ "BDInfo": {
+ "target": "project"
+ },
+ "DvdLib": {
+ "target": "project"
+ },
+ "Emby.Dlna": {
+ "target": "project"
+ },
+ "Emby.Drawing": {
+ "target": "project"
+ },
+ "Emby.Photos": {
+ "target": "project"
+ },
+ "Emby.Server.Implementations": {
+ "target": "project"
+ },
+ "MediaBrowser.Api": {
+ "target": "project"
+ },
+ "MediaBrowser.Common": {
+ "target": "project"
+ },
+ "MediaBrowser.Controller": {
+ "target": "project"
+ },
+ "MediaBrowser.LocalMetadata": {
+ "target": "project"
+ },
+ "MediaBrowser.MediaEncoding": {
+ "target": "project"
+ },
+ "MediaBrowser.Model": {
+ "target": "project"
+ },
+ "MediaBrowser.Providers": {
+ "target": "project"
+ },
+ "MediaBrowser.Server.Implementations": {
+ "target": "project"
+ },
+ "MediaBrowser.WebDashboard": {
+ "target": "project"
+ },
+ "MediaBrowser.XbmcMetadata": {
+ "target": "project"
+ },
+ "OpenSubtitlesHandler": {
+ "target": "project"
+ },
+ "RSSDP": {
+ "target": "project"
+ },
+ "ServiceStack": {
+ "target": "project"
+ },
+ "SocketHttpListener.Portable": {
+ "target": "project"
+ }
+ }
+ }
+ }
+}