Преглед изворни кода

Merge pull request #2633 from MediaBrowser/beta

Beta
Luke пре 8 година
родитељ
комит
1a6ee3d48a
100 измењених фајлова са 1413 додато и 1233 уклоњено
  1. 63 16
      Emby.Common.Implementations/IO/ManagedFileSystem.cs
  2. 25 5
      Emby.Common.Implementations/IO/SharpCifsFileSystem.cs
  3. 1 1
      Emby.Common.Implementations/project.json
  4. 1 4
      Emby.Dlna/DlnaManager.cs
  5. 0 6
      Emby.Dlna/Emby.Dlna.csproj
  6. 0 146
      Emby.Dlna/Profiles/BubbleUpnpProfile.cs
  7. 54 8
      Emby.Dlna/Profiles/DefaultProfile.cs
  8. 0 151
      Emby.Dlna/Profiles/KodiProfile.cs
  9. 0 149
      Emby.Dlna/Profiles/VlcProfile.cs
  10. 0 29
      Emby.Dlna/Profiles/Xml/BubbleUPnp.xml
  11. 13 5
      Emby.Dlna/Profiles/Xml/Default.xml
  12. 11 3
      Emby.Dlna/Profiles/Xml/Denon AVR.xml
  13. 2 2
      Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml
  14. 2 2
      Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml
  15. 0 29
      Emby.Dlna/Profiles/Xml/Kodi.xml
  16. 2 2
      Emby.Dlna/Profiles/Xml/LG Smart TV.xml
  17. 2 2
      Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml
  18. 11 3
      Emby.Dlna/Profiles/Xml/MediaMonkey.xml
  19. 2 2
      Emby.Dlna/Profiles/Xml/Panasonic Viera.xml
  20. 2 2
      Emby.Dlna/Profiles/Xml/Popcorn Hour.xml
  21. 2 2
      Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml
  22. 2 2
      Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml
  23. 2 2
      Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml
  24. 2 2
      Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml
  25. 2 2
      Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml
  26. 2 2
      Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml
  27. 2 2
      Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml
  28. 2 2
      Emby.Dlna/Profiles/Xml/Sony Bravia (2010).xml
  29. 2 2
      Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml
  30. 2 2
      Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml
  31. 2 2
      Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml
  32. 2 2
      Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml
  33. 2 2
      Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml
  34. 2 2
      Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml
  35. 0 29
      Emby.Dlna/Profiles/Xml/Vlc.xml
  36. 2 2
      Emby.Dlna/Profiles/Xml/WDTV Live.xml
  37. 2 2
      Emby.Dlna/Profiles/Xml/Xbox 360.xml
  38. 2 2
      Emby.Dlna/Profiles/Xml/Xbox One.xml
  39. 11 3
      Emby.Dlna/Profiles/Xml/foobar2000.xml
  40. 5 11
      Emby.Drawing.ImageMagick/ImageMagickEncoder.cs
  41. 7 10
      Emby.Drawing.Net/GDIImageEncoder.cs
  42. 80 0
      Emby.Drawing.Skia/Emby.Drawing.Skia.csproj
  43. 31 0
      Emby.Drawing.Skia/PercentPlayedDrawer.cs
  44. 120 0
      Emby.Drawing.Skia/PlayedIndicatorDrawer.cs
  45. 25 0
      Emby.Drawing.Skia/Properties/AssemblyInfo.cs
  46. 387 0
      Emby.Drawing.Skia/SkiaEncoder.cs
  47. 190 0
      Emby.Drawing.Skia/StripCollageBuilder.cs
  48. 68 0
      Emby.Drawing.Skia/UnplayedCountIndicator.cs
  49. 4 0
      Emby.Drawing.Skia/packages.config
  50. 52 81
      Emby.Drawing/ImageProcessor.cs
  51. 5 0
      Emby.Drawing/NullImageEncoder.cs
  52. 8 11
      Emby.Server.Core/ApplicationHost.cs
  53. 1 1
      Emby.Server.Core/IO/LibraryMonitor.cs
  54. 1 1
      Emby.Server.Core/project.json
  55. 2 2
      Emby.Server.Implementations/Activity/ActivityRepository.cs
  56. 2 2
      Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
  57. 1 1
      Emby.Server.Implementations/AppBase/ConfigurationHelper.cs
  58. 5 5
      Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
  59. 6 6
      Emby.Server.Implementations/Data/SqliteExtensions.cs
  60. 4 4
      Emby.Server.Implementations/Data/SqliteFileOrganizationRepository.cs
  61. 28 28
      Emby.Server.Implementations/Data/SqliteItemRepository.cs
  62. 4 4
      Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
  63. 3 3
      Emby.Server.Implementations/Data/SqliteUserRepository.cs
  64. 1 1
      Emby.Server.Implementations/Devices/DeviceManager.cs
  65. 2 2
      Emby.Server.Implementations/Devices/DeviceRepository.cs
  66. 1 1
      Emby.Server.Implementations/Emby.Server.Implementations.csproj
  67. 2 2
      Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs
  68. 6 3
      Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
  69. 18 28
      Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
  70. 16 4
      Emby.Server.Implementations/HttpServer/LoggerUtils.cs
  71. 5 2
      Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs
  72. 9 0
      Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs
  73. 9 1
      Emby.Server.Implementations/IO/FileRefresher.cs
  74. 1 1
      Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
  75. 1 0
      Emby.Server.Implementations/Library/LibraryManager.cs
  76. 1 1
      Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs
  77. 1 1
      Emby.Server.Implementations/Library/Validators/GameGenresValidator.cs
  78. 1 1
      Emby.Server.Implementations/Library/Validators/GenresValidator.cs
  79. 1 1
      Emby.Server.Implementations/Library/Validators/MusicGenresValidator.cs
  80. 1 1
      Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
  81. 3 1
      Emby.Server.Implementations/Library/Validators/YearsPostScanTask.cs
  82. 9 9
      Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs
  83. 3 3
      Emby.Server.Implementations/Security/AuthenticationRepository.cs
  84. 3 3
      Emby.Server.Implementations/Social/SharingRepository.cs
  85. 12 2
      Emby.Server.Implementations/Updates/InstallationManager.cs
  86. 1 1
      Emby.Server.Implementations/packages.config
  87. 0 2
      Emby.Server.sln
  88. 0 4
      MediaBrowser.Api/MediaBrowser.Api.csproj
  89. 4 1
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  90. 4 8
      MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
  91. 2 0
      MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
  92. 2 1
      MediaBrowser.Api/Reports/Common/HeaderMetadata.cs
  93. 5 0
      MediaBrowser.Api/Reports/Common/ReportBuilderBase.cs
  94. 10 3
      MediaBrowser.Api/Reports/Data/ReportBuilder.cs
  95. 0 256
      MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs
  96. 0 33
      MediaBrowser.Api/Reports/Stat/ReportStatGroup.cs
  97. 0 23
      MediaBrowser.Api/Reports/Stat/ReportStatItem.cs
  98. 0 24
      MediaBrowser.Api/Reports/Stat/ReportStatResult.cs
  99. 2 6
      MediaBrowser.Controller/Drawing/IImageEncoder.cs
  100. 2 0
      MediaBrowser.Controller/Drawing/IImageProcessor.cs

+ 63 - 16
Emby.Common.Implementations/IO/ManagedFileSystem.cs

@@ -392,10 +392,27 @@ namespace Emby.Common.Implementations.IO
 
             if (_supportsAsyncFileStreams && isAsync)
             {
-                return new FileStream(path, GetFileMode(mode), GetFileAccess(access), GetFileShare(share), 262144, true);
+                return GetFileStream(path, mode, access, share, FileOpenOptions.Asynchronous);
             }
 
-            return new FileStream(path, GetFileMode(mode), GetFileAccess(access), GetFileShare(share), 262144);
+            return GetFileStream(path, mode, access, share, FileOpenOptions.None);
+        }
+
+        public Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share, FileOpenOptions fileOpenOptions)
+        {
+            if (_sharpCifsFileSystem.IsEnabledForPath(path))
+            {
+                return _sharpCifsFileSystem.GetFileStream(path, mode, access, share);
+            }
+
+            var defaultBufferSize = 4096;
+            return new FileStream(path, GetFileMode(mode), GetFileAccess(access), GetFileShare(share), defaultBufferSize, GetFileOptions(fileOpenOptions));
+        }
+
+        private FileOptions GetFileOptions(FileOpenOptions mode)
+        {
+            var val = (int)mode;
+            return (FileOptions)val;
         }
 
         private FileMode GetFileMode(FileOpenMode mode)
@@ -501,6 +518,49 @@ namespace Emby.Common.Implementations.IO
             }
         }
 
+        public void SetAttributes(string path, bool isHidden, bool isReadOnly)
+        {
+            if (_sharpCifsFileSystem.IsEnabledForPath(path))
+            {
+                _sharpCifsFileSystem.SetAttributes(path, isHidden, isReadOnly);
+                return;
+            }
+
+            var info = GetFileInfo(path);
+
+            if (!info.Exists)
+            {
+                return;
+            }
+
+            if (info.IsReadOnly == isReadOnly && info.IsHidden == isHidden)
+            {
+                return;
+            }
+
+            var attributes = File.GetAttributes(path);
+
+            if (isReadOnly)
+            {
+                attributes = attributes | FileAttributes.ReadOnly;
+            }
+            else
+            {
+                attributes = RemoveAttribute(attributes, FileAttributes.ReadOnly);
+            }
+
+            if (isHidden)
+            {
+                attributes = attributes | FileAttributes.Hidden;
+            }
+            else
+            {
+                attributes = RemoveAttribute(attributes, FileAttributes.Hidden);
+            }
+
+            File.SetAttributes(path, attributes);
+        }
+
         private static FileAttributes RemoveAttribute(FileAttributes attributes, FileAttributes attributesToRemove)
         {
             return attributes & ~attributesToRemove;
@@ -673,20 +733,7 @@ namespace Emby.Common.Implementations.IO
                 return;
             }
 
-            var fileInfo = GetFileInfo(path);
-
-            if (fileInfo.Exists)
-            {
-                if (fileInfo.IsHidden)
-                {
-                    SetHidden(path, false);
-                }
-                if (fileInfo.IsReadOnly)
-                {
-                    SetReadOnly(path, false);
-                }
-            }
-
+            SetAttributes(path, false, false);
             File.Delete(path);
         }
 

+ 25 - 5
Emby.Common.Implementations/IO/SharpCifsFileSystem.cs

@@ -53,6 +53,11 @@ namespace Emby.Common.Implementations.IO
             if (separator == '/')
             {
                 result = result.Replace('\\', '/');
+
+                if (result.StartsWith("smb:/", StringComparison.OrdinalIgnoreCase) && !result.StartsWith("smb://", StringComparison.OrdinalIgnoreCase))
+                {
+                    result = result.Replace("smb:/", "smb://");
+                }
             }
 
             return result;
@@ -161,23 +166,38 @@ namespace Emby.Common.Implementations.IO
         public void SetHidden(string path, bool isHidden)
         {
             var file = CreateSmbFile(path);
+            SetHidden(file, isHidden);
+        }
+
+        public void SetReadOnly(string path, bool isReadOnly)
+        {
+            var file = CreateSmbFile(path);
+            SetReadOnly(file, isReadOnly);
+        }
 
+        public void SetAttributes(string path, bool isHidden, bool isReadOnly)
+        {
+            var file = CreateSmbFile(path);
+            SetHidden(file, isHidden);
+            SetReadOnly(file, isReadOnly);
+        }
+
+        private void SetHidden(SmbFile file, bool isHidden)
+        {
             var isCurrentlyHidden = file.IsHidden();
 
             if (isCurrentlyHidden && !isHidden)
             {
-                file.SetAttributes(file.GetAttributes() & ~SmbFile.AttrReadonly);
+                file.SetAttributes(file.GetAttributes() & ~SmbFile.AttrHidden);
             }
             else if (!isCurrentlyHidden && isHidden)
             {
-                file.SetAttributes(file.GetAttributes() | SmbFile.AttrReadonly);
+                file.SetAttributes(file.GetAttributes() | SmbFile.AttrHidden);
             }
         }
 
-        public void SetReadOnly(string path, bool isReadOnly)
+        private void SetReadOnly(SmbFile file, bool isReadOnly)
         {
-            var file = CreateSmbFile(path);
-
             var isCurrentlyReadOnly = !file.CanWrite();
 
             if (isCurrentlyReadOnly && !isReadOnly)

+ 1 - 1
Emby.Common.Implementations/project.json

@@ -45,7 +45,7 @@
         "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.Http": "4.3.2",
         "System.Net.Primitives": "4.3.0",
         "System.Net.Sockets": "4.3.0",
         "System.Net.NetworkInformation": "4.3.0",

+ 1 - 4
Emby.Dlna/DlnaManager.cs

@@ -587,10 +587,7 @@ namespace Emby.Dlna
                 new DirectTvProfile(),
                 new DishHopperJoeyProfile(),
                 new DefaultProfile(),
-                new PopcornHourProfile(),
-                new VlcProfile(),
-                new BubbleUpnpProfile(),
-                new KodiProfile(),
+                new PopcornHourProfile()
             };
 
             foreach (var item in list)

+ 0 - 6
Emby.Dlna/Emby.Dlna.csproj

@@ -79,13 +79,11 @@
     <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" />
@@ -105,7 +103,6 @@
     <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" />
@@ -153,13 +150,11 @@
     <EmbeddedResource Include="Profiles\Xml\Sharp Smart TV.xml" />
   </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" />
@@ -178,7 +173,6 @@
     <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" />
     <EmbeddedResource Include="Profiles\Xml\Xbox One.xml" />

+ 0 - 146
Emby.Dlna/Profiles/BubbleUpnpProfile.cs

@@ -1,146 +0,0 @@
-using MediaBrowser.Model.Dlna;
-using System.Xml.Serialization;
-
-namespace Emby.Dlna.Profiles
-{
-    [XmlRoot("Profile")]
-    public class BubbleUpnpProfile : DefaultProfile
-    {
-        public BubbleUpnpProfile()
-        {
-            Name = "BubbleUPnp";
-
-            Identification = new DeviceIdentification
-            {
-                ModelName = "BubbleUPnp",
-
-                Headers = new[]
-                {
-                    new HttpHeaderInfo {Name = "User-Agent", Value = "BubbleUPnp", Match = HeaderMatchType.Substring}
-                }
-            };
-
-            TranscodingProfiles = new[]
-            {
-                new TranscodingProfile
-                {
-                    Container = "mp3",
-                    AudioCodec = "mp3",
-                    Type = DlnaProfileType.Audio
-                },
-
-                new TranscodingProfile
-                {
-                    Container = "ts",
-                    Type = DlnaProfileType.Video,
-                    AudioCodec = "aac",
-                    VideoCodec = "h264"
-                },
-
-                new TranscodingProfile
-                {
-                    Container = "jpeg",
-                    Type = DlnaProfileType.Photo
-                }
-            };
-
-            DirectPlayProfiles = new[]
-            {
-                new DirectPlayProfile
-                {
-                    Container = "",
-                    Type = DlnaProfileType.Video
-                },
-
-                new DirectPlayProfile
-                {
-                    Container = "",
-                    Type = DlnaProfileType.Audio
-                },
-
-                new DirectPlayProfile
-                {
-                    Container = "",
-                    Type = DlnaProfileType.Photo,
-                }
-            };
-
-            ResponseProfiles = new ResponseProfile[] { };
-
-            ContainerProfiles = new ContainerProfile[] { };
-
-            CodecProfiles = new CodecProfile[] { };
-
-            SubtitleProfiles = new[]
-            {
-                new SubtitleProfile
-                {
-                    Format = "srt",
-                    Method = SubtitleDeliveryMethod.External,
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "sub",
-                    Method = SubtitleDeliveryMethod.External,
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "srt",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "ass",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "ssa",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "smi",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "dvdsub",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "pgs",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "pgssub",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "sub",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                }
-            };
-        }
-    }
-}

+ 54 - 8
Emby.Dlna/Profiles/DefaultProfile.cs

@@ -30,8 +30,8 @@ namespace Emby.Dlna.Profiles
             MaxIconWidth = 48;
             MaxIconHeight = 48;
 
-            MaxStreamingBitrate = 30000000;
-            MaxStaticBitrate = 30000000;
+            MaxStreamingBitrate = 40000000;
+            MaxStaticBitrate = 40000000;
             MusicStreamingTranscodingBitrate = 192000;
 
             EnableAlbumArtInDidl = false;
@@ -64,15 +64,13 @@ namespace Emby.Dlna.Profiles
             {
                 new DirectPlayProfile
                 {
-                    Container = "m4v,ts,mpegts,mkv,avi,mpg,mpeg,mp4,mov",
-                    VideoCodec = "h264",
-                    AudioCodec = "aac,mp3,ac3",
+                    Container = "m4v,mpegts,ts,3gp,mov,xvid,vob,mkv,wmv,asf,ogm,ogv,m2v,avi,mpg,mpeg,mp4,webm,wtv,m2ts,dvr-ms",
                     Type = DlnaProfileType.Video
                 },
 
                 new DirectPlayProfile
                 {
-                    Container = "mp3,wma,aac,wav,flac",
+                    Container = "aac,mp3,mpa,wav,wma,mp2,ogg,oga,webma,ape,opus,flac",
                     Type = DlnaProfileType.Audio
                 }
             };
@@ -82,13 +80,61 @@ namespace Emby.Dlna.Profiles
                 new SubtitleProfile
                 {
                     Format = "srt",
-                    Method = SubtitleDeliveryMethod.Embed
+                    Method = SubtitleDeliveryMethod.External,
                 },
 
                 new SubtitleProfile
                 {
-                    Format = "srt",
+                    Format = "sub",
                     Method = SubtitleDeliveryMethod.External,
+                },
+
+                new SubtitleProfile
+                {
+                    Format = "srt",
+                    Method = SubtitleDeliveryMethod.Embed
+                },
+
+                new SubtitleProfile
+                {
+                    Format = "ass",
+                    Method = SubtitleDeliveryMethod.Embed
+                },
+
+                new SubtitleProfile
+                {
+                    Format = "ssa",
+                    Method = SubtitleDeliveryMethod.Embed
+                },
+
+                new SubtitleProfile
+                {
+                    Format = "smi",
+                    Method = SubtitleDeliveryMethod.Embed
+                },
+
+                new SubtitleProfile
+                {
+                    Format = "dvdsub",
+                    Method = SubtitleDeliveryMethod.Embed
+                },
+
+                new SubtitleProfile
+                {
+                    Format = "pgs",
+                    Method = SubtitleDeliveryMethod.Embed
+                },
+
+                new SubtitleProfile
+                {
+                    Format = "pgssub",
+                    Method = SubtitleDeliveryMethod.Embed
+                },
+
+                new SubtitleProfile
+                {
+                    Format = "sub",
+                    Method = SubtitleDeliveryMethod.Embed
                 }
             };
 

+ 0 - 151
Emby.Dlna/Profiles/KodiProfile.cs

@@ -1,151 +0,0 @@
-using MediaBrowser.Model.Dlna;
-using System.Xml.Serialization;
-
-namespace Emby.Dlna.Profiles
-{
-    [XmlRoot("Profile")]
-    public class KodiProfile : DefaultProfile
-    {
-        public KodiProfile()
-        {
-            Name = "Kodi";
-
-            MaxStreamingBitrate = 100000000;
-            MusicStreamingTranscodingBitrate = 1280000;
-
-            TimelineOffsetSeconds = 5;
-
-            Identification = new DeviceIdentification
-            {
-                ModelName = "Kodi",
-
-                Headers = new[]
-                {
-                    new HttpHeaderInfo {Name = "User-Agent", Value = "Kodi", Match = HeaderMatchType.Substring}
-                }
-            };
-
-            TranscodingProfiles = new[]
-            {
-                new TranscodingProfile
-                {
-                    Container = "mp3",
-                    AudioCodec = "mp3",
-                    Type = DlnaProfileType.Audio
-                },
-
-                new TranscodingProfile
-                {
-                    Container = "ts",
-                    Type = DlnaProfileType.Video,
-                    AudioCodec = "aac",
-                    VideoCodec = "h264"
-                },
-
-                new TranscodingProfile
-                {
-                    Container = "jpeg",
-                    Type = DlnaProfileType.Photo
-                }
-            };
-
-            DirectPlayProfiles = new[]
-            {
-                new DirectPlayProfile
-                {
-                    Container = "",
-                    Type = DlnaProfileType.Video
-                },
-
-                new DirectPlayProfile
-                {
-                    Container = "",
-                    Type = DlnaProfileType.Audio
-                },
-
-                new DirectPlayProfile
-                {
-                    Container = "",
-                    Type = DlnaProfileType.Photo,
-                }
-            };
-
-            ResponseProfiles = new ResponseProfile[] { };
-
-            ContainerProfiles = new ContainerProfile[] { };
-
-            CodecProfiles = new CodecProfile[] { };
-
-            SubtitleProfiles = new[]
-            {
-                new SubtitleProfile
-                {
-                    Format = "srt",
-                    Method = SubtitleDeliveryMethod.External,
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "sub",
-                    Method = SubtitleDeliveryMethod.External,
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "srt",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "ass",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "ssa",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "smi",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "dvdsub",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "pgs",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "pgssub",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "sub",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                }
-            };
-        }
-    }
-}

+ 0 - 149
Emby.Dlna/Profiles/VlcProfile.cs

@@ -1,149 +0,0 @@
-using MediaBrowser.Model.Dlna;
-using System.Xml.Serialization;
-
-namespace Emby.Dlna.Profiles
-{
-    [XmlRoot("Profile")]
-    public class VlcProfile : DefaultProfile
-    {
-        public VlcProfile()
-        {
-            Name = "Vlc";
-
-
-            TimelineOffsetSeconds = 5;
-
-            Identification = new DeviceIdentification
-            {
-                ModelName = "Vlc",
-
-                Headers = new[]
-                {
-                    new HttpHeaderInfo {Name = "User-Agent", Value = "vlc", Match = HeaderMatchType.Substring}
-                }
-            };
-
-            TranscodingProfiles = new[]
-            {
-                new TranscodingProfile
-                {
-                    Container = "mp3",
-                    AudioCodec = "mp3",
-                    Type = DlnaProfileType.Audio
-                },
-
-                new TranscodingProfile
-                {
-                    Container = "ts",
-                    Type = DlnaProfileType.Video,
-                    AudioCodec = "aac",
-                    VideoCodec = "h264"
-                },
-
-                new TranscodingProfile
-                {
-                    Container = "jpeg",
-                    Type = DlnaProfileType.Photo
-                }
-            };
-
-            DirectPlayProfiles = new[]
-            {
-                new DirectPlayProfile
-                {
-                    Container = "",
-                    Type = DlnaProfileType.Video
-                },
-
-                new DirectPlayProfile
-                {
-                    Container = "",
-                    Type = DlnaProfileType.Audio
-                },
-
-                new DirectPlayProfile
-                {
-                    Container = "",
-                    Type = DlnaProfileType.Photo,
-                }
-            };
-
-            ResponseProfiles = new ResponseProfile[] { };
-
-            ContainerProfiles = new ContainerProfile[] { };
-
-            CodecProfiles = new CodecProfile[] { };
-
-            SubtitleProfiles = new[]
-            {
-                new SubtitleProfile
-                {
-                    Format = "srt",
-                    Method = SubtitleDeliveryMethod.External,
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "sub",
-                    Method = SubtitleDeliveryMethod.External,
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "srt",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "ass",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "ssa",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "smi",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "dvdsub",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "pgs",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "pgssub",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "sub",
-                    Method = SubtitleDeliveryMethod.Embed,
-                    DidlMode = "",
-                }
-            };
-        }
-    }
-}

Разлика између датотеке није приказан због своје велике величине
+ 0 - 29
Emby.Dlna/Profiles/Xml/BubbleUPnp.xml


+ 13 - 5
Emby.Dlna/Profiles/Xml/Default.xml

@@ -16,8 +16,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -29,8 +29,8 @@
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
   <XmlRootAttributes />
   <DirectPlayProfiles>
-    <DirectPlayProfile container="m4v,ts,mpegts,mkv,avi,mpg,mpeg,mp4,mov" audioCodec="aac,mp3,ac3" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="mp3,wma,aac,wav,flac" type="Audio" />
+    <DirectPlayProfile container="m4v,mpegts,ts,3gp,mov,xvid,vob,mkv,wmv,asf,ogm,ogv,m2v,avi,mpg,mpeg,mp4,webm,wtv,m2ts,dvr-ms" type="Video" />
+    <DirectPlayProfile container="aac,mp3,mpa,wav,wma,mp2,ogg,oga,webma,ape,opus,flac" type="Audio" />
   </DirectPlayProfiles>
   <TranscodingProfiles>
     <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
@@ -45,7 +45,15 @@
     </ResponseProfile>
   </ResponseProfiles>
   <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="Embed" />
     <SubtitleProfile format="srt" method="External" />
+    <SubtitleProfile format="sub" method="External" />
+    <SubtitleProfile format="srt" method="Embed" />
+    <SubtitleProfile format="ass" method="Embed" />
+    <SubtitleProfile format="ssa" method="Embed" />
+    <SubtitleProfile format="smi" method="Embed" />
+    <SubtitleProfile format="dvdsub" method="Embed" />
+    <SubtitleProfile format="pgs" method="Embed" />
+    <SubtitleProfile format="pgssub" method="Embed" />
+    <SubtitleProfile format="sub" method="Embed" />
   </SubtitleProfiles>
 </Profile>

+ 11 - 3
Emby.Dlna/Profiles/Xml/Denon AVR.xml

@@ -21,8 +21,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -45,7 +45,15 @@
   <CodecProfiles />
   <ResponseProfiles />
   <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="Embed" />
     <SubtitleProfile format="srt" method="External" />
+    <SubtitleProfile format="sub" method="External" />
+    <SubtitleProfile format="srt" method="Embed" />
+    <SubtitleProfile format="ass" method="Embed" />
+    <SubtitleProfile format="ssa" method="Embed" />
+    <SubtitleProfile format="smi" method="Embed" />
+    <SubtitleProfile format="dvdsub" method="Embed" />
+    <SubtitleProfile format="pgs" method="Embed" />
+    <SubtitleProfile format="pgssub" method="Embed" />
+    <SubtitleProfile format="sub" method="Embed" />
   </SubtitleProfiles>
 </Profile>

+ 2 - 2
Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml

@@ -22,8 +22,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>

+ 2 - 2
Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml

@@ -23,8 +23,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>

Разлика између датотеке није приказан због своје велике величине
+ 0 - 29
Emby.Dlna/Profiles/Xml/Kodi.xml


+ 2 - 2
Emby.Dlna/Profiles/Xml/LG Smart TV.xml

@@ -22,8 +22,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>

+ 2 - 2
Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml

@@ -20,8 +20,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>

+ 11 - 3
Emby.Dlna/Profiles/Xml/MediaMonkey.xml

@@ -22,8 +22,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -51,7 +51,15 @@
   <CodecProfiles />
   <ResponseProfiles />
   <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="Embed" />
     <SubtitleProfile format="srt" method="External" />
+    <SubtitleProfile format="sub" method="External" />
+    <SubtitleProfile format="srt" method="Embed" />
+    <SubtitleProfile format="ass" method="Embed" />
+    <SubtitleProfile format="ssa" method="Embed" />
+    <SubtitleProfile format="smi" method="Embed" />
+    <SubtitleProfile format="dvdsub" method="Embed" />
+    <SubtitleProfile format="pgs" method="Embed" />
+    <SubtitleProfile format="pgssub" method="Embed" />
+    <SubtitleProfile format="sub" method="Embed" />
   </SubtitleProfiles>
 </Profile>

+ 2 - 2
Emby.Dlna/Profiles/Xml/Panasonic Viera.xml

@@ -23,8 +23,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>

+ 2 - 2
Emby.Dlna/Profiles/Xml/Popcorn Hour.xml

@@ -16,8 +16,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>

+ 2 - 2
Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml

@@ -22,8 +22,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>

+ 2 - 2
Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml

@@ -22,8 +22,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>

+ 2 - 2
Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml

@@ -26,8 +26,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>

+ 2 - 2
Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml

@@ -26,8 +26,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>

+ 2 - 2
Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml

@@ -24,8 +24,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>

+ 2 - 2
Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml

@@ -24,8 +24,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>

+ 2 - 2
Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml

@@ -24,8 +24,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>

+ 2 - 2
Emby.Dlna/Profiles/Xml/Sony Bravia (2010).xml

@@ -23,8 +23,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>

+ 2 - 2
Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml

@@ -23,8 +23,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>

+ 2 - 2
Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml

@@ -23,8 +23,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>

+ 2 - 2
Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml

@@ -23,8 +23,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>

+ 2 - 2
Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml

@@ -23,8 +23,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>

+ 2 - 2
Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml

@@ -23,8 +23,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>

+ 2 - 2
Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml

@@ -23,8 +23,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>

Разлика између датотеке није приказан због своје велике величине
+ 0 - 29
Emby.Dlna/Profiles/Xml/Vlc.xml


+ 2 - 2
Emby.Dlna/Profiles/Xml/WDTV Live.xml

@@ -23,8 +23,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>

+ 2 - 2
Emby.Dlna/Profiles/Xml/Xbox 360.xml

@@ -24,8 +24,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>

+ 2 - 2
Emby.Dlna/Profiles/Xml/Xbox One.xml

@@ -23,8 +23,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>

+ 11 - 3
Emby.Dlna/Profiles/Xml/foobar2000.xml

@@ -22,8 +22,8 @@
   <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
   <MaxIconWidth>48</MaxIconWidth>
   <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>30000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>30000000</MaxStaticBitrate>
+  <MaxStreamingBitrate>40000000</MaxStreamingBitrate>
+  <MaxStaticBitrate>40000000</MaxStaticBitrate>
   <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
   <MaxStaticMusicBitrate xsi:nil="true" />
   <XDlnaDoc>DMS-1.50</XDlnaDoc>
@@ -51,7 +51,15 @@
   <CodecProfiles />
   <ResponseProfiles />
   <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="Embed" />
     <SubtitleProfile format="srt" method="External" />
+    <SubtitleProfile format="sub" method="External" />
+    <SubtitleProfile format="srt" method="Embed" />
+    <SubtitleProfile format="ass" method="Embed" />
+    <SubtitleProfile format="ssa" method="Embed" />
+    <SubtitleProfile format="smi" method="Embed" />
+    <SubtitleProfile format="dvdsub" method="Embed" />
+    <SubtitleProfile format="pgs" method="Embed" />
+    <SubtitleProfile format="pgssub" method="Embed" />
+    <SubtitleProfile format="sub" method="Embed" />
   </SubtitleProfiles>
 </Profile>

+ 5 - 11
Emby.Drawing.ImageMagick/ImageMagickEncoder.cs

@@ -105,17 +105,6 @@ namespace Emby.Drawing.ImageMagick
             }
         }
 
-        public void CropWhiteSpace(string inputPath, string outputPath)
-        {
-            CheckDisposed();
-
-            using (var wand = new MagickWand(inputPath))
-            {
-                wand.CurrentImage.TrimImage(10);
-                wand.SaveImage(outputPath);
-            }
-        }
-
         public ImageSize GetImageSize(string path)
         {
             CheckDisposed();
@@ -150,6 +139,11 @@ namespace Emby.Drawing.ImageMagick
             {
                 using (var originalImage = new MagickWand(inputPath))
                 {
+                    if (options.CropWhiteSpace)
+                    {
+                        originalImage.CurrentImage.TrimImage(10);
+                    }
+
                     ScaleImage(originalImage, width, height, options.Blur ?? 0);
 
                     if (autoOrient)

+ 7 - 10
Emby.Drawing.Net/GDIImageEncoder.cs

@@ -75,27 +75,24 @@ namespace Emby.Drawing.Net
             }
         }
 
-        public void CropWhiteSpace(string inputPath, string outputPath)
+        private Image GetImage(string path, bool cropWhitespace)
         {
-            using (var image = (Bitmap)Image.FromFile(inputPath))
+            if (cropWhitespace)
             {
-                using (var croppedImage = image.CropWhitespace())
+                using (var originalImage = (Bitmap)Image.FromFile(path))
                 {
-                    _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(outputPath));
-
-                    using (var outputStream = _fileSystem.GetFileStream(outputPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, false))
-                    {
-                        croppedImage.Save(System.Drawing.Imaging.ImageFormat.Png, outputStream, 100);
-                    }
+                    return originalImage.CropWhitespace();
                 }
             }
+
+            return Image.FromFile(path);
         }
 
         public void EncodeImage(string inputPath, string cacheFilePath, bool autoOrient, int width, int height, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat)
         {
             var hasPostProcessing = !string.IsNullOrEmpty(options.BackgroundColor) || options.UnplayedCount.HasValue || options.AddPlayedIndicator || options.PercentPlayed > 0;
 
-            using (var originalImage = Image.FromFile(inputPath))
+            using (var originalImage = GetImage(inputPath, options.CropWhiteSpace))
             {
                 var newWidth = Convert.ToInt32(width);
                 var newHeight = Convert.ToInt32(height);

+ 80 - 0
Emby.Drawing.Skia/Emby.Drawing.Skia.csproj

@@ -0,0 +1,80 @@
+<?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>{2312DA6D-FF86-4597-9777-BCEEC32D96DD}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Emby.Drawing.Skia</RootNamespace>
+    <AssemblyName>Emby.Drawing.Skia</AssemblyName>
+    <DefaultLanguage>en-US</DefaultLanguage>
+    <FileAlignment>512</FileAlignment>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <!-- A reference to the entire .NET Framework is automatically included -->
+    <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
+      <Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
+      <Name>MediaBrowser.Common</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj">
+      <Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project>
+      <Name>MediaBrowser.Controller</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
+      <Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
+      <Name>MediaBrowser.Model</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="..\SharedVersion.cs">
+      <Link>Properties\SharedVersion.cs</Link>
+    </Compile>
+    <Compile Include="PercentPlayedDrawer.cs" />
+    <Compile Include="PlayedIndicatorDrawer.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="SkiaEncoder.cs" />
+    <Compile Include="StripCollageBuilder.cs" />
+    <Compile Include="UnplayedCountIndicator.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Reference Include="SkiaSharp, Version=1.57.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
+      <HintPath>..\packages\SkiaSharp.1.57.1\lib\portable-net45+win8+wpa81+wp8\SkiaSharp.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="fonts\robotoregular.ttf" />
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 31 - 0
Emby.Drawing.Skia/PercentPlayedDrawer.cs

@@ -0,0 +1,31 @@
+using SkiaSharp;
+using MediaBrowser.Model.Drawing;
+using System;
+
+namespace Emby.Drawing.Skia
+{
+    public class PercentPlayedDrawer
+    {
+        private const int IndicatorHeight = 8;
+
+        public void Process(SKCanvas canvas, ImageSize imageSize, double percent)
+        {
+            using (var paint = new SKPaint())
+            {
+                var endX = imageSize.Width - 1;
+                var endY = imageSize.Height - 1;
+
+                paint.Color = SKColor.Parse("#99000000");
+                paint.Style = SKPaintStyle.Fill;
+                canvas.DrawRect(SKRect.Create(0, (float)endY - IndicatorHeight, (float)endX, (float)endY), paint);
+
+                double foregroundWidth = endX;
+                foregroundWidth *= percent;
+                foregroundWidth /= 100;
+
+                paint.Color = SKColor.Parse("#FF52B54B");
+                canvas.DrawRect(SKRect.Create(0, (float)endY - IndicatorHeight, Convert.ToInt32(Math.Round(foregroundWidth)), (float)endY), paint);
+            }
+        }
+    }
+}

+ 120 - 0
Emby.Drawing.Skia/PlayedIndicatorDrawer.cs

@@ -0,0 +1,120 @@
+using SkiaSharp;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Drawing;
+using System;
+using System.IO;
+using System.Threading.Tasks;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+using System.Reflection;
+
+namespace Emby.Drawing.Skia
+{
+    public class PlayedIndicatorDrawer
+    {
+        private const int FontSize = 42;
+        private const int OffsetFromTopRightCorner = 38;
+
+        private readonly IApplicationPaths _appPaths;
+        private readonly IHttpClient _iHttpClient;
+        private readonly IFileSystem _fileSystem;
+
+        public PlayedIndicatorDrawer(IApplicationPaths appPaths, IHttpClient iHttpClient, IFileSystem fileSystem)
+        {
+            _appPaths = appPaths;
+            _iHttpClient = iHttpClient;
+            _fileSystem = fileSystem;
+        }
+
+        public async Task DrawPlayedIndicator(SKCanvas canvas, ImageSize imageSize)
+        {
+            var x = imageSize.Width - OffsetFromTopRightCorner;
+
+            using (var paint = new SKPaint())
+            {
+                paint.Color = SKColor.Parse("#CC52B54B");
+                paint.Style = SKPaintStyle.Fill;
+                canvas.DrawCircle((float)x, OffsetFromTopRightCorner, 20, paint);
+            }
+
+            using (var paint = new SKPaint())
+            {
+                paint.Color = new SKColor(255, 255, 255, 255);
+                paint.Style = SKPaintStyle.Fill;
+                paint.Typeface = SKTypeface.FromFile(await DownloadFont("webdings.ttf", "https://github.com/MediaBrowser/Emby.Resources/raw/master/fonts/webdings.ttf",
+                    _appPaths, _iHttpClient, _fileSystem).ConfigureAwait(false));
+                paint.TextSize = FontSize;
+                paint.IsAntialias = true;
+
+                canvas.DrawText("a", (float)x-20, OffsetFromTopRightCorner + 12, paint);
+            }
+        }
+
+        internal static string ExtractFont(string name, IApplicationPaths paths, IFileSystem fileSystem)
+        {
+            var filePath = Path.Combine(paths.ProgramDataPath, "fonts", name);
+
+            if (fileSystem.FileExists(filePath))
+            {
+                return filePath;
+            }
+
+            var namespacePath = typeof(PlayedIndicatorDrawer).Namespace + ".fonts." + name;
+            var tempPath = Path.Combine(paths.TempDirectory, Guid.NewGuid().ToString("N") + ".ttf");
+            fileSystem.CreateDirectory(fileSystem.GetDirectoryName(tempPath));
+
+            using (var stream = typeof(PlayedIndicatorDrawer).GetTypeInfo().Assembly.GetManifestResourceStream(namespacePath))
+            {
+                using (var fileStream = fileSystem.GetFileStream(tempPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
+                {
+                    stream.CopyTo(fileStream);
+                }
+            }
+
+            fileSystem.CreateDirectory(fileSystem.GetDirectoryName(filePath));
+
+            try
+            {
+                fileSystem.CopyFile(tempPath, filePath, false);
+            }
+            catch (IOException)
+            {
+
+            }
+
+            return tempPath;
+        }
+
+        internal static async Task<string> DownloadFont(string name, string url, IApplicationPaths paths, IHttpClient httpClient, IFileSystem fileSystem)
+        {
+            var filePath = Path.Combine(paths.ProgramDataPath, "fonts", name);
+
+            if (fileSystem.FileExists(filePath))
+            {
+                return filePath;
+            }
+
+            var tempPath = await httpClient.GetTempFile(new HttpRequestOptions
+            {
+                Url = url,
+                Progress = new Progress<double>()
+
+            }).ConfigureAwait(false);
+
+            fileSystem.CreateDirectory(fileSystem.GetDirectoryName(filePath));
+
+            try
+            {
+                fileSystem.CopyFile(tempPath, filePath, false);
+            }
+            catch (IOException)
+            {
+
+            }
+
+            return tempPath;
+        }
+    }
+}

+ 25 - 0
Emby.Drawing.Skia/Properties/AssemblyInfo.cs

@@ -0,0 +1,25 @@
+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.Drawing.Skia")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Emby.Drawing.Skia")]
+[assembly: AssemblyCopyright("Copyright ©  2017")]
+[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
+//

+ 387 - 0
Emby.Drawing.Skia/SkiaEncoder.cs

@@ -0,0 +1,387 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Model.Drawing;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using SkiaSharp;
+using System;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
+
+namespace Emby.Drawing.Skia
+{
+    public class SkiaEncoder : IImageEncoder
+    {
+        private readonly ILogger _logger;
+        private readonly IApplicationPaths _appPaths;
+        private readonly Func<IHttpClient> _httpClientFactory;
+        private readonly IFileSystem _fileSystem;
+
+        public SkiaEncoder(ILogger logger, IApplicationPaths appPaths, Func<IHttpClient> httpClientFactory, IFileSystem fileSystem)
+        {
+            _logger = logger;
+            _appPaths = appPaths;
+            _httpClientFactory = httpClientFactory;
+            _fileSystem = fileSystem;
+
+            LogVersion();
+        }
+
+        public string[] SupportedInputFormats
+        {
+            get
+            {
+                // Some common file name extensions for RAW picture files include: .cr2, .crw, .dng, .nef, .orf, .rw2, .pef, .arw, .sr2, .srf, and .tif.
+                return new[]
+                {
+                    "jpeg",
+                    "jpg",
+                    "png",
+                    "dng",
+                    "webp",
+                    "gif",
+                    "bmp",
+                    "ico",
+                    "astc",
+                    "ktx",
+                    "pkm",
+                    "wbmp"
+                };
+            }
+        }
+
+        public ImageFormat[] SupportedOutputFormats
+        {
+            get
+            {
+                return new[] { ImageFormat.Webp, ImageFormat.Gif, ImageFormat.Jpg, ImageFormat.Png, ImageFormat.Bmp };
+            }
+        }
+
+        private void LogVersion()
+        {
+            _logger.Info("SkiaSharp version: " + GetVersion());
+        }
+
+        public static string GetVersion()
+        {
+            using (var bitmap = new SKBitmap())
+            {
+                return typeof(SKBitmap).GetTypeInfo().Assembly.GetName().Version.ToString();
+            }
+        }
+
+        private static bool IsWhiteSpace(SKColor color)
+        {
+            return (color.Red == 255 && color.Green == 255 && color.Blue == 255) || color.Alpha == 0;
+        }
+
+        public static SKEncodedImageFormat GetImageFormat(ImageFormat selectedFormat)
+        {
+            switch (selectedFormat)
+            {
+                case ImageFormat.Bmp:
+                    return SKEncodedImageFormat.Bmp;
+                case ImageFormat.Jpg:
+                    return SKEncodedImageFormat.Jpeg;
+                case ImageFormat.Gif:
+                    return SKEncodedImageFormat.Gif;
+                case ImageFormat.Webp:
+                    return SKEncodedImageFormat.Webp;
+                default:
+                    return SKEncodedImageFormat.Png;
+            }
+        }
+
+        private static bool IsAllWhiteRow(SKBitmap bmp, int row)
+        {
+            for (var i = 0; i < bmp.Width; ++i)
+            {
+                if (!IsWhiteSpace(bmp.GetPixel(i, row)))
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        private static bool IsAllWhiteColumn(SKBitmap bmp, int col)
+        {
+            for (var i = 0; i < bmp.Height; ++i)
+            {
+                if (!IsWhiteSpace(bmp.GetPixel(col, i)))
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        private SKBitmap CropWhiteSpace(SKBitmap bitmap)
+        {
+            var topmost = 0;
+            for (int row = 0; row < bitmap.Height; ++row)
+            {
+                if (IsAllWhiteRow(bitmap, row))
+                    topmost = row;
+                else break;
+            }
+
+            int bottommost = 0;
+            for (int row = bitmap.Height - 1; row >= 0; --row)
+            {
+                if (IsAllWhiteRow(bitmap, row))
+                    bottommost = row;
+                else break;
+            }
+
+            int leftmost = 0, rightmost = 0;
+            for (int col = 0; col < bitmap.Width; ++col)
+            {
+                if (IsAllWhiteColumn(bitmap, col))
+                    leftmost = col;
+                else
+                    break;
+            }
+
+            for (int col = bitmap.Width - 1; col >= 0; --col)
+            {
+                if (IsAllWhiteColumn(bitmap, col))
+                    rightmost = col;
+                else
+                    break;
+            }
+
+            var newRect = SKRectI.Create(leftmost, topmost, rightmost - leftmost, bottommost - topmost);
+
+            using (var image = SKImage.FromBitmap(bitmap))
+            {
+                using (var subset = image.Subset(newRect))
+                {
+                    return SKBitmap.FromImage(subset);
+                    //using (var data = subset.Encode(StripCollageBuilder.GetEncodedFormat(outputPath), 90))
+                    //{
+                    //    using (var fileStream = _fileSystem.GetFileStream(outputPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
+                    //    {
+                    //        data.AsStream().CopyTo(fileStream);
+                    //    }
+                    //}
+                }
+            }
+        }
+
+        public ImageSize GetImageSize(string path)
+        {
+            using (var s = new SKFileStream(path))
+            {
+                using (var codec = SKCodec.Create(s))
+                {
+                    var info = codec.Info;
+
+                    return new ImageSize
+                    {
+                        Width = info.Width,
+                        Height = info.Height
+                    };
+                }
+            }
+        }
+
+        private string[] TransparentImageTypes = new string[] { ".png", ".gif", ".webp" };
+        private SKBitmap Decode(string path)
+        {
+            var requiresTransparencyHack = TransparentImageTypes.Contains(Path.GetExtension(path) ?? string.Empty);
+
+            if (requiresTransparencyHack)
+            {
+                using (var stream = new SKFileStream(path))
+                {
+                    var codec = SKCodec.Create(stream);
+
+                    // create the bitmap
+                    var bitmap = new SKBitmap(codec.Info.Width, codec.Info.Height);
+                    // decode
+                    codec.GetPixels(bitmap.Info, bitmap.GetPixels());
+
+                    return bitmap;
+                }
+            }
+
+            return SKBitmap.Decode(path);
+        }
+
+        private SKBitmap GetBitmap(string path, bool cropWhitespace)
+        {
+            if (cropWhitespace)
+            {
+                using (var bitmap = Decode(path))
+                {
+                    return CropWhiteSpace(bitmap);
+                }
+            }
+
+            return Decode(path);
+        }
+
+        public void EncodeImage(string inputPath, string outputPath, bool autoOrient, int width, int height, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat)
+        {
+            if (string.IsNullOrWhiteSpace(inputPath))
+            {
+                throw new ArgumentNullException("inputPath");
+            }
+            if (string.IsNullOrWhiteSpace(inputPath))
+            {
+                throw new ArgumentNullException("outputPath");
+            }
+
+            var skiaOutputFormat = GetImageFormat(selectedOutputFormat);
+
+            var hasBackgroundColor = !string.IsNullOrWhiteSpace(options.BackgroundColor);
+            var hasForegroundColor = !string.IsNullOrWhiteSpace(options.ForegroundLayer);
+            var blur = options.Blur ?? 0;
+            var hasIndicator = options.AddPlayedIndicator || options.UnplayedCount.HasValue || !options.PercentPlayed.Equals(0);
+
+            using (var bitmap = GetBitmap(inputPath, options.CropWhiteSpace))
+            {
+                using (var resizedBitmap = new SKBitmap(width, height))//, bitmap.ColorType, bitmap.AlphaType))
+                {
+                    // scale image
+                    var resizeMethod = SKBitmapResizeMethod.Lanczos3;
+
+                    bitmap.Resize(resizedBitmap, resizeMethod);
+
+                    // If all we're doing is resizing then we can stop now
+                    if (!hasBackgroundColor && !hasForegroundColor && blur == 0 && !hasIndicator)
+                    {
+                        using (var outputStream = new SKFileWStream(outputPath))
+                        {
+                            resizedBitmap.Encode(outputStream, skiaOutputFormat, quality);
+                            return;
+                        }
+                    }
+
+                    // create bitmap to use for canvas drawing
+                    using (var saveBitmap = new SKBitmap(width, height))//, bitmap.ColorType, bitmap.AlphaType))
+                    {
+                        // create canvas used to draw into bitmap
+                        using (var canvas = new SKCanvas(saveBitmap))
+                        {
+                            // set background color if present
+                            if (hasBackgroundColor)
+                            {
+                                canvas.Clear(SKColor.Parse(options.BackgroundColor));
+                            }
+
+                            // Add blur if option is present
+                            if (blur > 0)
+                            {
+                                using (var paint = new SKPaint())
+                                {
+                                    // create image from resized bitmap to apply blur
+                                    using (var filter = SKImageFilter.CreateBlur(blur, blur))
+                                    {
+                                        paint.ImageFilter = filter;
+                                        canvas.DrawBitmap(resizedBitmap, SKRect.Create(width, height), paint);
+                                    }
+                                }
+                            }
+                            else
+                            {
+                                // draw resized bitmap onto canvas
+                                canvas.DrawBitmap(resizedBitmap, SKRect.Create(width, height));
+                            }
+
+                            // If foreground layer present then draw
+                            if (hasForegroundColor)
+                            {
+                                Double opacity;
+                                if (!Double.TryParse(options.ForegroundLayer, out opacity)) opacity = .4;
+
+                                canvas.DrawColor(new SKColor(0, 0, 0, (Byte)((1 - opacity) * 0xFF)), SKBlendMode.SrcOver);
+                            }
+
+                            if (hasIndicator)
+                            {
+                                DrawIndicator(canvas, width, height, options);
+                            }
+
+                            using (var outputStream = new SKFileWStream(outputPath))
+                            {
+                                saveBitmap.Encode(outputStream, skiaOutputFormat, quality);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        public void CreateImageCollage(ImageCollageOptions options)
+        {
+            double ratio = options.Width;
+            ratio /= options.Height;
+
+            if (ratio >= 1.4)
+            {
+                new StripCollageBuilder(_appPaths, _fileSystem).BuildThumbCollage(options.InputPaths, options.OutputPath, options.Width, options.Height);
+            }
+            else if (ratio >= .9)
+            {
+                new StripCollageBuilder(_appPaths, _fileSystem).BuildSquareCollage(options.InputPaths, options.OutputPath, options.Width, options.Height);
+            }
+            else
+            {
+                // @todo create Poster collage capability
+                new StripCollageBuilder(_appPaths, _fileSystem).BuildSquareCollage(options.InputPaths, options.OutputPath, options.Width, options.Height);
+            }
+        }
+
+        private void DrawIndicator(SKCanvas canvas, int imageWidth, int imageHeight, ImageProcessingOptions options)
+        {
+            try
+            {
+                var currentImageSize = new ImageSize(imageWidth, imageHeight);
+
+                if (options.AddPlayedIndicator)
+                {
+                    var task = new PlayedIndicatorDrawer(_appPaths, _httpClientFactory(), _fileSystem).DrawPlayedIndicator(canvas, currentImageSize);
+                    Task.WaitAll(task);
+                }
+                else if (options.UnplayedCount.HasValue)
+                {
+                    new UnplayedCountIndicator(_appPaths, _httpClientFactory(), _fileSystem).DrawUnplayedCountIndicator(canvas, currentImageSize, options.UnplayedCount.Value);
+                }
+
+                if (options.PercentPlayed > 0)
+                {
+                    new PercentPlayedDrawer().Process(canvas, currentImageSize, options.PercentPlayed);
+                }
+            }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("Error drawing indicator overlay", ex);
+            }
+        }
+
+        public string Name
+        {
+            get { return "Skia"; }
+        }
+
+        public void Dispose()
+        {
+        }
+
+        public bool SupportsImageCollageCreation
+        {
+            get { return true; }
+        }
+
+        public bool SupportsImageEncoding
+        {
+            get { return true; }
+        }
+    }
+}

+ 190 - 0
Emby.Drawing.Skia/StripCollageBuilder.cs

@@ -0,0 +1,190 @@
+using SkiaSharp;
+using MediaBrowser.Common.Configuration;
+using System;
+using System.IO;
+using MediaBrowser.Model.IO;
+
+namespace Emby.Drawing.Skia
+{
+    public class StripCollageBuilder
+    {
+        private readonly IApplicationPaths _appPaths;
+        private readonly IFileSystem _fileSystem;
+
+        public StripCollageBuilder(IApplicationPaths appPaths, IFileSystem fileSystem)
+        {
+            _appPaths = appPaths;
+            _fileSystem = fileSystem;
+        }
+
+        public static SKEncodedImageFormat GetEncodedFormat(string outputPath)
+        {
+            var ext = Path.GetExtension(outputPath).ToLower();
+
+            if (ext == ".jpg" || ext == ".jpeg")
+                return SKEncodedImageFormat.Jpeg;
+
+            if (ext == ".webp")
+                return SKEncodedImageFormat.Webp;
+
+            if (ext == ".gif")
+                return SKEncodedImageFormat.Gif;
+
+            if (ext == ".bmp")
+                return SKEncodedImageFormat.Bmp;
+
+            // default to png
+            return SKEncodedImageFormat.Png;
+        }
+
+        public void BuildPosterCollage(string[] paths, string outputPath, int width, int height)
+        {
+            // @todo
+        }
+
+        public void BuildSquareCollage(string[] paths, string outputPath, int width, int height)
+        {
+            using (var bitmap = BuildSquareCollageBitmap(paths, width, height))
+            {
+                using (var outputStream = new SKFileWStream(outputPath))
+                {
+                    bitmap.Encode(outputStream, GetEncodedFormat(outputPath), 90);
+                }
+            }
+        }
+
+        public void BuildThumbCollage(string[] paths, string outputPath, int width, int height)
+        {
+            using (var bitmap = BuildThumbCollageBitmap(paths, width, height))
+            {
+                using (var outputStream = new SKFileWStream(outputPath))
+                {
+                    bitmap.Encode(outputStream, GetEncodedFormat(outputPath), 90);
+                }
+            }
+        }
+
+        private SKBitmap BuildThumbCollageBitmap(string[] paths, int width, int height)
+        {
+            var bitmap = new SKBitmap(width, height);
+
+            using (var canvas = new SKCanvas(bitmap))
+            {
+                canvas.Clear(SKColors.Black);
+
+                // determine sizes for each image that will composited into the final image
+                var iSlice = Convert.ToInt32(width * 0.23475);
+                int iTrans = Convert.ToInt32(height * .25);
+                int iHeight = Convert.ToInt32(height * .70);
+                var horizontalImagePadding = Convert.ToInt32(width * 0.0125);
+                var verticalSpacing = Convert.ToInt32(height * 0.01111111111111111111111111111111);
+                int imageIndex = 0;
+
+                for (int i = 0; i < 4; i++)
+                {
+                    using (var currentBitmap = SKBitmap.Decode(paths[imageIndex]))
+                    {
+                        // resize to the same aspect as the original
+                        int iWidth = (int)Math.Abs(iHeight * currentBitmap.Width / currentBitmap.Height);
+                        using (var resizeBitmap = new SKBitmap(iWidth, iHeight, currentBitmap.ColorType, currentBitmap.AlphaType))
+                        {
+                            currentBitmap.Resize(resizeBitmap, SKBitmapResizeMethod.Lanczos3);
+                            // determine how much to crop
+                            int ix = (int)Math.Abs((iWidth - iSlice) / 2);
+                            using (var image = SKImage.FromBitmap(resizeBitmap))
+                            {
+                                // crop image
+                                using (var subset = image.Subset(SKRectI.Create(ix, 0, iSlice, iHeight)))
+                                {
+                                    // draw image onto canvas
+                                    canvas.DrawImage(subset, (horizontalImagePadding * (i + 1)) + (iSlice * i), verticalSpacing);
+
+                                    using (var croppedBitmap = SKBitmap.FromImage(subset))
+                                    {
+                                        // create reflection of image below the drawn image
+                                        using (var reflectionBitmap = new SKBitmap(croppedBitmap.Width, croppedBitmap.Height / 2, croppedBitmap.ColorType, croppedBitmap.AlphaType))
+                                        {
+                                            // resize to half height
+                                            croppedBitmap.Resize(reflectionBitmap, SKBitmapResizeMethod.Lanczos3);
+
+                                            using (var flippedBitmap = new SKBitmap(reflectionBitmap.Width, reflectionBitmap.Height, reflectionBitmap.ColorType, reflectionBitmap.AlphaType))
+                                            {
+                                                using (var flippedCanvas = new SKCanvas(flippedBitmap))
+                                                {
+                                                    // flip image vertically
+                                                    var matrix = SKMatrix.MakeScale(1, -1);
+                                                    matrix.SetScaleTranslate(1, -1, 0, flippedBitmap.Height);
+                                                    flippedCanvas.SetMatrix(matrix);
+                                                    flippedCanvas.DrawBitmap(reflectionBitmap, 0, 0);
+                                                    flippedCanvas.ResetMatrix();
+
+                                                    // create gradient to make image appear as a reflection
+                                                    var remainingHeight = height - (iHeight + (2 * verticalSpacing));
+                                                    flippedCanvas.ClipRect(SKRect.Create(reflectionBitmap.Width, remainingHeight));
+                                                    using (var gradient = new SKPaint())
+                                                    {
+                                                        gradient.IsAntialias = true;
+                                                        gradient.BlendMode = SKBlendMode.SrcOver;
+                                                        gradient.Shader = SKShader.CreateLinearGradient(new SKPoint(0, 0), new SKPoint(0, remainingHeight), new[] { new SKColor(0, 0, 0, 128), new SKColor(0, 0, 0, 208), new SKColor(0, 0, 0, 240), new SKColor(0, 0, 0, 255) }, null, SKShaderTileMode.Clamp);
+                                                        flippedCanvas.DrawPaint(gradient);
+                                                    }
+
+                                                    // finally draw reflection onto canvas
+                                                    canvas.DrawBitmap(flippedBitmap, (horizontalImagePadding * (i + 1)) + (iSlice * i), iHeight + (2 * verticalSpacing));
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+
+                    imageIndex++;
+
+                    if (imageIndex >= paths.Length)
+                        imageIndex = 0;
+                }
+            }
+
+            return bitmap;
+        }
+
+        private SKBitmap BuildSquareCollageBitmap(string[] paths, int width, int height)
+        {
+            var bitmap = new SKBitmap(width, height);
+            var imageIndex = 0;
+            var cellWidth = width / 2;
+            var cellHeight = height / 2;
+
+            using (var canvas = new SKCanvas(bitmap))
+            {
+                for (var x = 0; x < 2; x++)
+                {
+                    for (var y = 0; y < 2; y++)
+                    {
+                        using (var currentBitmap = SKBitmap.Decode(paths[imageIndex]))
+                        {
+                            using (var resizedBitmap = new SKBitmap(cellWidth, cellHeight, currentBitmap.ColorType, currentBitmap.AlphaType))
+                            {
+                                // scale image
+                                currentBitmap.Resize(resizedBitmap, SKBitmapResizeMethod.Lanczos3);
+
+                                // draw this image into the strip at the next position
+                                var xPos = x * cellWidth;
+                                var yPos = y * cellHeight;
+                                canvas.DrawBitmap(resizedBitmap, xPos, yPos);
+                            }
+                        }
+                        imageIndex++;
+
+                        if (imageIndex >= paths.Length)
+                            imageIndex = 0;
+                    }
+                }
+            }
+
+            return bitmap;
+        }
+    }
+}

+ 68 - 0
Emby.Drawing.Skia/UnplayedCountIndicator.cs

@@ -0,0 +1,68 @@
+using SkiaSharp;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Drawing;
+using System.Globalization;
+using System.Threading.Tasks;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
+
+namespace Emby.Drawing.Skia
+{
+    public class UnplayedCountIndicator
+    {
+        private const int OffsetFromTopRightCorner = 38;
+
+        private readonly IApplicationPaths _appPaths;
+        private readonly IHttpClient _iHttpClient;
+        private readonly IFileSystem _fileSystem;
+
+        public UnplayedCountIndicator(IApplicationPaths appPaths, IHttpClient iHttpClient, IFileSystem fileSystem)
+        {
+            _appPaths = appPaths;
+            _iHttpClient = iHttpClient;
+            _fileSystem = fileSystem;
+        }
+
+        public void DrawUnplayedCountIndicator(SKCanvas canvas, ImageSize imageSize, int count)
+        {
+            var x = imageSize.Width - OffsetFromTopRightCorner;
+            var text = count.ToString(CultureInfo.InvariantCulture);
+
+            using (var paint = new SKPaint())
+            {
+                paint.Color = SKColor.Parse("#CC52B54B");
+                paint.Style = SKPaintStyle.Fill;
+                canvas.DrawCircle((float)x, OffsetFromTopRightCorner, 20, paint);
+            }
+            using (var paint = new SKPaint())
+            {
+                paint.Color = new SKColor(255, 255, 255, 255);
+                paint.Style = SKPaintStyle.Fill;
+                paint.Typeface = SKTypeface.FromFile(PlayedIndicatorDrawer.ExtractFont("robotoregular.ttf", _appPaths, _fileSystem));
+                paint.TextSize = 24;
+                paint.IsAntialias = true;
+
+                var y = OffsetFromTopRightCorner + 9;
+
+                if (text.Length == 1)
+                {
+                    x -= 7;
+                }
+                if (text.Length == 2)
+                {
+                    x -= 13;
+                }
+                else if (text.Length >= 3)
+                {
+                    x -= 15;
+                    y -= 2;
+                    paint.TextSize = 18;
+                }
+
+                canvas.DrawText(text, (float)x, y, paint);
+            }
+        }
+    }
+}

+ 4 - 0
Emby.Drawing.Skia/packages.config

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="SkiaSharp" version="1.57.1" targetFramework="portable45-net45+win8" />
+</packages>

+ 52 - 81
Emby.Drawing/ImageProcessor.cs

@@ -56,7 +56,7 @@ namespace Emby.Drawing
         private readonly IFileSystem _fileSystem;
         private readonly IJsonSerializer _jsonSerializer;
         private readonly IServerApplicationPaths _appPaths;
-        private readonly IImageEncoder _imageEncoder;
+        private IImageEncoder _imageEncoder;
         private readonly Func<ILibraryManager> _libraryManager;
 
         public ImageProcessor(ILogger logger,
@@ -64,7 +64,7 @@ namespace Emby.Drawing
             IFileSystem fileSystem,
             IJsonSerializer jsonSerializer,
             IImageEncoder imageEncoder,
-            int maxConcurrentImageProcesses, Func<ILibraryManager> libraryManager, ITimerFactory timerFactory)
+            Func<ILibraryManager> libraryManager, ITimerFactory timerFactory)
         {
             _logger = logger;
             _fileSystem = fileSystem;
@@ -103,6 +103,20 @@ namespace Emby.Drawing
             _cachedImagedSizes = new ConcurrentDictionary<Guid, ImageSize>(sizeDictionary);
         }
 
+        public IImageEncoder ImageEncoder
+        {
+            get { return _imageEncoder; }
+            set
+            {
+                if (value == null)
+                {
+                    throw new ArgumentNullException("value");
+                }
+
+                _imageEncoder = value;
+            }
+        }
+
         public string[] SupportedInputFormats
         {
             get
@@ -136,14 +150,6 @@ namespace Emby.Drawing
             }
         }
 
-        private string CroppedWhitespaceImageCachePath
-        {
-            get
-            {
-                return Path.Combine(_appPaths.ImageCachePath, "cropped-images");
-            }
-        }
-
         public void AddParts(IEnumerable<IImageEnhancer> enhancers)
         {
             ImageEnhancers = enhancers.ToArray();
@@ -186,14 +192,6 @@ namespace Emby.Drawing
                 return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
             }
 
-            if (options.CropWhiteSpace && _imageEncoder.SupportsImageEncoding)
-            {
-                var tuple = await GetWhitespaceCroppedImage(originalImagePath, dateModified).ConfigureAwait(false);
-
-                originalImagePath = tuple.Item1;
-                dateModified = tuple.Item2;
-            }
-
             if (options.Enhancers.Count > 0)
             {
                 var tuple = await GetEnhancedImage(new ItemImageInfo
@@ -214,7 +212,7 @@ namespace Emby.Drawing
                 return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
             }
 
-            ImageSize? originalImageSize;
+            ImageSize? originalImageSize = null;
             try
             {
                 originalImageSize = GetImageSize(originalImagePath, dateModified, true);
@@ -241,8 +239,8 @@ namespace Emby.Drawing
 
                 if (!_fileSystem.FileExists(cacheFilePath))
                 {
-                    var newWidth = Convert.ToInt32(newSize.Width);
-                    var newHeight = Convert.ToInt32(newSize.Height);
+                    var newWidth = Convert.ToInt32(Math.Round(newSize.Width));
+                    var newHeight = Convert.ToInt32(Math.Round(newSize.Height));
 
                     _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(cacheFilePath));
                     var tmpPath = Path.ChangeExtension(Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString("N")), Path.GetExtension(cacheFilePath));
@@ -349,22 +347,22 @@ namespace Emby.Drawing
                 return new ImageSize(options.Width.Value, options.Height.Value);
             }
 
-            var aspect = GetEstimatedAspectRatio(options.Image.Type);
+            var aspect = GetEstimatedAspectRatio(options.Image.Type, options.Item);
 
             var width = options.Width ?? options.MaxWidth;
 
             if (width.HasValue)
             {
-                var heightValue = aspect / width.Value;
-                return new ImageSize(width.Value, Convert.ToInt32(heightValue));
+                var heightValue = width.Value / aspect;
+                return new ImageSize(width.Value, heightValue);
             }
 
             var height = options.Height ?? options.MaxHeight ?? 200;
             var widthValue = aspect * height;
-            return new ImageSize(Convert.ToInt32(widthValue), height);
+            return new ImageSize(widthValue, height);
         }
 
-        private double GetEstimatedAspectRatio(ImageType type)
+        private double GetEstimatedAspectRatio(ImageType type, IHasImages item)
         {
             switch (type)
             {
@@ -384,7 +382,7 @@ namespace Emby.Drawing
                 case ImageType.Logo:
                     return 2.58;
                 case ImageType.Primary:
-                    return .667;
+                    return item.GetDefaultPrimaryImageAspectRatio() ?? .667;
                 default:
                     return 1;
             }
@@ -400,46 +398,6 @@ namespace Emby.Drawing
             return requestedFormat;
         }
 
-        /// <summary>
-        /// Crops whitespace from an image, caches the result, and returns the cached path
-        /// </summary>
-        private async Task<Tuple<string, DateTime>> GetWhitespaceCroppedImage(string originalImagePath, DateTime dateModified)
-        {
-            var name = originalImagePath;
-            name += "datemodified=" + dateModified.Ticks;
-
-            var croppedImagePath = GetCachePath(CroppedWhitespaceImageCachePath, name, Path.GetExtension(originalImagePath));
-
-            // Check again in case of contention
-            if (_fileSystem.FileExists(croppedImagePath))
-            {
-                return GetResult(croppedImagePath);
-            }
-
-            try
-            {
-                _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(croppedImagePath));
-                var tmpPath = Path.ChangeExtension(Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString("N")), Path.GetExtension(croppedImagePath));
-                _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(tmpPath));
-
-                _imageEncoder.CropWhiteSpace(originalImagePath, tmpPath);
-                CopyFile(tmpPath, croppedImagePath);
-                return GetResult(tmpPath);
-            }
-            catch (NotImplementedException)
-            {
-                // No need to spam the log with an error message
-                return new Tuple<string, DateTime>(originalImagePath, dateModified);
-            }
-            catch (Exception ex)
-            {
-                // We have to have a catch-all here because some of the .net image methods throw a plain old Exception
-                _logger.ErrorException("Error cropping image {0}", ex, originalImagePath);
-
-                return new Tuple<string, DateTime>(originalImagePath, dateModified);
-            }
-        }
-
         private Tuple<string, DateTime> GetResult(string path)
         {
             return new Tuple<string, DateTime>(path, _fileSystem.GetLastWriteTimeUtc(path));
@@ -555,26 +513,39 @@ namespace Emby.Drawing
         /// <returns>ImageSize.</returns>
         private ImageSize GetImageSizeInternal(string path, bool allowSlowMethod)
         {
+            // Can't use taglib because it keeps a lock on the file
+            //try
+            //{
+            //    using (var file = TagLib.File.Create(new StreamFileAbstraction(Path.GetFileName(path), _fileSystem.OpenRead(path), null)))
+            //    {
+            //        var image = file as TagLib.Image.File;
+
+            //        var properties = image.Properties;
+
+            //        return new ImageSize
+            //        {
+            //            Height = properties.PhotoHeight,
+            //            Width = properties.PhotoWidth
+            //        };
+            //    }
+            //}
+            //catch
+            //{
+            //}
+
             try
             {
-                using (var file = TagLib.File.Create(new StreamFileAbstraction(Path.GetFileName(path), _fileSystem.OpenRead(path), null)))
-                {
-                    var image = file as TagLib.Image.File;
-
-                    var properties = image.Properties;
-
-                    return new ImageSize
-                    {
-                        Height = properties.PhotoHeight,
-                        Width = properties.PhotoWidth
-                    };
-                }
+                return ImageHeader.GetDimensions(path, _logger, _fileSystem);
             }
             catch
             {
-            }
+                if (allowSlowMethod)
+                {
+                    return _imageEncoder.GetImageSize(path);
+                }
 
-            return ImageHeader.GetDimensions(path, _logger, _fileSystem);
+                throw;
+            }
         }
 
         private readonly ITimer _saveImageSizeTimer;

+ 5 - 0
Emby.Drawing/NullImageEncoder.cs

@@ -57,6 +57,11 @@ namespace Emby.Drawing
             get { return false; }
         }
 
+        public ImageSize GetImageSize(string path)
+        {
+            throw new NotImplementedException();
+        }
+
         public void Dispose()
         {
         }

+ 8 - 11
Emby.Server.Core/ApplicationHost.cs

@@ -187,7 +187,7 @@ namespace Emby.Server.Core
         /// <value>The HTTP server.</value>
         private IHttpServer HttpServer { get; set; }
         private IDtoService DtoService { get; set; }
-        private IImageProcessor ImageProcessor { get; set; }
+        public IImageProcessor ImageProcessor { get; set; }
 
         /// <summary>
         /// Gets or sets the media encoder.
@@ -761,7 +761,10 @@ namespace Emby.Server.Core
                     return null;
                 }
 
-                X509Certificate2 localCert = new X509Certificate2(certificateLocation, info.Password);
+                // Don't use an empty string password
+                var password = string.IsNullOrWhiteSpace(info.Password) ? null : info.Password;
+
+                X509Certificate2 localCert = new X509Certificate2(certificateLocation, password);
                 //localCert.PrivateKey = PrivateKey.CreateFromFile(pvk_file).RSA;
                 if (!localCert.HasPrivateKey)
                 {
@@ -780,14 +783,7 @@ namespace Emby.Server.Core
 
         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);
+            return new ImageProcessor(LogManager.GetLogger("ImageProcessor"), ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer, ImageEncoder, () => LibraryManager, TimerFactory);
         }
 
         protected virtual FFMpegInstallInfo GetFfmpegInstallInfo()
@@ -1132,7 +1128,8 @@ namespace Emby.Server.Core
                 // Custom cert
                 return new CertificateInfo
                 {
-                    Path = ServerConfigurationManager.Configuration.CertificatePath
+                    Path = ServerConfigurationManager.Configuration.CertificatePath,
+                    Password = ServerConfigurationManager.Configuration.CertificatePassword
                 };
             }
 

+ 1 - 1
Emby.Server.Core/IO/LibraryMonitor.cs

@@ -537,7 +537,7 @@ namespace Emby.Server.Core.IO
                     }
                 }
 
-                var newRefresher = new FileRefresher(path, _fileSystem, ConfigurationManager, LibraryManager, TaskManager, Logger, _timerFactory, _environmentInfo);
+                var newRefresher = new FileRefresher(path, _fileSystem, ConfigurationManager, LibraryManager, TaskManager, Logger, _timerFactory, _environmentInfo, LibraryManager);
                 newRefresher.Completed += NewRefresher_Completed;
                 _activeRefreshers.Add(newRefresher);
             }

+ 1 - 1
Emby.Server.Core/project.json

@@ -68,7 +68,7 @@
         "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.Net.Security": "4.3.1",
         "System.Security.Cryptography.X509Certificates": "4.3.0",
         "System.Runtime.Extensions": "4.3.0",
         "MediaBrowser.Model": {

+ 2 - 2
Emby.Server.Implementations/Activity/ActivityRepository.cs

@@ -60,7 +60,7 @@ namespace Emby.Server.Implementations.Activity
                     {
                         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("@Id", entry.Id.ToGuidBlob());
                             statement.TryBind("@Name", entry.Name);
 
                             statement.TryBind("@Overview", entry.Overview);
@@ -168,7 +168,7 @@ namespace Emby.Server.Implementations.Activity
 
             var info = new ActivityLogEntry
             {
-                Id = reader[index].ReadGuid().ToString("N")
+                Id = reader[index].ReadGuidFromBlob().ToString("N")
             };
 
             index++;

+ 2 - 2
Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs

@@ -126,7 +126,7 @@ namespace Emby.Server.Implementations.AppBase
             Logger.Info("Saving system configuration");
             var path = CommonApplicationPaths.SystemConfigurationFilePath;
 
-            FileSystem.CreateDirectory(Path.GetDirectoryName(path));
+            FileSystem.CreateDirectory(FileSystem.GetDirectoryName(path));
 
             lock (_configurationSyncLock)
             {
@@ -293,7 +293,7 @@ namespace Emby.Server.Implementations.AppBase
             _configurations.AddOrUpdate(key, configuration, (k, v) => configuration);
 
             var path = GetConfigurationFile(key);
-            FileSystem.CreateDirectory(Path.GetDirectoryName(path));
+            FileSystem.CreateDirectory(FileSystem.GetDirectoryName(path));
 
             lock (_configurationSyncLock)
             {

+ 1 - 1
Emby.Server.Implementations/AppBase/ConfigurationHelper.cs

@@ -47,7 +47,7 @@ namespace Emby.Server.Implementations.AppBase
                 // If the file didn't exist before, or if something has changed, re-save
                 if (buffer == null || !buffer.SequenceEqual(newBytes))
                 {
-                    fileSystem.CreateDirectory(Path.GetDirectoryName(path));
+                    fileSystem.CreateDirectory(fileSystem.GetDirectoryName(path));
 
                     // Save it after load in case we got new items
                     fileSystem.WriteAllBytes(path, newBytes);

+ 5 - 5
Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs

@@ -106,8 +106,8 @@ namespace Emby.Server.Implementations.Data
 
             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("@id", displayPreferences.Id.ToGuidBlob());
+                statement.TryBind("@userId", userId.ToGuidBlob());
                 statement.TryBind("@client", client);
                 statement.TryBind("@data", serialized);
 
@@ -170,8 +170,8 @@ namespace Emby.Server.Implementations.Data
                 {
                     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("@id", guidId.ToGuidBlob());
+                        statement.TryBind("@userId", userId.ToGuidBlob());
                         statement.TryBind("@client", client);
 
                         foreach (var row in statement.ExecuteQuery())
@@ -204,7 +204,7 @@ namespace Emby.Server.Implementations.Data
                 {
                     using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where userId=@userId"))
                     {
-                        statement.TryBind("@userId", userId.ToGuidParamValue());
+                        statement.TryBind("@userId", userId.ToGuidBlob());
 
                         foreach (var row in statement.ExecuteQuery())
                         {

+ 6 - 6
Emby.Server.Implementations/Data/SqliteExtensions.cs

@@ -26,17 +26,17 @@ namespace Emby.Server.Implementations.Data
             });
         }
 
-        public static byte[] ToGuidParamValue(this string str)
+        public static byte[] ToGuidBlob(this string str)
         {
-            return ToGuidParamValue(new Guid(str));
+            return ToGuidBlob(new Guid(str));
         }
 
-        public static byte[] ToGuidParamValue(this Guid guid)
+        public static byte[] ToGuidBlob(this Guid guid)
         {
             return guid.ToByteArray();
         }
 
-        public static Guid ReadGuid(this IResultSetValue result)
+        public static Guid ReadGuidFromBlob(this IResultSetValue result)
         {
             return new Guid(result.ToBlob());
         }
@@ -172,7 +172,7 @@ namespace Emby.Server.Implementations.Data
 
         public static Guid GetGuid(this IReadOnlyList<IResultSetValue> result, int index)
         {
-            return result[index].ReadGuid();
+            return result[index].ReadGuidFromBlob();
         }
 
         private static void CheckName(string name)
@@ -262,7 +262,7 @@ namespace Emby.Server.Implementations.Data
             IBindParameter bindParam;
             if (statement.BindParameters.TryGetValue(name, out bindParam))
             {
-                bindParam.Bind(value.ToGuidParamValue());
+                bindParam.Bind(value.ToGuidBlob());
             }
             else
             {

+ 4 - 4
Emby.Server.Implementations/Data/SqliteFileOrganizationRepository.cs

@@ -62,7 +62,7 @@ namespace Emby.Server.Implementations.Data
 
                         using (var statement = db.PrepareStatement(commandText))
                         {
-                            statement.TryBind("@ResultId", result.Id.ToGuidParamValue());
+                            statement.TryBind("@ResultId", result.Id.ToGuidBlob());
                             statement.TryBind("@OriginalPath", result.OriginalPath);
 
                             statement.TryBind("@TargetPath", result.TargetPath);
@@ -100,7 +100,7 @@ namespace Emby.Server.Implementations.Data
                     {
                         using (var statement = db.PrepareStatement("delete from FileOrganizerResults where ResultId = @ResultId"))
                         {
-                            statement.TryBind("@ResultId", id.ToGuidParamValue());
+                            statement.TryBind("@ResultId", id.ToGuidBlob());
                             statement.MoveNext();
                         }
                     }, TransactionMode);
@@ -188,7 +188,7 @@ namespace Emby.Server.Implementations.Data
                 {
                     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());
+                        statement.TryBind("@ResultId", id.ToGuidBlob());
 
                         foreach (var row in statement.ExecuteQuery())
                         {
@@ -207,7 +207,7 @@ namespace Emby.Server.Implementations.Data
 
             var result = new FileOrganizationResult
             {
-                Id = reader[0].ReadGuid().ToString("N")
+                Id = reader[0].ReadGuidFromBlob().ToString("N")
             };
 
             index++;

+ 28 - 28
Emby.Server.Implementations/Data/SqliteItemRepository.cs

@@ -2128,7 +2128,7 @@ namespace Emby.Server.Implementations.Data
                     connection.RunInTransaction(db =>
                     {
                         // First delete chapters
-                        db.Execute("delete from " + ChaptersTableName + " where ItemId=@ItemId", id.ToGuidParamValue());
+                        db.Execute("delete from " + ChaptersTableName + " where ItemId=@ItemId", id.ToGuidBlob());
 
                         using (var saveChapterStatement = PrepareStatement(db, "replace into " + ChaptersTableName + " (ItemId, ChapterIndex, StartPositionTicks, Name, ImagePath, ImageDateModified) values (@ItemId, @ChapterIndex, @StartPositionTicks, @Name, @ImagePath, @ImageDateModified)"))
                         {
@@ -2139,7 +2139,7 @@ namespace Emby.Server.Implementations.Data
                                     saveChapterStatement.Reset();
                                 }
 
-                                saveChapterStatement.TryBind("@ItemId", id.ToGuidParamValue());
+                                saveChapterStatement.TryBind("@ItemId", id.ToGuidBlob());
                                 saveChapterStatement.TryBind("@ChapterIndex", index);
                                 saveChapterStatement.TryBind("@StartPositionTicks", chapter.StartPositionTicks);
                                 saveChapterStatement.TryBind("@Name", chapter.Name);
@@ -2919,7 +2919,7 @@ namespace Emby.Server.Implementations.Data
 
                         foreach (var row in statement.ExecuteQuery())
                         {
-                            list.Add(row[0].ReadGuid());
+                            list.Add(row[0].ReadGuidFromBlob());
                         }
                     }
 
@@ -3113,7 +3113,7 @@ namespace Emby.Server.Implementations.Data
 
                                 foreach (var row in statement.ExecuteQuery())
                                 {
-                                    list.Add(row[0].ReadGuid());
+                                    list.Add(row[0].ReadGuidFromBlob());
                                 }
                             }
                         }
@@ -3643,7 +3643,7 @@ namespace Emby.Server.Implementations.Data
                     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());
+                        statement.TryBind(paramName, personId.ToGuidBlob());
                     }
                     index++;
                 }
@@ -3843,7 +3843,7 @@ namespace Emby.Server.Implementations.Data
                     clauses.Add("(select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from itemvalues where ItemId=Guid and Type<=1)");
                     if (statement != null)
                     {
-                        statement.TryBind(paramName, artistId.ToGuidParamValue());
+                        statement.TryBind(paramName, artistId.ToGuidBlob());
                     }
                     index++;
                 }
@@ -3862,7 +3862,7 @@ namespace Emby.Server.Implementations.Data
                     clauses.Add("Album in (select Name from typedbaseitems where guid=" + paramName + ")");
                     if (statement != null)
                     {
-                        statement.TryBind(paramName, albumId.ToGuidParamValue());
+                        statement.TryBind(paramName, albumId.ToGuidBlob());
                     }
                     index++;
                 }
@@ -3881,7 +3881,7 @@ namespace Emby.Server.Implementations.Data
                     clauses.Add("(select CleanName from TypedBaseItems where guid=" + paramName + ") not in (select CleanValue from itemvalues where ItemId=Guid and Type<=1)");
                     if (statement != null)
                     {
-                        statement.TryBind(paramName, artistId.ToGuidParamValue());
+                        statement.TryBind(paramName, artistId.ToGuidBlob());
                     }
                     index++;
                 }
@@ -3900,7 +3900,7 @@ namespace Emby.Server.Implementations.Data
                     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());
+                        statement.TryBind(paramName, genreId.ToGuidBlob());
                     }
                     index++;
                 }
@@ -3953,7 +3953,7 @@ namespace Emby.Server.Implementations.Data
                     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());
+                        statement.TryBind(paramName, studioId.ToGuidBlob());
                     }
                     index++;
                 }
@@ -4521,22 +4521,22 @@ namespace Emby.Server.Implementations.Data
                     connection.RunInTransaction(db =>
                     {
                         // Delete people
-                        ExecuteWithSingleParam(db, "delete from People where ItemId=@Id", id.ToGuidParamValue());
+                        ExecuteWithSingleParam(db, "delete from People where ItemId=@Id", id.ToGuidBlob());
 
                         // Delete chapters
-                        ExecuteWithSingleParam(db, "delete from " + ChaptersTableName + " where ItemId=@Id", id.ToGuidParamValue());
+                        ExecuteWithSingleParam(db, "delete from " + ChaptersTableName + " where ItemId=@Id", id.ToGuidBlob());
 
                         // Delete media streams
-                        ExecuteWithSingleParam(db, "delete from mediastreams where ItemId=@Id", id.ToGuidParamValue());
+                        ExecuteWithSingleParam(db, "delete from mediastreams where ItemId=@Id", id.ToGuidBlob());
 
                         // Delete ancestors
-                        ExecuteWithSingleParam(db, "delete from AncestorIds where ItemId=@Id", id.ToGuidParamValue());
+                        ExecuteWithSingleParam(db, "delete from AncestorIds where ItemId=@Id", id.ToGuidBlob());
 
                         // Delete item values
-                        ExecuteWithSingleParam(db, "delete from ItemValues where ItemId=@Id", id.ToGuidParamValue());
+                        ExecuteWithSingleParam(db, "delete from ItemValues where ItemId=@Id", id.ToGuidBlob());
 
                         // Delete the item
-                        ExecuteWithSingleParam(db, "delete from TypedBaseItems where guid=@Id", id.ToGuidParamValue());
+                        ExecuteWithSingleParam(db, "delete from TypedBaseItems where guid=@Id", id.ToGuidBlob());
                     }, TransactionMode);
                 }
             }
@@ -4643,7 +4643,7 @@ namespace Emby.Server.Implementations.Data
                 whereClauses.Add("ItemId=@ItemId");
                 if (statement != null)
                 {
-                    statement.TryBind("@ItemId", query.ItemId.ToGuidParamValue());
+                    statement.TryBind("@ItemId", query.ItemId.ToGuidBlob());
                 }
             }
             if (query.AppearsInItemId != Guid.Empty)
@@ -4651,7 +4651,7 @@ namespace Emby.Server.Implementations.Data
                 whereClauses.Add("Name in (Select Name from People where ItemId=@AppearsInItemId)");
                 if (statement != null)
                 {
-                    statement.TryBind("@AppearsInItemId", query.AppearsInItemId.ToGuidParamValue());
+                    statement.TryBind("@AppearsInItemId", query.AppearsInItemId.ToGuidBlob());
                 }
             }
             var queryPersonTypes = query.PersonTypes.Where(IsValidPersonType).ToList();
@@ -4730,14 +4730,14 @@ namespace Emby.Server.Implementations.Data
 
             // First delete 
             deleteAncestorsStatement.Reset();
-            deleteAncestorsStatement.TryBind("@ItemId", itemId.ToGuidParamValue());
+            deleteAncestorsStatement.TryBind("@ItemId", itemId.ToGuidBlob());
             deleteAncestorsStatement.MoveNext();
 
             foreach (var ancestorId in ancestorIds)
             {
                 updateAncestorsStatement.Reset();
-                updateAncestorsStatement.TryBind("@ItemId", itemId.ToGuidParamValue());
-                updateAncestorsStatement.TryBind("@AncestorId", ancestorId.ToGuidParamValue());
+                updateAncestorsStatement.TryBind("@ItemId", itemId.ToGuidBlob());
+                updateAncestorsStatement.TryBind("@AncestorId", ancestorId.ToGuidBlob());
                 updateAncestorsStatement.TryBind("@AncestorIdText", ancestorId.ToString("N"));
                 updateAncestorsStatement.MoveNext();
             }
@@ -5198,7 +5198,7 @@ namespace Emby.Server.Implementations.Data
             CheckDisposed();
 
             // First delete 
-            db.Execute("delete from ItemValues where ItemId=@Id", itemId.ToGuidParamValue());
+            db.Execute("delete from ItemValues where ItemId=@Id", itemId.ToGuidBlob());
 
             using (var statement = PrepareStatement(db, "insert into ItemValues (ItemId, Type, Value, CleanValue) values (@ItemId, @Type, @Value, @CleanValue)"))
             {
@@ -5214,7 +5214,7 @@ namespace Emby.Server.Implementations.Data
 
                     statement.Reset();
 
-                    statement.TryBind("@ItemId", itemId.ToGuidParamValue());
+                    statement.TryBind("@ItemId", itemId.ToGuidBlob());
                     statement.TryBind("@Type", pair.Item1);
                     statement.TryBind("@Value", itemValue);
 
@@ -5252,7 +5252,7 @@ namespace Emby.Server.Implementations.Data
                 {
                     // First delete 
                     // "delete from People where ItemId=?"
-                    connection.Execute("delete from People where ItemId=?", itemId.ToGuidParamValue());
+                    connection.Execute("delete from People where ItemId=?", itemId.ToGuidBlob());
 
                     var listIndex = 0;
 
@@ -5266,7 +5266,7 @@ namespace Emby.Server.Implementations.Data
                                 statement.Reset();
                             }
 
-                            statement.TryBind("@ItemId", itemId.ToGuidParamValue());
+                            statement.TryBind("@ItemId", itemId.ToGuidBlob());
                             statement.TryBind("@Name", person.Name);
                             statement.TryBind("@Role", person.Role);
                             statement.TryBind("@PersonType", person.Type);
@@ -5339,7 +5339,7 @@ namespace Emby.Server.Implementations.Data
 
                     using (var statement = PrepareStatementSafe(connection, cmdText))
                     {
-                        statement.TryBind("@ItemId", query.ItemId.ToGuidParamValue());
+                        statement.TryBind("@ItemId", query.ItemId.ToGuidBlob());
 
                         if (query.Type.HasValue)
                         {
@@ -5383,7 +5383,7 @@ namespace Emby.Server.Implementations.Data
                 using (var connection = CreateConnection())
                 {
                     // First delete chapters
-                    connection.Execute("delete from mediastreams where ItemId=@ItemId", id.ToGuidParamValue());
+                    connection.Execute("delete from mediastreams where ItemId=@ItemId", id.ToGuidBlob());
 
                     using (var statement = PrepareStatement(connection, string.Format("replace into mediastreams ({0}) values ({1})",
                                 string.Join(",", _mediaStreamSaveColumns),
@@ -5393,7 +5393,7 @@ namespace Emby.Server.Implementations.Data
                         {
                             var paramList = new List<object>();
 
-                            paramList.Add(id.ToGuidParamValue());
+                            paramList.Add(id.ToGuidBlob());
                             paramList.Add(stream.Index);
                             paramList.Add(stream.Type.ToString());
                             paramList.Add(stream.Codec);

+ 4 - 4
Emby.Server.Implementations/Data/SqliteUserDataRepository.cs

@@ -213,7 +213,7 @@ namespace Emby.Server.Implementations.Data
         {
             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("@userId", userId.ToGuidBlob());
                 statement.TryBind("@key", key);
 
                 if (userData.Rating.HasValue)
@@ -311,7 +311,7 @@ namespace Emby.Server.Implementations.Data
                 {
                     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("@UserId", userId.ToGuidBlob());
                         statement.TryBind("@Key", key);
 
                         foreach (var row in statement.ExecuteQuery())
@@ -364,7 +364,7 @@ namespace Emby.Server.Implementations.Data
                 {
                     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());
+                        statement.TryBind("@UserId", userId.ToGuidBlob());
 
                         foreach (var row in statement.ExecuteQuery())
                         {
@@ -386,7 +386,7 @@ namespace Emby.Server.Implementations.Data
             var userData = new UserItemData();
 
             userData.Key = reader[0].ToString();
-            userData.UserId = reader[1].ReadGuid();
+            userData.UserId = reader[1].ReadGuidFromBlob();
 
             if (reader[2].SQLiteType != SQLiteType.Null)
             {

+ 3 - 3
Emby.Server.Implementations/Data/SqliteUserRepository.cs

@@ -93,7 +93,7 @@ namespace Emby.Server.Implementations.Data
                     {
                         using (var statement = db.PrepareStatement("replace into users (guid, data) values (@guid, @data)"))
                         {
-                            statement.TryBind("@guid", user.Id.ToGuidParamValue());
+                            statement.TryBind("@guid", user.Id.ToGuidBlob());
                             statement.TryBind("@data", serialized);
                             statement.MoveNext();
                         }
@@ -116,7 +116,7 @@ namespace Emby.Server.Implementations.Data
                 {
                     foreach (var row in connection.Query("select guid,data from users"))
                     {
-                        var id = row[0].ReadGuid();
+                        var id = row[0].ReadGuidFromBlob();
 
                         using (var stream = _memoryStreamProvider.CreateNew(row[1].ToBlob()))
                         {
@@ -156,7 +156,7 @@ namespace Emby.Server.Implementations.Data
                     {
                         using (var statement = db.PrepareStatement("delete from users where guid=@id"))
                         {
-                            statement.TryBind("@id", user.Id.ToGuidParamValue());
+                            statement.TryBind("@id", user.Id.ToGuidBlob());
                             statement.MoveNext();
                         }
                     }, TransactionMode);

+ 1 - 1
Emby.Server.Implementations/Devices/DeviceManager.cs

@@ -158,7 +158,7 @@ namespace Emby.Server.Implementations.Devices
 
             _libraryMonitor.ReportFileSystemChangeBeginning(path);
 
-            _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
+            _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
 
             try
             {

+ 2 - 2
Emby.Server.Implementations/Devices/DeviceRepository.cs

@@ -46,7 +46,7 @@ namespace Emby.Server.Implementations.Devices
         public Task SaveDevice(DeviceInfo device)
         {
             var path = Path.Combine(GetDevicePath(device.Id), "device.json");
-            _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
+            _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
 
             lock (_syncLock)
             {
@@ -180,7 +180,7 @@ namespace Emby.Server.Implementations.Devices
         public void AddCameraUpload(string deviceId, LocalFileInfo file)
         {
             var path = Path.Combine(GetDevicePath(deviceId), "camerauploads.json");
-            _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
+            _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
 
             lock (_syncLock)
             {

+ 1 - 1
Emby.Server.Implementations/Emby.Server.Implementations.csproj

@@ -308,7 +308,7 @@
       <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.2\lib\portable-net45+netcore45+wpa81+MonoAndroid10+MonoTouch10+Xamarin.iOS10\SQLitePCLRaw.core.dll</HintPath>
+      <HintPath>..\packages\SQLitePCLRaw.core.1.1.5\lib\portable-net45+netcore45+wpa81+MonoAndroid10+MonoTouch10+Xamarin.iOS10\SQLitePCLRaw.core.dll</HintPath>
       <Private>True</Private>
     </Reference>
   </ItemGroup>

+ 2 - 2
Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs

@@ -97,7 +97,7 @@ namespace Emby.Server.Implementations.FFMpeg
                 else
                 {
                     info = existingVersion;
-                    versionedDirectoryPath = Path.GetDirectoryName(info.EncoderPath);
+                    versionedDirectoryPath = _fileSystem.GetDirectoryName(info.EncoderPath);
                     excludeFromDeletions.Add(versionedDirectoryPath);
                 }
             }
@@ -135,7 +135,7 @@ namespace Emby.Server.Implementations.FFMpeg
                     {
                         EncoderPath = encoder,
                         ProbePath = probe,
-                        Version = Path.GetFileName(Path.GetDirectoryName(probe))
+                        Version = Path.GetFileName(_fileSystem.GetDirectoryName(probe))
                     };
                 }
             }

+ 6 - 3
Emby.Server.Implementations/HttpServer/HttpListenerHost.cs

@@ -228,7 +228,8 @@ namespace Emby.Server.Implementations.HttpServer
                 _streamFactory,
                 _enableDualModeSockets,
                 GetRequest,
-                _fileSystem);
+                _fileSystem,
+                _environment);
         }
 
         private IHttpRequest GetRequest(HttpListenerContext httpContext)
@@ -452,6 +453,7 @@ namespace Emby.Server.Implementations.HttpServer
             var date = DateTime.Now;
             var httpRes = httpReq.Response;
             bool enableLog = false;
+            bool logHeaders = false;
             string urlToLog = null;
             string remoteIp = null;
 
@@ -490,13 +492,14 @@ namespace Emby.Server.Implementations.HttpServer
                 var urlString = url.OriginalString;
                 enableLog = EnableLogging(urlString, localPath);
                 urlToLog = urlString;
+                 logHeaders = enableLog && urlToLog.IndexOf("/videos/", StringComparison.OrdinalIgnoreCase) != -1;
 
                 if (enableLog)
                 {
                     urlToLog = GetUrlToLog(urlString);
                     remoteIp = httpReq.RemoteIp;
 
-                    LoggerUtils.LogRequest(_logger, urlToLog, httpReq.HttpMethod, httpReq.UserAgent);
+                    LoggerUtils.LogRequest(_logger, urlToLog, httpReq.HttpMethod, httpReq.UserAgent, logHeaders ? httpReq.Headers : null);
                 }
 
                 if (string.Equals(localPath, "/emby/", StringComparison.OrdinalIgnoreCase) ||
@@ -611,7 +614,7 @@ namespace Emby.Server.Implementations.HttpServer
 
                     var duration = DateTime.Now - date;
 
-                    LoggerUtils.LogResponse(_logger, statusCode, urlToLog, remoteIp, duration);
+                    LoggerUtils.LogResponse(_logger, statusCode, urlToLog, remoteIp, duration, logHeaders ? httpRes.Headers : null);
                 }
             }
         }

+ 18 - 28
Emby.Server.Implementations/HttpServer/HttpResultFactory.cs

@@ -353,31 +353,28 @@ namespace Emby.Server.Implementations.HttpServer
         /// <summary>
         /// Pres the process optimized result.
         /// </summary>
-        /// <param name="requestContext">The request context.</param>
-        /// <param name="responseHeaders">The responseHeaders.</param>
-        /// <param name="cacheKey">The cache key.</param>
-        /// <param name="cacheKeyString">The cache key string.</param>
-        /// <param name="lastDateModified">The last date modified.</param>
-        /// <param name="cacheDuration">Duration of the cache.</param>
-        /// <param name="contentType">Type of the content.</param>
-        /// <returns>System.Object.</returns>
         private object GetCachedResult(IRequest requestContext, IDictionary<string, string> responseHeaders, Guid cacheKey, string cacheKeyString, DateTime? lastDateModified, TimeSpan? cacheDuration, string contentType)
         {
             responseHeaders["ETag"] = string.Format("\"{0}\"", cacheKeyString);
 
-            if (IsNotModified(requestContext, cacheKey, lastDateModified, cacheDuration))
+            var noCache = (requestContext.Headers.Get("Cache-Control") ?? string.Empty).IndexOf("no-cache", StringComparison.OrdinalIgnoreCase) != -1;
+
+            if (!noCache)
             {
-                AddAgeHeader(responseHeaders, lastDateModified);
-                AddExpiresHeader(responseHeaders, cacheKeyString, cacheDuration);
+                if (IsNotModified(requestContext, cacheKey, lastDateModified, cacheDuration))
+                {
+                    AddAgeHeader(responseHeaders, lastDateModified);
+                    AddExpiresHeader(responseHeaders, cacheKeyString, cacheDuration, noCache);
 
-                var result = new HttpResult(new byte[] { }, contentType ?? "text/html", HttpStatusCode.NotModified);
+                    var result = new HttpResult(new byte[] { }, contentType ?? "text/html", HttpStatusCode.NotModified);
 
-                AddResponseHeaders(result, responseHeaders);
+                    AddResponseHeaders(result, responseHeaders);
 
-                return result;
+                    return result;
+                }
             }
 
-            AddCachingHeaders(responseHeaders, cacheKeyString, lastDateModified, cacheDuration);
+            AddCachingHeaders(responseHeaders, cacheKeyString, lastDateModified, cacheDuration, noCache);
 
             return null;
         }
@@ -673,11 +670,7 @@ namespace Emby.Server.Implementations.HttpServer
         /// <summary>
         /// Adds the caching responseHeaders.
         /// </summary>
-        /// <param name="responseHeaders">The responseHeaders.</param>
-        /// <param name="cacheKey">The cache key.</param>
-        /// <param name="lastDateModified">The last date modified.</param>
-        /// <param name="cacheDuration">Duration of the cache.</param>
-        private void AddCachingHeaders(IDictionary<string, string> responseHeaders, string cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration)
+        private void AddCachingHeaders(IDictionary<string, string> responseHeaders, string cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration, bool noCache)
         {
             // Don't specify both last modified and Etag, unless caching unconditionally. They are redundant
             // https://developers.google.com/speed/docs/best-practices/caching#LeverageBrowserCaching
@@ -687,11 +680,11 @@ namespace Emby.Server.Implementations.HttpServer
                 responseHeaders["Last-Modified"] = lastDateModified.Value.ToString("r");
             }
 
-            if (cacheDuration.HasValue)
+            if (!noCache && cacheDuration.HasValue)
             {
                 responseHeaders["Cache-Control"] = "public, max-age=" + Convert.ToInt32(cacheDuration.Value.TotalSeconds);
             }
-            else if (!string.IsNullOrEmpty(cacheKey))
+            else if (!noCache && !string.IsNullOrEmpty(cacheKey))
             {
                 responseHeaders["Cache-Control"] = "public";
             }
@@ -701,18 +694,15 @@ namespace Emby.Server.Implementations.HttpServer
                 responseHeaders["pragma"] = "no-cache, no-store, must-revalidate";
             }
 
-            AddExpiresHeader(responseHeaders, cacheKey, cacheDuration);
+            AddExpiresHeader(responseHeaders, cacheKey, cacheDuration, noCache);
         }
 
         /// <summary>
         /// Adds the expires header.
         /// </summary>
-        /// <param name="responseHeaders">The responseHeaders.</param>
-        /// <param name="cacheKey">The cache key.</param>
-        /// <param name="cacheDuration">Duration of the cache.</param>
-        private void AddExpiresHeader(IDictionary<string, string> responseHeaders, string cacheKey, TimeSpan? cacheDuration)
+        private void AddExpiresHeader(IDictionary<string, string> responseHeaders, string cacheKey, TimeSpan? cacheDuration, bool noCache)
         {
-            if (cacheDuration.HasValue)
+            if (!noCache && cacheDuration.HasValue)
             {
                 responseHeaders["Expires"] = DateTime.UtcNow.Add(cacheDuration.Value).ToString("r");
             }

+ 16 - 4
Emby.Server.Implementations/HttpServer/LoggerUtils.cs

@@ -1,6 +1,8 @@
 using MediaBrowser.Model.Logging;
 using System;
 using System.Globalization;
+using System.Linq;
+using MediaBrowser.Model.Services;
 using SocketHttpListener.Net;
 
 namespace Emby.Server.Implementations.HttpServer
@@ -19,9 +21,18 @@ namespace Emby.Server.Implementations.HttpServer
             logger.Info("{0} {1}. UserAgent: {2}", request.IsWebSocketRequest ? "WS" : "HTTP " + request.HttpMethod, url, request.UserAgent ?? string.Empty);
         }
 
-        public static void LogRequest(ILogger logger, string url, string method, string userAgent)
+        public static void LogRequest(ILogger logger, string url, string method, string userAgent, QueryParamCollection headers)
         {
-            logger.Info("{0} {1}. UserAgent: {2}", "HTTP " + method, url, userAgent ?? string.Empty);
+            if (headers == null)
+            {
+                logger.Info("{0} {1}. UserAgent: {2}", "HTTP " + method, url, userAgent ?? string.Empty);
+            }
+            else
+            {
+                var headerText = string.Join(", ", headers.Select(i => i.Name + "=" + i.Value).ToArray());
+
+                logger.Info("HTTP {0} {1}. {2}", method, url, headerText);
+            }
         }
 
         /// <summary>
@@ -32,12 +43,13 @@ namespace Emby.Server.Implementations.HttpServer
         /// <param name="url">The URL.</param>
         /// <param name="endPoint">The end point.</param>
         /// <param name="duration">The duration.</param>
-        public static void LogResponse(ILogger logger, int statusCode, string url, string endPoint, TimeSpan duration)
+        public static void LogResponse(ILogger logger, int statusCode, string url, string endPoint, TimeSpan duration, QueryParamCollection headers)
         {
             var durationMs = duration.TotalMilliseconds;
             var logSuffix = durationMs >= 1000 && durationMs < 60000 ? "ms (slow)" : "ms";
 
-            logger.Info("HTTP Response {0} to {1}. Time: {2}{3}. {4}", statusCode, endPoint, Convert.ToInt32(durationMs).ToString(CultureInfo.InvariantCulture), logSuffix, url);
+            var headerText = headers == null ? string.Empty : "Headers: " + string.Join(", ", headers.Where(i => i.Name.IndexOf("Access-", StringComparison.OrdinalIgnoreCase) == -1).Select(i => i.Name + "=" + i.Value).ToArray());
+            logger.Info("HTTP Response {0} to {1}. Time: {2}{3}. {4} {5}", statusCode, endPoint, Convert.ToInt32(durationMs).ToString(CultureInfo.InvariantCulture), logSuffix, url, headerText);
         }
     }
 }

+ 5 - 2
Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs

@@ -10,6 +10,7 @@ using MediaBrowser.Model.Cryptography;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Services;
+using MediaBrowser.Model.System;
 using MediaBrowser.Model.Text;
 using SocketHttpListener.Primitives;
 
@@ -30,8 +31,9 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
         private readonly IFileSystem _fileSystem;
         private readonly Func<HttpListenerContext, IHttpRequest> _httpRequestFactory;
         private readonly bool _enableDualMode;
+        private readonly IEnvironmentInfo _environment;
 
-        public WebSocketSharpListener(ILogger logger, ICertificate certificate, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, INetworkManager networkManager, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, bool enableDualMode, Func<HttpListenerContext, IHttpRequest> httpRequestFactory, IFileSystem fileSystem)
+        public WebSocketSharpListener(ILogger logger, ICertificate certificate, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, INetworkManager networkManager, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, bool enableDualMode, Func<HttpListenerContext, IHttpRequest> httpRequestFactory, IFileSystem fileSystem, IEnvironmentInfo environment)
         {
             _logger = logger;
             _certificate = certificate;
@@ -44,6 +46,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
             _enableDualMode = enableDualMode;
             _httpRequestFactory = httpRequestFactory;
             _fileSystem = fileSystem;
+            _environment = environment;
         }
 
         public Action<Exception, IRequest, bool> ErrorHandler { get; set; }
@@ -56,7 +59,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
         public void Start(IEnumerable<string> urlPrefixes)
         {
             if (_listener == null)
-                _listener = new HttpListener(_logger, _cryptoProvider, _streamFactory, _socketFactory, _networkManager, _textEncoding, _memoryStreamProvider, _fileSystem);
+                _listener = new HttpListener(_logger, _cryptoProvider, _streamFactory, _socketFactory, _networkManager, _textEncoding, _memoryStreamProvider, _fileSystem, _environment);
 
             _listener.EnableDualMode = _enableDualMode;
 

+ 9 - 0
Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs

@@ -7,6 +7,7 @@ using System.Threading;
 using System.Threading.Tasks;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Services;
 using SocketHttpListener.Net;
 using HttpListenerResponse = SocketHttpListener.Net.HttpListenerResponse;
 using IHttpResponse = MediaBrowser.Model.Services.IHttpResponse;
@@ -66,6 +67,14 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
             _response.AddHeader(name, value);
         }
 
+        public QueryParamCollection Headers
+        {
+            get
+            {
+                return _response.Headers;
+            }
+        }
+
         public string GetHeader(string name)
         {
             return _response.Headers[name];

+ 9 - 1
Emby.Server.Implementations/IO/FileRefresher.cs

@@ -34,8 +34,9 @@ namespace Emby.Server.Implementations.IO
 
         public event EventHandler<EventArgs> Completed;
         private readonly IEnvironmentInfo _environmentInfo;
+        private readonly ILibraryManager _libraryManager;
 
-        public FileRefresher(string path, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ITaskManager taskManager, ILogger logger, ITimerFactory timerFactory, IEnvironmentInfo environmentInfo)
+        public FileRefresher(string path, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ITaskManager taskManager, ILogger logger, ITimerFactory timerFactory, IEnvironmentInfo environmentInfo, ILibraryManager libraryManager1)
         {
             logger.Debug("New file refresher created for {0}", path);
             Path = path;
@@ -47,6 +48,7 @@ namespace Emby.Server.Implementations.IO
             Logger = logger;
             _timerFactory = timerFactory;
             _environmentInfo = environmentInfo;
+            _libraryManager = libraryManager1;
             AddPath(path);
         }
 
@@ -235,6 +237,12 @@ namespace Emby.Server.Implementations.IO
                 return false;
             }
 
+            // Only try to open video files
+            if (!_libraryManager.IsVideoFile(path))
+            {
+                return false;
+            }
+
             try
             {
                 var data = _fileSystem.GetFileSystemInfo(path);

+ 1 - 1
Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs

@@ -258,7 +258,7 @@ namespace Emby.Server.Implementations.Images
                 {
                     return await CreateSquareCollage(item, itemsWithImages, outputPath).ConfigureAwait(false);
                 }
-                if (item is Playlist || item is MusicGenre || item is Genre || item is GameGenre)
+                if (item is Playlist || item is MusicGenre || item is Genre || item is GameGenre || item is PhotoAlbum)
                 {
                     return await CreateSquareCollage(item, itemsWithImages, outputPath).ConfigureAwait(false);
                 }

+ 1 - 0
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -1197,6 +1197,7 @@ namespace Emby.Server.Implementations.Library
                 catch (OperationCanceledException)
                 {
                     _logger.Info("Post-scan task cancelled: {0}", task.GetType().Name);
+                    throw;
                 }
                 catch (Exception ex)
                 {

+ 1 - 1
Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs

@@ -63,7 +63,7 @@ namespace Emby.Server.Implementations.Library.Validators
                 catch (OperationCanceledException)
                 {
                     // Don't clutter the log
-                    break;
+                    throw;
                 }
                 catch (Exception ex)
                 {

+ 1 - 1
Emby.Server.Implementations/Library/Validators/GameGenresValidator.cs

@@ -53,7 +53,7 @@ namespace Emby.Server.Implementations.Library.Validators
                 catch (OperationCanceledException)
                 {
                     // Don't clutter the log
-                    break;
+                    throw;
                 }
                 catch (Exception ex)
                 {

+ 1 - 1
Emby.Server.Implementations/Library/Validators/GenresValidator.cs

@@ -54,7 +54,7 @@ namespace Emby.Server.Implementations.Library.Validators
                 catch (OperationCanceledException)
                 {
                     // Don't clutter the log
-                    break;
+                    throw;
                 }
                 catch (Exception ex)
                 {

+ 1 - 1
Emby.Server.Implementations/Library/Validators/MusicGenresValidator.cs

@@ -54,7 +54,7 @@ namespace Emby.Server.Implementations.Library.Validators
                 catch (OperationCanceledException)
                 {
                     // Don't clutter the log
-                    break;
+                    throw;
                 }
                 catch (Exception ex)
                 {

+ 1 - 1
Emby.Server.Implementations/Library/Validators/StudiosValidator.cs

@@ -53,7 +53,7 @@ namespace Emby.Server.Implementations.Library.Validators
                 catch (OperationCanceledException)
                 {
                     // Don't clutter the log
-                    break;
+                    throw;
                 }
                 catch (Exception ex)
                 {

+ 3 - 1
Emby.Server.Implementations/Library/Validators/YearsPostScanTask.cs

@@ -26,6 +26,8 @@ namespace Emby.Server.Implementations.Library.Validators
 
             while (yearNumber < maxYear)
             {
+                cancellationToken.ThrowIfCancellationRequested();
+
                 try
                 {
                     var year = _libraryManager.GetYear(yearNumber);
@@ -35,7 +37,7 @@ namespace Emby.Server.Implementations.Library.Validators
                 catch (OperationCanceledException)
                 {
                     // Don't clutter the log
-                    break;
+                    throw;
                 }
                 catch (Exception ex)
                 {

+ 9 - 9
Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs

@@ -81,7 +81,7 @@ namespace Emby.Server.Implementations.Notifications
             }
 
             clauses.Add("UserId=?");
-            paramList.Add(query.UserId.ToGuidParamValue());
+            paramList.Add(query.UserId.ToGuidBlob());
 
             var whereClause = " where " + string.Join(" And ", clauses.ToArray());
 
@@ -133,7 +133,7 @@ namespace Emby.Server.Implementations.Notifications
                     using (var statement = connection.PrepareStatement("select Level from Notifications where UserId=@UserId and IsRead=@IsRead"))
                     {
                         statement.TryBind("@IsRead", false);
-                        statement.TryBind("@UserId", userId.ToGuidParamValue());
+                        statement.TryBind("@UserId", userId.ToGuidBlob());
 
                         var levels = new List<NotificationLevel>();
 
@@ -159,8 +159,8 @@ namespace Emby.Server.Implementations.Notifications
         {
             var notification = new Notification
             {
-                Id = reader[0].ReadGuid().ToString("N"),
-                UserId = reader[1].ReadGuid().ToString("N"),
+                Id = reader[0].ReadGuidFromBlob().ToString("N"),
+                UserId = reader[1].ReadGuidFromBlob().ToString("N"),
                 Date = reader[2].ReadDateTime(),
                 Name = reader[3].ToString()
             };
@@ -251,8 +251,8 @@ namespace Emby.Server.Implementations.Notifications
                     {
                         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("@Id", notification.Id.ToGuidBlob());
+                            statement.TryBind("@UserId", notification.UserId.ToGuidBlob());
                             statement.TryBind("@Date", notification.Date.ToDateTimeParamValue());
                             statement.TryBind("@Name", notification.Name);
                             statement.TryBind("@Description", notification.Description);
@@ -315,7 +315,7 @@ namespace Emby.Server.Implementations.Notifications
                         using (var statement = conn.PrepareStatement("update Notifications set IsRead=@IsRead where UserId=@UserId"))
                         {
                             statement.TryBind("@IsRead", isRead);
-                            statement.TryBind("@UserId", userId.ToGuidParamValue());
+                            statement.TryBind("@UserId", userId.ToGuidBlob());
 
                             statement.MoveNext();
                         }
@@ -337,13 +337,13 @@ namespace Emby.Server.Implementations.Notifications
                         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());
+                            statement.TryBind("@UserId", userId.ToGuidBlob());
 
                             foreach (var id in notificationIdList)
                             {
                                 statement.Reset();
 
-                                statement.TryBind("@Id", id.ToGuidParamValue());
+                                statement.TryBind("@Id", id.ToGuidBlob());
 
                                 statement.MoveNext();
                             }

+ 3 - 3
Emby.Server.Implementations/Security/AuthenticationRepository.cs

@@ -74,7 +74,7 @@ namespace Emby.Server.Implementations.Security
                     {
                         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("@Id", info.Id.ToGuidBlob());
                             statement.TryBind("@AccessToken", info.AccessToken);
 
                             statement.TryBind("@DeviceId", info.DeviceId);
@@ -259,7 +259,7 @@ namespace Emby.Server.Implementations.Security
 
                     using (var statement = connection.PrepareStatement(commandText))
                     {
-                        statement.BindParameters["@Id"].Bind(id.ToGuidParamValue());
+                        statement.BindParameters["@Id"].Bind(id.ToGuidBlob());
 
                         foreach (var row in statement.ExecuteQuery())
                         {
@@ -275,7 +275,7 @@ namespace Emby.Server.Implementations.Security
         {
             var info = new AuthenticationInfo
             {
-                Id = reader[0].ReadGuid().ToString("N"),
+                Id = reader[0].ReadGuidFromBlob().ToString("N"),
                 AccessToken = reader[1].ToString()
             };
 

+ 3 - 3
Emby.Server.Implementations/Social/SharingRepository.cs

@@ -61,7 +61,7 @@ namespace Emby.Server.Implementations.Social
                         var commandText = "replace into Shares (Id, ItemId, UserId, ExpirationDate) values (?, ?, ?, ?)";
 
                         db.Execute(commandText,
-                            info.Id.ToGuidParamValue(),
+                            info.Id.ToGuidBlob(),
                             info.ItemId,
                             info.UserId,
                             info.ExpirationDate.ToDateTimeParamValue());
@@ -84,7 +84,7 @@ namespace Emby.Server.Implementations.Social
                     var commandText = "select Id, ItemId, UserId, ExpirationDate from Shares where id = ?";
 
                     var paramList = new List<object>();
-                    paramList.Add(id.ToGuidParamValue());
+                    paramList.Add(id.ToGuidBlob());
 
                     foreach (var row in connection.Query(commandText, paramList.ToArray()))
                     {
@@ -100,7 +100,7 @@ namespace Emby.Server.Implementations.Social
         {
             var info = new SocialShareInfo();
 
-            info.Id = reader[0].ReadGuid().ToString("N");
+            info.Id = reader[0].ReadGuidFromBlob().ToString("N");
             info.ItemId = reader[1].ToString();
             info.UserId = reader[2].ToString();
             info.ExpirationDate = reader[3].ReadDateTime();

+ 12 - 2
Emby.Server.Implementations/Updates/InstallationManager.cs

@@ -664,9 +664,19 @@ namespace Emby.Server.Implementations.Updates
             // Remove it the quick way for now
             _applicationHost.RemovePlugin(plugin);
 
-            _logger.Info("Deleting plugin file {0}", plugin.AssemblyFilePath);
+            var path = plugin.AssemblyFilePath;
+            _logger.Info("Deleting plugin file {0}", path);
 
-            _fileSystem.DeleteFile(plugin.AssemblyFilePath);
+            // Make this case-insensitive to account for possible incorrect assembly naming
+            var file = _fileSystem.GetFilePaths(path)
+                .FirstOrDefault(i => string.Equals(i, path, StringComparison.OrdinalIgnoreCase));
+
+            if (!string.IsNullOrWhiteSpace(file))
+            {
+                path = file;
+            }
+
+            _fileSystem.DeleteFile(path);
 
             OnPluginUninstalled(plugin);
 

+ 1 - 1
Emby.Server.Implementations/packages.config

@@ -3,5 +3,5 @@
   <package id="Emby.XmlTv" version="1.0.8" targetFramework="portable45-net45+win8" />
   <package id="MediaBrowser.Naming" version="1.0.5" targetFramework="portable45-net45+win8" />
   <package id="SQLitePCL.pretty" version="1.1.0" targetFramework="portable45-net45+win8" />
-  <package id="SQLitePCLRaw.core" version="1.1.2" targetFramework="portable45-net45+win8" />
+  <package id="SQLitePCLRaw.core" version="1.1.5" targetFramework="portable45-net45+win8" />
 </packages>

+ 0 - 2
Emby.Server.sln

@@ -54,8 +54,6 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Emby.Server.Core", "Emby.Se
 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

+ 0 - 4
MediaBrowser.Api/MediaBrowser.Api.csproj

@@ -82,10 +82,6 @@
     <Compile Include="Reports\Model\ReportRow.cs" />
     <Compile Include="Reports\ReportRequests.cs" />
     <Compile Include="Reports\ReportsService.cs" />
-    <Compile Include="Reports\Stat\ReportStatBuilder.cs" />
-    <Compile Include="Reports\Stat\ReportStatGroup.cs" />
-    <Compile Include="Reports\Stat\ReportStatItem.cs" />
-    <Compile Include="Reports\Stat\ReportStatResult.cs" />
     <Compile Include="Social\SharingService.cs" />
     <Compile Include="StartupWizardService.cs" />
     <Compile Include="Subtitles\SubtitleService.cs" />

+ 4 - 1
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -671,12 +671,15 @@ namespace MediaBrowser.Api.Playback
                 request.AudioCodec = EncodingHelper.InferAudioCodec(url);
             }
 
+            var enableDlnaHeaders = !string.IsNullOrWhiteSpace(request.Params) /*||
+                                    string.Equals(Request.Headers.Get("GetContentFeatures.DLNA.ORG"), "1", StringComparison.OrdinalIgnoreCase)*/;
+
             var state = new StreamState(MediaSourceManager, Logger, TranscodingJobType)
             {
                 Request = request,
                 RequestedUrl = url,
                 UserAgent = Request.UserAgent,
-                EnableDlnaHeaders = !string.IsNullOrWhiteSpace(request.Params)
+                EnableDlnaHeaders = enableDlnaHeaders
             };
 
             var auth = AuthorizationContext.GetAuthorizationInfo(Request);

+ 4 - 8
MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs

@@ -879,7 +879,7 @@ namespace MediaBrowser.Api.Playback.Hls
                 // Add resolution params, if specified
                 if (!hasGraphicalSubs)
                 {
-                    args += EncodingHelper.GetOutputSizeParam(state, codec, EnableCopyTs(state));
+                    args += EncodingHelper.GetOutputSizeParam(state, codec, true);
                 }
 
                 // This is for internal graphical subs
@@ -891,7 +891,7 @@ namespace MediaBrowser.Api.Playback.Hls
                 //args += " -flags -global_header";
             }
 
-            if (EnableCopyTs(state) && args.IndexOf("-copyts", StringComparison.OrdinalIgnoreCase) == -1)
+            if (args.IndexOf("-copyts", StringComparison.OrdinalIgnoreCase) == -1)
             {
                 args += " -copyts";
             }
@@ -901,13 +901,9 @@ namespace MediaBrowser.Api.Playback.Hls
                 args += " -vsync " + state.OutputVideoSync;
             }
 
-            return args;
-        }
+            args += EncodingHelper.GetOutputFFlags(state);
 
-        private bool EnableCopyTs(StreamState state)
-        {
-            //return state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode;
-            return true;
+            return args;
         }
 
         protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)

+ 2 - 0
MediaBrowser.Api/Playback/Hls/VideoHlsService.cs

@@ -124,6 +124,8 @@ namespace MediaBrowser.Api.Playback.Hls
                 args += " -vsync " + state.OutputVideoSync;
             }
 
+            args += EncodingHelper.GetOutputFFlags(state);
+
             return args;
         }
 

+ 2 - 1
MediaBrowser.Api/Reports/Common/HeaderMetadata.cs

@@ -35,7 +35,8 @@ namespace MediaBrowser.Api.Reports
 		Tracks,
 		EpisodeSeries,
 		EpisodeSeason,
-		AudioAlbumArtist,
+        EpisodeNumber,
+        AudioAlbumArtist,
 		MusicArtist,
 		AudioAlbum,
         Locked,

+ 5 - 0
MediaBrowser.Api/Reports/Common/ReportBuilderBase.cs

@@ -148,6 +148,11 @@ namespace MediaBrowser.Api.Reports
         /// <returns> The localized header. </returns>
         protected static string GetLocalizedHeader(HeaderMetadata internalHeader)
         {
+            if (internalHeader == HeaderMetadata.EpisodeNumber)
+            {
+                return "Episode";
+            }
+
             string headerName = "";
             if (internalHeader != HeaderMetadata.None)
             {

+ 10 - 3
MediaBrowser.Api/Reports/Data/ReportBuilder.cs

@@ -102,7 +102,7 @@ namespace MediaBrowser.Api.Reports
 						HeaderMetadata.Series,
 						HeaderMetadata.Season,
 						HeaderMetadata.SeasonNumber,
-						HeaderMetadata.DateAdded,
+                        HeaderMetadata.DateAdded,
 						HeaderMetadata.Year,
 						HeaderMetadata.Genres
 					};
@@ -269,10 +269,11 @@ namespace MediaBrowser.Api.Reports
                         HeaderMetadata.ImagePrimary,
                         HeaderMetadata.ImageBackdrop,
                         HeaderMetadata.ImageLogo,
-						HeaderMetadata.Name,
+                        HeaderMetadata.Name,
 						HeaderMetadata.EpisodeSeries,
 						HeaderMetadata.Season,
-						HeaderMetadata.DateAdded,
+                        HeaderMetadata.EpisodeNumber,
+                        HeaderMetadata.DateAdded,
 						HeaderMetadata.ReleaseDate,
 						HeaderMetadata.Year,
 						HeaderMetadata.Genres,
@@ -450,6 +451,12 @@ namespace MediaBrowser.Api.Reports
                     internalHeader = HeaderMetadata.Season;
                     break;
 
+                case HeaderMetadata.EpisodeNumber:
+                    option.Column = (i, r) => this.GetObject<BaseItem, string>(i, (x) => x.IndexNumber == null ? "" : x.IndexNumber.ToString());
+                    //option.Header.SortField = "IndexNumber";
+                    //option.Header.HeaderFieldType = ReportFieldType.Int;
+                    break;
+
                 case HeaderMetadata.Network:
                     option.Column = (i, r) => this.GetListAsString(i.Studios);
                     option.ItemID = (i) => this.GetStudioID(i.Studios.FirstOrDefault());

+ 0 - 256
MediaBrowser.Api/Reports/Stat/ReportStatBuilder.cs

@@ -1,256 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace MediaBrowser.Api.Reports
-{
-    /// <summary> A report stat builder. </summary>
-    /// <seealso cref="T:MediaBrowser.Api.Reports.ReportBuilderBase"/>
-    public class ReportStatBuilder : ReportBuilderBase
-    {
-        #region [Constructors]
-
-        /// <summary>
-        /// Initializes a new instance of the MediaBrowser.Api.Reports.ReportStatBuilder class. </summary>
-        /// <param name="libraryManager"> Manager for library. </param>
-        public ReportStatBuilder(ILibraryManager libraryManager)
-            : base(libraryManager)
-        {
-        }
-
-        #endregion
-
-        #region [Public Methods]
-
-        /// <summary> Gets report stat result. </summary>
-        /// <param name="items"> The items. </param>
-        /// <param name="reportIncludeItemTypes"> List of types of the report include items. </param>
-        /// <param name="topItem"> The top item. </param>
-        /// <returns> The report stat result. </returns>
-        public ReportStatResult GetResult(BaseItem[] items, ReportIncludeItemTypes reportIncludeItemTypes, int topItem = 5)
-        {
-            ReportStatResult result = new ReportStatResult();
-            result = this.GetResultGenres(result, items, topItem);
-            result = this.GetResultStudios(result, items, topItem);
-            result = this.GetResultPersons(result, items, topItem);
-            result = this.GetResultProductionYears(result, items, topItem);
-            result = this.GetResultCommunityRatings(result, items, topItem);
-            result = this.GetResultParentalRatings(result, items, topItem);
-
-            switch (reportIncludeItemTypes)
-            {
-                case ReportIncludeItemTypes.Season:
-                case ReportIncludeItemTypes.Series:
-                case ReportIncludeItemTypes.MusicAlbum:
-                case ReportIncludeItemTypes.MusicArtist:
-                case ReportIncludeItemTypes.Game:
-                    break;
-                case ReportIncludeItemTypes.Movie:
-                case ReportIncludeItemTypes.BoxSet:
-
-                    break;
-                case ReportIncludeItemTypes.Book:
-                case ReportIncludeItemTypes.Episode:
-                case ReportIncludeItemTypes.Video:
-                case ReportIncludeItemTypes.MusicVideo:
-                case ReportIncludeItemTypes.Trailer:
-                case ReportIncludeItemTypes.Audio:
-                case ReportIncludeItemTypes.BaseItem:
-                default:
-                    break;
-            }
-
-            result.Groups = result.Groups.OrderByDescending(n => n.Items.Count()).ToList();
-
-            return result;
-        }
-
-        #endregion
-
-        #region [Protected Internal Methods]
-        /// <summary> Gets the headers. </summary>
-        /// <typeparam name="H"> Type of the header. </typeparam>
-        /// <param name="request"> The request. </param>
-        /// <returns> The headers. </returns>
-        /// <seealso cref="M:MediaBrowser.Api.Reports.ReportBuilderBase.GetHeaders{H}(H)"/>
-        protected internal override List<ReportHeader> GetHeaders<H>(H request)
-        {
-            throw new NotImplementedException();
-        }
-
-        #endregion
-
-        #region [Private Methods]
-
-        /// <summary> Gets the groups. </summary>
-        /// <param name="result"> The result. </param>
-        /// <param name="header"> The header. </param>
-        /// <param name="topItem"> The top item. </param>
-        /// <param name="top"> The top. </param>
-        private void GetGroups(ReportStatResult result, string header, int topItem, IEnumerable<ReportStatItem> top)
-        {
-            if (top != null && top.Count() > 0)
-            {
-                var group = new ReportStatGroup { Header = ReportStatGroup.FormatedHeader(header, topItem) };
-                group.Items.AddRange(top);
-                result.Groups.Add(group);
-            }
-        }
-
-        /// <summary> Gets result community ratings. </summary>
-        /// <param name="result"> The result. </param>
-        /// <param name="items"> The items. </param>
-        /// <param name="topItem"> The top item. </param>
-        /// <returns> The result community ratings. </returns>
-        private ReportStatResult GetResultCommunityRatings(ReportStatResult result, BaseItem[] items, int topItem = 5)
-        {
-            this.GetGroups(result, GetLocalizedHeader(HeaderMetadata.CommunityRating), topItem,
-                       items.Where(x => x.CommunityRating != null && x.CommunityRating > 0)
-                           .GroupBy(x => x.CommunityRating)
-                           .OrderByDescending(x => x.Count())
-                           .Take(topItem)
-                           .Select(x => new ReportStatItem
-                           {
-                               Name = x.Key.ToString(),
-                               Value = x.Count().ToString()
-                           })
-               );
-
-            return result;
-        }
-
-        /// <summary> Gets result genres. </summary>
-        /// <param name="result"> The result. </param>
-        /// <param name="items"> The items. </param>
-        /// <param name="topItem"> The top item. </param>
-        /// <returns> The result genres. </returns>
-        private ReportStatResult GetResultGenres(ReportStatResult result, BaseItem[] items, int topItem = 5)
-        {
-            this.GetGroups(result, GetLocalizedHeader(HeaderMetadata.Genres), topItem,
-                            items.SelectMany(x => x.Genres)
-                                .GroupBy(x => x)
-                                .OrderByDescending(x => x.Count())
-                                .Take(topItem)
-                                .Select(x => new ReportStatItem
-                                {
-                                    Name = x.Key,
-                                    Value = x.Count().ToString(),
-                                    Id = GetGenreID(x.Key)
-                                }));
-            return result;
-
-        }
-
-        /// <summary> Gets result parental ratings. </summary>
-        /// <param name="result"> The result. </param>
-        /// <param name="items"> The items. </param>
-        /// <param name="topItem"> The top item. </param>
-        /// <returns> The result parental ratings. </returns>
-        private ReportStatResult GetResultParentalRatings(ReportStatResult result, BaseItem[] items, int topItem = 5)
-        {
-            this.GetGroups(result, GetLocalizedHeader(HeaderMetadata.ParentalRatings), topItem,
-                       items.Where(x => x.OfficialRating != null)
-                           .GroupBy(x => x.OfficialRating)
-                           .OrderByDescending(x => x.Count())
-                           .Take(topItem)
-                           .Select(x => new ReportStatItem
-                           {
-                               Name = x.Key.ToString(),
-                               Value = x.Count().ToString()
-                           })
-               );
-
-            return result;
-        }
-
-        /// <summary> Gets result persons. </summary>
-        /// <param name="result"> The result. </param>
-        /// <param name="items"> The items. </param>
-        /// <param name="topItem"> The top item. </param>
-        /// <returns> The result persons. </returns>
-        private ReportStatResult GetResultPersons(ReportStatResult result, BaseItem[] items, int topItem = 5)
-        {
-            List<HeaderMetadata> t = new List<HeaderMetadata> 
-            { 
-                HeaderMetadata.Actor, 
-                HeaderMetadata.Composer, 
-                HeaderMetadata.Director, 
-                HeaderMetadata.GuestStar, 
-                HeaderMetadata.Producer,
-                HeaderMetadata.Writer, 
-                HeaderMetadata.Artist, 
-                HeaderMetadata.AlbumArtist
-            };
-            foreach (var item in t)
-            {
-                var ps = items.SelectMany(x => _libraryManager.GetPeople(x))
-                                .Where(n => n.Type == item.ToString())
-                                .GroupBy(x => x.Name)
-                                .OrderByDescending(x => x.Count())
-                                .Take(topItem);
-                if (ps != null && ps.Count() > 0)
-                    this.GetGroups(result, GetLocalizedHeader(item), topItem,
-                            ps.Select(x => new ReportStatItem
-                                    {
-                                        Name = x.Key,
-                                        Value = x.Count().ToString(),
-                                        Id = GetPersonID(x.Key)
-                                    })
-                    );
-            }
-
-            return result;
-        }
-
-        /// <summary> Gets result production years. </summary>
-        /// <param name="result"> The result. </param>
-        /// <param name="items"> The items. </param>
-        /// <param name="topItem"> The top item. </param>
-        /// <returns> The result production years. </returns>
-        private ReportStatResult GetResultProductionYears(ReportStatResult result, BaseItem[] items, int topItem = 5)
-        {
-            this.GetGroups(result, GetLocalizedHeader(HeaderMetadata.Year), topItem,
-                    items.Where(x => x.ProductionYear != null && x.ProductionYear > 0)
-                        .GroupBy(x => x.ProductionYear)
-                        .OrderByDescending(x => x.Count())
-                        .Take(topItem)
-                        .Select(x => new ReportStatItem
-                        {
-                            Name = x.Key.ToString(),
-                            Value = x.Count().ToString()
-                        })
-            );
-
-            return result;
-        }
-
-        /// <summary> Gets result studios. </summary>
-        /// <param name="result"> The result. </param>
-        /// <param name="items"> The items. </param>
-        /// <param name="topItem"> The top item. </param>
-        /// <returns> The result studios. </returns>
-        private ReportStatResult GetResultStudios(ReportStatResult result, BaseItem[] items, int topItem = 5)
-        {
-            this.GetGroups(result, GetLocalizedHeader(HeaderMetadata.Studios), topItem,
-                                    items.SelectMany(x => x.Studios)
-                                        .GroupBy(x => x)
-                                        .OrderByDescending(x => x.Count())
-                                        .Take(topItem)
-                                        .Select(x => new ReportStatItem
-                                        {
-                                            Name = x.Key,
-                                            Value = x.Count().ToString(),
-                                            Id = GetStudioID(x.Key)
-                                        })
-                    );
-
-            return result;
-
-        }
-
-        #endregion
-
-    }
-}

+ 0 - 33
MediaBrowser.Api/Reports/Stat/ReportStatGroup.cs

@@ -1,33 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Api.Reports
-{
-	/// <summary> A report stat group. </summary>
-	public class ReportStatGroup
-	{
-		/// <summary>
-		/// Initializes a new instance of the MediaBrowser.Api.Reports.ReportStatGroup class. </summary>
-		public ReportStatGroup()
-		{
-			Items = new List<ReportStatItem>();
-			TotalRecordCount = 0;
-		}
-
-		/// <summary> Gets or sets the header. </summary>
-		/// <value> The header. </value>
-		public string Header { get; set; }
-
-		/// <summary> Gets or sets the items. </summary>
-		/// <value> The items. </value>
-		public List<ReportStatItem> Items { get; set; }
-
-		/// <summary> Gets or sets the number of total records. </summary>
-		/// <value> The total number of record count. </value>
-		public int TotalRecordCount { get; set; }
-
-		internal static string FormatedHeader(string header, int topItem)
-		{
-			return string.Format("Top {0} {1}", topItem, header);
-		}
-	}
-}

+ 0 - 23
MediaBrowser.Api/Reports/Stat/ReportStatItem.cs

@@ -1,23 +0,0 @@
-namespace MediaBrowser.Api.Reports
-{
-	/// <summary> A report stat item. </summary>
-	public class ReportStatItem
-	{
-		/// <summary> Gets or sets the name. </summary>
-		/// <value> The name. </value>
-		public string Name { get; set; }
-
-		/// <summary> Gets or sets the image. </summary>
-		/// <value> The image. </value>
-		public string Image { get; set; }
-
-		/// <summary> Gets or sets the value. </summary>
-		/// <value> The value. </value>
-		public string Value { get; set; }
-
-		/// <summary> Gets or sets the identifier. </summary>
-		/// <value> The identifier. </value>
-		public string Id { get; set; }
-
-	}
-}

+ 0 - 24
MediaBrowser.Api/Reports/Stat/ReportStatResult.cs

@@ -1,24 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Api.Reports
-{
-	/// <summary> Encapsulates the result of a report stat. </summary>
-	public class ReportStatResult 
-	{
-		/// <summary>
-		/// Initializes a new instance of the MediaBrowser.Api.Reports.ReportStatResult class. </summary>
-		public ReportStatResult()
-		{
-			Groups = new List<ReportStatGroup>();
-			TotalRecordCount = 0;
-		}
-
-		/// <summary> Gets or sets the groups. </summary>
-		/// <value> The groups. </value>
-		public List<ReportStatGroup> Groups { get; set; }
-
-		/// <summary> Gets or sets the number of total records. </summary>
-		/// <value> The total number of record count. </value>
-		public int TotalRecordCount { get; set; }	
-	}
-}

+ 2 - 6
MediaBrowser.Controller/Drawing/IImageEncoder.cs

@@ -16,12 +16,6 @@ namespace MediaBrowser.Controller.Drawing
         /// <value>The supported output formats.</value>
         ImageFormat[] SupportedOutputFormats { get; }
         /// <summary>
-        /// Crops the white space.
-        /// </summary>
-        /// <param name="inputPath">The input path.</param>
-        /// <param name="outputPath">The output path.</param>
-        void CropWhiteSpace(string inputPath, string outputPath);
-        /// <summary>
         /// Encodes the image.
         /// </summary>
         /// <param name="inputPath">The input path.</param>
@@ -56,5 +50,7 @@ namespace MediaBrowser.Controller.Drawing
         /// </summary>
         /// <value><c>true</c> if [supports image encoding]; otherwise, <c>false</c>.</value>
         bool SupportsImageEncoding { get; }
+
+        ImageSize GetImageSize(string path);
     }
 }

+ 2 - 0
MediaBrowser.Controller/Drawing/IImageProcessor.cs

@@ -112,5 +112,7 @@ namespace MediaBrowser.Controller.Drawing
         /// </summary>
         /// <value><c>true</c> if [supports image collage creation]; otherwise, <c>false</c>.</value>
         bool SupportsImageCollageCreation { get; }
+
+        IImageEncoder ImageEncoder { get; set; }
     }
 }

Неке датотеке нису приказане због велике количине промена