ソースを参照

Merge pull request #1409 from MediaBrowser/beta

merge from beta
Luke 9 年 前
コミット
236b5caee2
100 ファイル変更1945 行追加994 行削除
  1. 2 2
      Emby.Drawing/Emby.Drawing.csproj
  2. 18 12
      Emby.Drawing/ImageMagick/ImageMagickEncoder.cs
  3. 2 2
      Emby.Drawing/packages.config
  4. 5 1
      MediaBrowser.Api/ItemLookupService.cs
  5. 46 16
      MediaBrowser.Api/Library/LibraryService.cs
  6. 4 4
      MediaBrowser.Api/MediaBrowser.Api.csproj
  7. 1 1
      MediaBrowser.Api/PackageService.cs
  8. 7 0
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  9. 2 1
      MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
  10. 1 0
      MediaBrowser.Api/SearchService.cs
  11. 14 8
      MediaBrowser.Api/System/SystemService.cs
  12. 2 2
      MediaBrowser.Api/packages.config
  13. 1 1
      MediaBrowser.Common.Implementations/BaseApplicationHost.cs
  14. 1 1
      MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
  15. 11 7
      MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj
  16. 89 58
      MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs
  17. 212 0
      MediaBrowser.Common.Implementations/Updates/GithubUpdater.cs
  18. 6 6
      MediaBrowser.Common.Implementations/Updates/InstallationManager.cs
  19. 4 3
      MediaBrowser.Common.Implementations/packages.config
  20. 8 1
      MediaBrowser.Common/Net/INetworkManager.cs
  21. 2 1
      MediaBrowser.Common/Updates/IInstallationManager.cs
  22. 8 2
      MediaBrowser.Controller/Dto/IDtoService.cs
  23. 3 1
      MediaBrowser.Controller/IServerApplicationHost.cs
  24. 4 4
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  25. 0 6
      MediaBrowser.Controller/Net/IHttpServer.cs
  26. 2 2
      MediaBrowser.Controller/packages.config
  27. 25 18
      MediaBrowser.Dlna/Main/DlnaEntryPoint.cs
  28. 4 1
      MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
  29. 0 1
      MediaBrowser.Dlna/Profiles/DefaultProfile.cs
  30. 1 3
      MediaBrowser.Dlna/Profiles/Xbox360Profile.cs
  31. 1 1
      MediaBrowser.Dlna/Profiles/XboxOneProfile.cs
  32. 0 1
      MediaBrowser.Dlna/Profiles/Xml/BubbleUPnp.xml
  33. 0 1
      MediaBrowser.Dlna/Profiles/Xml/Default.xml
  34. 0 1
      MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml
  35. 0 1
      MediaBrowser.Dlna/Profiles/Xml/DirecTV HD-DVR.xml
  36. 0 1
      MediaBrowser.Dlna/Profiles/Xml/Dish Hopper-Joey.xml
  37. 0 1
      MediaBrowser.Dlna/Profiles/Xml/Kodi.xml
  38. 0 1
      MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml
  39. 0 1
      MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml
  40. 0 1
      MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml
  41. 0 1
      MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml
  42. 0 1
      MediaBrowser.Dlna/Profiles/Xml/Popcorn Hour.xml
  43. 0 1
      MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml
  44. 0 1
      MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml
  45. 0 1
      MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml
  46. 0 1
      MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml
  47. 0 1
      MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml
  48. 0 1
      MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml
  49. 0 1
      MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml
  50. 0 1
      MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2014).xml
  51. 0 1
      MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml
  52. 0 1
      MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 4.xml
  53. 0 1
      MediaBrowser.Dlna/Profiles/Xml/Vlc.xml
  54. 0 1
      MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml
  55. 2 3
      MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml
  56. 1 2
      MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml
  57. 0 1
      MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml
  58. 8 17
      MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs
  59. 29 47
      MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs
  60. 14 1
      MediaBrowser.Dlna/Ssdp/SsdpHandler.cs
  61. 2 1
      MediaBrowser.Dlna/packages.config
  62. 1 1
      MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
  63. 1 1
      MediaBrowser.LocalMetadata/packages.config
  64. 1 1
      MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
  65. 1 1
      MediaBrowser.MediaEncoding/packages.config
  66. 0 1
      MediaBrowser.Model/Dlna/DeviceProfile.cs
  67. 6 0
      MediaBrowser.Model/Search/SearchHint.cs
  68. 8 5
      MediaBrowser.Providers/Manager/ImageSaver.cs
  69. 0 16
      MediaBrowser.Providers/Manager/ProviderManager.cs
  70. 3 4
      MediaBrowser.Providers/MediaBrowser.Providers.csproj
  71. 8 1
      MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
  72. 3 13
      MediaBrowser.Providers/Omdb/OmdbImageProvider.cs
  73. 16 4
      MediaBrowser.Providers/Omdb/OmdbItemProvider.cs
  74. 17 26
      MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs
  75. 2 0
      MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs
  76. 36 21
      MediaBrowser.Providers/TV/TvdbSeriesProvider.cs
  77. 2 2
      MediaBrowser.Providers/packages.config
  78. 11 6
      MediaBrowser.Server.Implementations/Dto/DtoService.cs
  79. 123 49
      MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
  80. 4 43
      MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs
  81. 4 1
      MediaBrowser.Server.Implementations/HttpServer/ResponseFilter.cs
  82. 4 1
      MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs
  83. 941 0
      MediaBrowser.Server.Implementations/HttpServer/SocketSharp/HttpUtility.cs
  84. 2 1
      MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs
  85. 1 4
      MediaBrowser.Server.Implementations/Library/UserManager.cs
  86. 0 59
      MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/EmbyListings.cs
  87. 0 366
      MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/EmbyListingsNorthAmerica.cs
  88. 0 18
      MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/IEmbyListingProvider.cs
  89. 53 23
      MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
  90. 1 4
      MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs
  91. 54 0
      MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp.cs
  92. 2 2
      MediaBrowser.Server.Implementations/Localization/Core/ca.json
  93. 8 9
      MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
  94. 1 1
      MediaBrowser.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs
  95. 3 3
      MediaBrowser.Server.Implementations/packages.config
  96. 1 1
      MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
  97. 1 1
      MediaBrowser.Server.Mono/Program.cs
  98. 1 1
      MediaBrowser.Server.Mono/packages.config
  99. 82 42
      MediaBrowser.Server.Startup.Common/ApplicationHost.cs
  100. 1 1
      MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj

+ 2 - 2
Emby.Drawing/Emby.Drawing.csproj

@@ -33,11 +33,11 @@
   <ItemGroup>
     <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
+      <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath>
     </Reference>
     <Reference Include="ImageMagickSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\ImageMagickSharp.1.0.0.16\lib\net45\ImageMagickSharp.dll</HintPath>
+      <HintPath>..\packages\ImageMagickSharp.1.0.0.17\lib\net45\ImageMagickSharp.dll</HintPath>
     </Reference>
     <Reference Include="Patterns.Logging">
       <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>

+ 18 - 12
Emby.Drawing/ImageMagick/ImageMagickEncoder.cs

@@ -72,11 +72,16 @@ namespace Emby.Drawing.ImageMagick
 
         private void LogVersion()
         {
-            _logger.Info("ImageMagick version: " + Wand.VersionString);
+            _logger.Info("ImageMagick version: " + GetVersion());
             TestWebp();
             Wand.SetMagickThreadCount(1);
         }
 
+        public static string GetVersion()
+        {
+            return Wand.VersionString;
+        }
+
         private bool _webpAvailable = true;
         private void TestWebp()
         {
@@ -148,7 +153,7 @@ namespace Emby.Drawing.ImageMagick
                     DrawIndicator(originalImage, width, height, options);
 
                     originalImage.CurrentImage.CompressionQuality = quality;
-                    //originalImage.CurrentImage.StripImage();
+                    originalImage.CurrentImage.StripImage();
 
                     originalImage.SaveImage(outputPath);
                 }
@@ -165,7 +170,7 @@ namespace Emby.Drawing.ImageMagick
                         DrawIndicator(wand, width, height, options);
 
                         wand.CurrentImage.CompressionQuality = quality;
-                        //wand.CurrentImage.StripImage();
+                        wand.CurrentImage.StripImage();
 
                         wand.SaveImage(outputPath);
                     }
@@ -176,15 +181,16 @@ namespace Emby.Drawing.ImageMagick
 
         private void ScaleImage(MagickWand wand, int width, int height)
         {
-            wand.CurrentImage.ResizeImage(width, height);
-            //if (_config.Configuration.EnableHighQualityImageScaling)
-            //{
-            //    wand.CurrentImage.ResizeImage(width, height);
-            //}
-            //else
-            //{
-            //    wand.CurrentImage.ScaleImage(width, height);
-            //}
+            var highQuality = false;
+
+            if (highQuality)
+            {
+                wand.CurrentImage.ResizeImage(width, height);
+            }
+            else
+            {
+                wand.CurrentImage.ScaleImage(width, height);
+            }
         }
 
         /// <summary>

+ 2 - 2
Emby.Drawing/packages.config

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
-  <package id="ImageMagickSharp" version="1.0.0.16" targetFramework="net45" />
+  <package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
+  <package id="ImageMagickSharp" version="1.0.0.17" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
 </packages>

+ 5 - 1
MediaBrowser.Api/ItemLookupService.cs

@@ -17,6 +17,7 @@ using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 using CommonIO;
+using MediaBrowser.Model.Serialization;
 
 namespace MediaBrowser.Api
 {
@@ -112,13 +113,15 @@ namespace MediaBrowser.Api
         private readonly IServerApplicationPaths _appPaths;
         private readonly IFileSystem _fileSystem;
         private readonly ILibraryManager _libraryManager;
+        private readonly IJsonSerializer _json;
 
-        public ItemLookupService(IProviderManager providerManager, IServerApplicationPaths appPaths, IFileSystem fileSystem, ILibraryManager libraryManager)
+        public ItemLookupService(IProviderManager providerManager, IServerApplicationPaths appPaths, IFileSystem fileSystem, ILibraryManager libraryManager, IJsonSerializer json)
         {
             _providerManager = providerManager;
             _appPaths = appPaths;
             _fileSystem = fileSystem;
             _libraryManager = libraryManager;
+            _json = json;
         }
 
         public object Get(GetExternalIdInfos request)
@@ -199,6 +202,7 @@ namespace MediaBrowser.Api
             //        item.SetProviderId(key.Key, value);
             //    }
             //}
+            Logger.Info("Setting provider id's to item {0}-{1}: {2}", item.Id, item.Name, _json.SerializeToString(request.ProviderIds));
             item.ProviderIds = request.ProviderIds;
 
 			var task = _providerManager.RefreshFullItem(item, new MetadataRefreshOptions(_fileSystem)

+ 46 - 16
MediaBrowser.Api/Library/LibraryService.cs

@@ -162,6 +162,14 @@ namespace MediaBrowser.Api.Library
         public string Id { get; set; }
     }
 
+    [Route("/Items", "DELETE", Summary = "Deletes an item from the library and file system")]
+    [Authenticated]
+    public class DeleteItems : IReturnVoid
+    {
+        [ApiMember(Name = "Ids", Description = "Ids", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
+        public string Ids { get; set; }
+    }
+
     [Route("/Items/Counts", "GET")]
     [Authenticated]
     public class GetItemCounts : IReturn<ItemCounts>
@@ -715,27 +723,49 @@ namespace MediaBrowser.Api.Library
         /// Deletes the specified request.
         /// </summary>
         /// <param name="request">The request.</param>
-        public void Delete(DeleteItem request)
+        public void Delete(DeleteItems request)
         {
-            var item = _libraryManager.GetItemById(request.Id);
-            var auth = _authContext.GetAuthorizationInfo(Request);
-            var user = _userManager.GetUserById(auth.UserId);
+            var ids = string.IsNullOrWhiteSpace(request.Ids)
+             ? new string[] { }
+             : request.Ids.Split(',');
 
-            if (!item.CanDelete(user))
+            var tasks = ids.Select(i =>
             {
-                throw new SecurityException("Unauthorized access");
-            }
+                var item = _libraryManager.GetItemById(i);
+                var auth = _authContext.GetAuthorizationInfo(Request);
+                var user = _userManager.GetUserById(auth.UserId);
 
-            if (item is ILiveTvRecording)
-            {
-                var task = _liveTv.DeleteRecording(request.Id);
-                Task.WaitAll(task);
-            }
-            else
+                if (!item.CanDelete(user))
+                {
+                    if (ids.Length > 1)
+                    {
+                        throw new SecurityException("Unauthorized access");
+                    }
+
+                    return Task.FromResult(true);
+                }
+
+                if (item is ILiveTvRecording)
+                {
+                    return _liveTv.DeleteRecording(i);
+                }
+
+                return _libraryManager.DeleteItem(item);
+            }).ToArray();
+
+            Task.WaitAll(tasks);
+        }
+
+        /// <summary>
+        /// Deletes the specified request.
+        /// </summary>
+        /// <param name="request">The request.</param>
+        public void Delete(DeleteItem request)
+        {
+            Delete(new DeleteItems
             {
-                var task = _libraryManager.DeleteItem(item);
-                Task.WaitAll(task);
-            }
+                Ids = request.Id
+            });
         }
 
         /// <summary>

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

@@ -47,7 +47,10 @@
   <ItemGroup>
     <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
+      <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath>
+    </Reference>
+    <Reference Include="MoreLinq">
+      <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
     </Reference>
     <Reference Include="Patterns.Logging">
       <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
@@ -63,9 +66,6 @@
     <Reference Include="ServiceStack.Text">
       <HintPath>..\ThirdParty\ServiceStack.Text\ServiceStack.Text.dll</HintPath>
     </Reference>
-    <Reference Include="MoreLinq">
-      <HintPath>..\packages\morelinq.1.1.1\lib\net35\MoreLinq.dll</HintPath>
-    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\SharedVersion.cs">

+ 1 - 1
MediaBrowser.Api/PackageService.cs

@@ -233,7 +233,7 @@ namespace MediaBrowser.Api
                 throw new ResourceNotFoundException(string.Format("Package not found: {0}", request.Name));
             }
 
-            Task.Run(() => _installationManager.InstallPackage(package, new Progress<double>(), CancellationToken.None));
+            Task.Run(() => _installationManager.InstallPackage(package, true, new Progress<double>(), CancellationToken.None));
         }
 
         /// <summary>

+ 7 - 0
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -1562,6 +1562,13 @@ namespace MediaBrowser.Api.Playback
                 RequestedUrl = url
             };
 
+            //if ((Request.UserAgent ?? string.Empty).IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 ||
+            //    (Request.UserAgent ?? string.Empty).IndexOf("ipad", StringComparison.OrdinalIgnoreCase) != -1 ||
+            //    (Request.UserAgent ?? string.Empty).IndexOf("ipod", StringComparison.OrdinalIgnoreCase) != -1)
+            //{
+            //    state.SegmentLength = 6;
+            //}
+
             if (!string.IsNullOrWhiteSpace(request.AudioCodec))
             {
                 state.SupportedAudioCodecs = request.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();

+ 2 - 1
MediaBrowser.Api/Playback/Hls/BaseHlsService.cs

@@ -111,7 +111,8 @@ namespace MediaBrowser.Api.Playback.Hls
                             throw;
                         }
 
-                        await WaitForMinimumSegmentCount(playlist, 3, cancellationTokenSource.Token).ConfigureAwait(false);
+                        var waitForSegments = state.SegmentLength >= 10 ? 2 : 3;
+                        await WaitForMinimumSegmentCount(playlist, waitForSegments, cancellationTokenSource.Token).ConfigureAwait(false);
                     }
                 }
                 finally

+ 1 - 0
MediaBrowser.Api/SearchService.cs

@@ -179,6 +179,7 @@ namespace MediaBrowser.Api
             if (primaryImageTag != null)
             {
                 result.PrimaryImageTag = primaryImageTag;
+                result.PrimaryImageAspectRatio = _dtoService.GetPrimaryImageAspectRatio(item);
             }
 
             SetThumbImageInfo(result, item);

+ 14 - 8
MediaBrowser.Api/System/SystemService.cs

@@ -13,6 +13,7 @@ using System.IO;
 using System.Linq;
 using System.Threading.Tasks;
 using CommonIO;
+using MediaBrowser.Model.Net;
 
 namespace MediaBrowser.Api.System
 {
@@ -32,6 +33,12 @@ namespace MediaBrowser.Api.System
 
     }
 
+    [Route("/System/Ping", "POST")]
+    public class PingSystem : IReturnVoid
+    {
+
+    }
+
     /// <summary>
     /// Class RestartApplication
     /// </summary>
@@ -59,7 +66,7 @@ namespace MediaBrowser.Api.System
 
     [Route("/System/Endpoint", "GET", Summary = "Gets information about the request endpoint")]
     [Authenticated]
-    public class GetEndpointInfo : IReturn<EndpointInfo>
+    public class GetEndpointInfo : IReturn<EndPointInfo>
     {
         public string Endpoint { get; set; }
     }
@@ -104,6 +111,11 @@ namespace MediaBrowser.Api.System
             _security = security;
         }
 
+        public object Post(PingSystem request)
+        {
+            return _appHost.Name;
+        }
+
         public object Get(GetServerLogs request)
         {
             List<FileSystemMetadata> files;
@@ -199,17 +211,11 @@ namespace MediaBrowser.Api.System
 
         public object Get(GetEndpointInfo request)
         {
-            return ToOptimizedResult(new EndpointInfo
+            return ToOptimizedResult(new EndPointInfo
             {
                 IsLocal = Request.IsLocal,
                 IsInNetwork = _network.IsInLocalNetwork(request.Endpoint ?? Request.RemoteIp)
             });
         }
     }
-
-    public class EndpointInfo
-    {
-        public bool IsLocal { get; set; }
-        public bool IsInNetwork { get; set; }
-    }
 }

+ 2 - 2
MediaBrowser.Api/packages.config

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
-  <package id="morelinq" version="1.1.1" targetFramework="net45" />
+  <package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
+  <package id="morelinq" version="1.4.0" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
 </packages>

+ 1 - 1
MediaBrowser.Common.Implementations/BaseApplicationHost.cs

@@ -133,7 +133,7 @@ namespace MediaBrowser.Common.Implementations
         /// Gets the HTTP client.
         /// </summary>
         /// <value>The HTTP client.</value>
-        protected IHttpClient HttpClient { get; private set; }
+        public IHttpClient HttpClient { get; private set; }
         /// <summary>
         /// Gets the network manager.
         /// </summary>

+ 1 - 1
MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs

@@ -150,7 +150,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
 
             request.Method = method;
             request.Timeout = options.TimeoutMs;
-
+            
             if (httpWebRequest != null)
             {
                 if (!string.IsNullOrEmpty(options.Host))

+ 11 - 7
MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj

@@ -49,11 +49,13 @@
   <ItemGroup>
     <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
+      <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath>
     </Reference>
-    <Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\NLog.4.1.1\lib\net45\NLog.dll</HintPath>
+    <Reference Include="MoreLinq">
+      <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
+    </Reference>
+    <Reference Include="NLog">
+      <HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.dll</HintPath>
     </Reference>
     <Reference Include="Patterns.Logging">
       <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
@@ -62,13 +64,14 @@
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\ThirdParty\SharpCompress\SharpCompress.dll</HintPath>
     </Reference>
-    <Reference Include="SimpleInjector, Version=2.8.0.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\SimpleInjector.3.0.5\lib\net45\SimpleInjector.dll</HintPath>
+    <Reference Include="SimpleInjector">
+      <HintPath>..\packages\SimpleInjector.3.1.2\lib\net45\SimpleInjector.dll</HintPath>
     </Reference>
     <Reference Include="System" />
+    <Reference Include="System.Configuration" />
     <Reference Include="System.Core" />
     <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
     <Reference Include="System.Net" />
     <Reference Include="System.Xml" />
     <Reference Include="ServiceStack.Text">
@@ -105,6 +108,7 @@
     <Compile Include="Security\SuppporterInfoResponse.cs" />
     <Compile Include="Serialization\JsonSerializer.cs" />
     <Compile Include="Serialization\XmlSerializer.cs" />
+    <Compile Include="Updates\GithubUpdater.cs" />
     <Compile Include="Updates\InstallationManager.cs" />
   </ItemGroup>
   <ItemGroup>

+ 89 - 58
MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs

@@ -7,6 +7,7 @@ using System.Net;
 using System.Net.NetworkInformation;
 using System.Net.Sockets;
 using System.Threading;
+using MoreLinq;
 
 namespace MediaBrowser.Common.Implementations.Networking
 {
@@ -31,14 +32,14 @@ namespace MediaBrowser.Common.Implementations.Networking
             }
         }
 
-        private volatile List<string> _localIpAddresses;
+		private volatile List<IPAddress> _localIpAddresses;
         private readonly object _localIpAddressSyncLock = new object();
 
         /// <summary>
         /// Gets the machine's local ip address
         /// </summary>
         /// <returns>IPAddress.</returns>
-        public IEnumerable<string> GetLocalIpAddresses()
+		public IEnumerable<IPAddress> GetLocalIpAddresses()
         {
             if (_localIpAddresses == null)
             {
@@ -58,25 +59,24 @@ namespace MediaBrowser.Common.Implementations.Networking
             return _localIpAddresses;
         }
 
-        private IEnumerable<string> GetLocalIpAddressesInternal()
+		private IEnumerable<IPAddress> GetLocalIpAddressesInternal()
         {
             var list = GetIPsDefault()
-                .Where(i => !IPAddress.IsLoopback(i))
-                .Select(i => i.ToString())
-                .Where(FilterIpAddress)
                 .ToList();
 
-            if (list.Count > 0)
+            if (list.Count == 0)
             {
-                return list;
+				list.AddRange(GetLocalIpAddressesFallback());
             }
 
-            return GetLocalIpAddressesFallback().Where(FilterIpAddress);
+			return list.Where(FilterIpAddress).DistinctBy(i => i.ToString());
         }
 
-        private bool FilterIpAddress(string address)
+		private bool FilterIpAddress(IPAddress address)
         {
-            if (address.StartsWith("169.", StringComparison.OrdinalIgnoreCase))
+			var addressString = address.ToString ();
+
+			if (addressString.StartsWith("169.", StringComparison.OrdinalIgnoreCase))
             {
                 return false;
             }
@@ -84,8 +84,16 @@ namespace MediaBrowser.Common.Implementations.Networking
             return true;
         }
 
-        private bool IsInPrivateAddressSpace(string endpoint)
+        public bool IsInPrivateAddressSpace(string endpoint)
         {
+            if (string.Equals(endpoint, "::1", StringComparison.OrdinalIgnoreCase))
+            {
+                return true;
+            }
+
+            // Handle ipv4 mapped to ipv6
+            endpoint = endpoint.Replace("::ffff:", string.Empty);
+
             // Private address space:
             // http://en.wikipedia.org/wiki/Private_network
 
@@ -96,9 +104,6 @@ namespace MediaBrowser.Common.Implementations.Networking
 
             return
 
-                // If url was requested with computer name, we may see this
-                endpoint.IndexOf("::", StringComparison.OrdinalIgnoreCase) != -1 ||
-
                 endpoint.StartsWith("localhost", StringComparison.OrdinalIgnoreCase) ||
                 endpoint.StartsWith("127.", StringComparison.OrdinalIgnoreCase) ||
                 endpoint.StartsWith("10.", StringComparison.OrdinalIgnoreCase) ||
@@ -131,26 +136,41 @@ namespace MediaBrowser.Common.Implementations.Networking
                 throw new ArgumentNullException("endpoint");
             }
 
-            if (IsInPrivateAddressSpace(endpoint))
-            {
-                return true;
-            }
-
-            const int lengthMatch = 4;
-
-            if (endpoint.Length >= lengthMatch)
+            IPAddress address;
+            if (IPAddress.TryParse(endpoint, out address))
             {
-                var prefix = endpoint.Substring(0, lengthMatch);
+                var addressString = address.ToString();
 
-                if (GetLocalIpAddresses()
-                    .Any(i => i.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)))
+                int lengthMatch = 100;
+                if (address.AddressFamily == AddressFamily.InterNetwork)
                 {
-                    return true;
+                    lengthMatch = 4;
+                    if (IsInPrivateAddressSpace(addressString))
+                    {
+                        return true;
+                    }
+                }
+                else if (address.AddressFamily == AddressFamily.InterNetworkV6)
+                {
+                    lengthMatch = 10;
+                    if (IsInPrivateAddressSpace(endpoint))
+                    {
+                        return true;
+                    }
                 }
-            }
 
-            IPAddress address;
-            if (resolveHost && !IPAddress.TryParse(endpoint, out address))
+                // Should be even be doing this with ipv6?
+                if (addressString.Length >= lengthMatch)
+                {
+                    var prefix = addressString.Substring(0, lengthMatch);
+
+					if (GetLocalIpAddresses().Any(i => i.ToString().StartsWith(prefix, StringComparison.OrdinalIgnoreCase)))
+                    {
+                        return true;
+                    }
+                }
+            } 
+            else if (resolveHost)
             {
                 Uri uri;
                 if (Uri.TryCreate(endpoint, UriKind.RelativeOrAbsolute, out uri))
@@ -188,33 +208,45 @@ namespace MediaBrowser.Common.Implementations.Networking
             return Dns.GetHostAddresses(hostName);
         }
 
-        private IEnumerable<IPAddress> GetIPsDefault()
-        {
-            foreach (var adapter in NetworkInterface.GetAllNetworkInterfaces())
-            {
-                var props = adapter.GetIPProperties();
-                var gateways = from ga in props.GatewayAddresses
-                               where !ga.Address.Equals(IPAddress.Any)
-                               select true;
-
-                if (!gateways.Any())
-                {
-                    continue;
-                }
-
-                foreach (var uni in props.UnicastAddresses)
-                {
-                    var address = uni.Address;
-                    if (address.AddressFamily != AddressFamily.InterNetwork)
-                    {
-                        continue;
-                    }
-                    yield return address;
-                }
-            }
-        }
-
-        private IEnumerable<string> GetLocalIpAddressesFallback()
+		private List<IPAddress> GetIPsDefault()
+		{
+			NetworkInterface[] interfaces;
+
+			try
+			{
+				interfaces = NetworkInterface.GetAllNetworkInterfaces();
+			}
+			catch (Exception ex)
+			{
+				Logger.ErrorException("Error in GetAllNetworkInterfaces", ex);
+				return new List<IPAddress>();
+			}
+
+			return interfaces.SelectMany(network => {
+
+				try
+				{
+                    Logger.Debug("Querying interface: {0}. Type: {1}. Status: {2}", network.Name, network.NetworkInterfaceType, network.OperationalStatus);
+
+					var properties = network.GetIPProperties();
+
+					return properties.UnicastAddresses
+                        .Where(i => i.IsDnsEligible)
+                        .Select(i => i.Address)
+                        .Where(i => i.AddressFamily == AddressFamily.InterNetwork)
+						.ToList();
+				}
+				catch (Exception ex)
+				{
+					Logger.ErrorException("Error querying network interface", ex);
+					return new List<IPAddress>();
+				}
+
+			}).DistinctBy(i => i.ToString())
+				.ToList();
+		}
+
+		private IEnumerable<IPAddress> GetLocalIpAddressesFallback()
         {
             var host = Dns.GetHostEntry(Dns.GetHostName());
 
@@ -222,7 +254,6 @@ namespace MediaBrowser.Common.Implementations.Networking
             // It's not fool-proof so ultimately the consumer will have to examine them and decide
             return host.AddressList
                 .Where(i => i.AddressFamily == AddressFamily.InterNetwork)
-                .Select(i => i.ToString())
                 .Reverse();
         }
 

+ 212 - 0
MediaBrowser.Common.Implementations/Updates/GithubUpdater.cs

@@ -0,0 +1,212 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Updates;
+
+namespace MediaBrowser.Common.Implementations.Updates
+{
+    public class GithubUpdater
+    {
+        private readonly IHttpClient _httpClient;
+        private readonly IJsonSerializer _jsonSerializer;
+        private TimeSpan _cacheLength;
+
+        public GithubUpdater(IHttpClient httpClient, IJsonSerializer jsonSerializer, TimeSpan cacheLength)
+        {
+            _httpClient = httpClient;
+            _jsonSerializer = jsonSerializer;
+            _cacheLength = cacheLength;
+        }
+
+        public async Task<CheckForUpdateResult> CheckForUpdateResult(string organzation, string repository, Version minVersion, PackageVersionClass updateLevel, string assetFilename, string packageName, string targetFilename, CancellationToken cancellationToken)
+        {
+            var url = string.Format("https://api.github.com/repos/{0}/{1}/releases", organzation, repository);
+
+            var options = new HttpRequestOptions
+            {
+                Url = url,
+                EnableKeepAlive = false,
+                CancellationToken = cancellationToken,
+                UserAgent = "Emby/3.0"
+
+            };
+
+            if (_cacheLength.Ticks > 0)
+            {
+                options.CacheMode = CacheMode.Unconditional;
+                options.CacheLength = _cacheLength;
+            }
+
+            using (var stream = await _httpClient.Get(options).ConfigureAwait(false))
+            {
+                var obj = _jsonSerializer.DeserializeFromStream<RootObject[]>(stream);
+
+                return CheckForUpdateResult(obj, minVersion, updateLevel, assetFilename, packageName, targetFilename);
+            }
+        }
+
+        private CheckForUpdateResult CheckForUpdateResult(RootObject[] obj, Version minVersion, PackageVersionClass updateLevel, string assetFilename, string packageName, string targetFilename)
+        {
+            if (updateLevel == PackageVersionClass.Release)
+            {
+                obj = obj.Where(i => !i.prerelease).ToArray();
+            }
+            else if (updateLevel == PackageVersionClass.Beta)
+            {
+                obj = obj.Where(i => !i.prerelease || i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase)).ToArray();
+            }
+            else if (updateLevel == PackageVersionClass.Dev)
+            {
+                obj = obj.Where(i => !i.prerelease || i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase) || i.name.EndsWith("-dev", StringComparison.OrdinalIgnoreCase)).ToArray();
+            }
+
+            var availableUpdate = obj
+                .Select(i => CheckForUpdateResult(i, minVersion, assetFilename, packageName, targetFilename))
+                .Where(i => i != null)
+                .OrderByDescending(i => Version.Parse(i.AvailableVersion))
+                .FirstOrDefault();
+            
+            return availableUpdate ?? new CheckForUpdateResult
+            {
+                IsUpdateAvailable = false
+            };
+        }
+
+        private CheckForUpdateResult CheckForUpdateResult(RootObject obj, Version minVersion, string assetFilename, string packageName, string targetFilename)
+        {
+            Version version;
+            if (!Version.TryParse(obj.tag_name, out version))
+            {
+                return null;
+            }
+
+            if (version < minVersion)
+            {
+                return null;
+            }
+
+            var asset = (obj.assets ?? new List<Asset>()).FirstOrDefault(i => IsAsset(i, assetFilename));
+
+            if (asset == null)
+            {
+                return null;
+            }
+
+            return new CheckForUpdateResult
+            {
+                AvailableVersion = version.ToString(),
+                IsUpdateAvailable = version > minVersion,
+                Package = new PackageVersionInfo
+                {
+                    classification = obj.prerelease ?
+                        (obj.name.EndsWith("-dev", StringComparison.OrdinalIgnoreCase) ? PackageVersionClass.Dev : PackageVersionClass.Beta) :
+                        PackageVersionClass.Release,
+                    name = packageName,
+                    sourceUrl = asset.browser_download_url,
+                    targetFilename = targetFilename,
+                    versionStr = version.ToString(),
+                    requiredVersionStr = "1.0.0",
+                    description = obj.body
+                }
+            };
+        }
+
+        private bool IsAsset(Asset asset, string assetFilename)
+        {
+            var downloadFilename = Path.GetFileName(asset.browser_download_url) ?? string.Empty;
+
+            if (downloadFilename.IndexOf(assetFilename, StringComparison.OrdinalIgnoreCase) != -1)
+            {
+                return true;
+            }
+
+            return string.Equals(assetFilename, downloadFilename, StringComparison.OrdinalIgnoreCase);
+        }
+
+        public class Uploader
+        {
+            public string login { get; set; }
+            public int id { get; set; }
+            public string avatar_url { get; set; }
+            public string gravatar_id { get; set; }
+            public string url { get; set; }
+            public string html_url { get; set; }
+            public string followers_url { get; set; }
+            public string following_url { get; set; }
+            public string gists_url { get; set; }
+            public string starred_url { get; set; }
+            public string subscriptions_url { get; set; }
+            public string organizations_url { get; set; }
+            public string repos_url { get; set; }
+            public string events_url { get; set; }
+            public string received_events_url { get; set; }
+            public string type { get; set; }
+            public bool site_admin { get; set; }
+        }
+
+        public class Asset
+        {
+            public string url { get; set; }
+            public int id { get; set; }
+            public string name { get; set; }
+            public object label { get; set; }
+            public Uploader uploader { get; set; }
+            public string content_type { get; set; }
+            public string state { get; set; }
+            public int size { get; set; }
+            public int download_count { get; set; }
+            public string created_at { get; set; }
+            public string updated_at { get; set; }
+            public string browser_download_url { get; set; }
+        }
+
+        public class Author
+        {
+            public string login { get; set; }
+            public int id { get; set; }
+            public string avatar_url { get; set; }
+            public string gravatar_id { get; set; }
+            public string url { get; set; }
+            public string html_url { get; set; }
+            public string followers_url { get; set; }
+            public string following_url { get; set; }
+            public string gists_url { get; set; }
+            public string starred_url { get; set; }
+            public string subscriptions_url { get; set; }
+            public string organizations_url { get; set; }
+            public string repos_url { get; set; }
+            public string events_url { get; set; }
+            public string received_events_url { get; set; }
+            public string type { get; set; }
+            public bool site_admin { get; set; }
+        }
+
+        public class RootObject
+        {
+            public string url { get; set; }
+            public string assets_url { get; set; }
+            public string upload_url { get; set; }
+            public string html_url { get; set; }
+            public int id { get; set; }
+            public string tag_name { get; set; }
+            public string target_commitish { get; set; }
+            public string name { get; set; }
+            public bool draft { get; set; }
+            public Author author { get; set; }
+            public bool prerelease { get; set; }
+            public string created_at { get; set; }
+            public string published_at { get; set; }
+            public List<Asset> assets { get; set; }
+            public string tarball_url { get; set; }
+            public string zipball_url { get; set; }
+            public string body { get; set; }
+        }
+    }
+}

+ 6 - 6
MediaBrowser.Common.Implementations/Updates/InstallationManager.cs

@@ -437,11 +437,12 @@ namespace MediaBrowser.Common.Implementations.Updates
         /// Installs the package.
         /// </summary>
         /// <param name="package">The package.</param>
+        /// <param name="isPlugin">if set to <c>true</c> [is plugin].</param>
         /// <param name="progress">The progress.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentNullException">package</exception>
-        public async Task InstallPackage(PackageVersionInfo package, IProgress<double> progress, CancellationToken cancellationToken)
+        public async Task InstallPackage(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken)
         {
             if (package == null)
             {
@@ -494,7 +495,7 @@ namespace MediaBrowser.Common.Implementations.Updates
 
             try
             {
-                await InstallPackageInternal(package, innerProgress, linkedToken).ConfigureAwait(false);
+                await InstallPackageInternal(package, isPlugin, innerProgress, linkedToken).ConfigureAwait(false);
 
                 lock (CurrentInstallations)
                 {
@@ -550,18 +551,17 @@ namespace MediaBrowser.Common.Implementations.Updates
         /// Installs the package internal.
         /// </summary>
         /// <param name="package">The package.</param>
+        /// <param name="isPlugin">if set to <c>true</c> [is plugin].</param>
         /// <param name="progress">The progress.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        private async Task InstallPackageInternal(PackageVersionInfo package, IProgress<double> progress, CancellationToken cancellationToken)
+        private async Task InstallPackageInternal(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken)
         {
             // Do the install
             await PerformPackageInstallation(progress, package, cancellationToken).ConfigureAwait(false);
 
-            var extension = Path.GetExtension(package.targetFilename) ?? "";
-
             // Do plugin-specific processing
-            if (!string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase) && !string.Equals(extension, ".rar", StringComparison.OrdinalIgnoreCase) && !string.Equals(extension, ".7z", StringComparison.OrdinalIgnoreCase))
+            if (isPlugin)
             {
                 // Set last update time if we were installed before
                 var plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase))

+ 4 - 3
MediaBrowser.Common.Implementations/packages.config

@@ -1,7 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
-  <package id="NLog" version="4.1.0" targetFramework="net45" />
+  <package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
+  <package id="morelinq" version="1.4.0" targetFramework="net45" />
+  <package id="NLog" version="4.2.3" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
-  <package id="SimpleInjector" version="3.0.5" targetFramework="net45" />
+  <package id="SimpleInjector" version="3.1.2" targetFramework="net45" />
 </packages>

+ 8 - 1
MediaBrowser.Common/Net/INetworkManager.cs

@@ -11,7 +11,7 @@ namespace MediaBrowser.Common.Net
         /// Gets the machine's local ip address
         /// </summary>
         /// <returns>IPAddress.</returns>
-        IEnumerable<string> GetLocalIpAddresses();
+		IEnumerable<IPAddress> GetLocalIpAddresses();
 
         /// <summary>
         /// Gets a random port number that is currently available
@@ -25,6 +25,13 @@ namespace MediaBrowser.Common.Net
         /// <returns>[string] MAC Address</returns>
         string GetMacAddress();
 
+        /// <summary>
+        /// Determines whether [is in private address space] [the specified endpoint].
+        /// </summary>
+        /// <param name="endpoint">The endpoint.</param>
+        /// <returns><c>true</c> if [is in private address space] [the specified endpoint]; otherwise, <c>false</c>.</returns>
+        bool IsInPrivateAddressSpace(string endpoint);
+
         /// <summary>
         /// Gets the network shares.
         /// </summary>

+ 2 - 1
MediaBrowser.Common/Updates/IInstallationManager.cs

@@ -105,11 +105,12 @@ namespace MediaBrowser.Common.Updates
         /// Installs the package.
         /// </summary>
         /// <param name="package">The package.</param>
+        /// <param name="isPlugin">if set to <c>true</c> [is plugin].</param>
         /// <param name="progress">The progress.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentNullException">package</exception>
-        Task InstallPackage(PackageVersionInfo package, IProgress<double> progress, CancellationToken cancellationToken);
+        Task InstallPackage(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken);
 
         /// <summary>
         /// Uninstalls a plugin

+ 8 - 2
MediaBrowser.Controller/Dto/IDtoService.cs

@@ -22,8 +22,14 @@ namespace MediaBrowser.Controller.Dto
         /// </summary>
         /// <param name="dto">The dto.</param>
         /// <param name="item">The item.</param>
-        /// <param name="fields">The fields.</param>
-        void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item, List<ItemFields> fields);
+        void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item);
+
+        /// <summary>
+        /// Gets the primary image aspect ratio.
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <returns>System.Nullable&lt;System.Double&gt;.</returns>
+        double? GetPrimaryImageAspectRatio(IHasImages item);
 
         /// <summary>
         /// Gets the base item dto.

+ 3 - 1
MediaBrowser.Controller/IServerApplicationHost.cs

@@ -1,6 +1,8 @@
 using MediaBrowser.Common;
 using MediaBrowser.Model.System;
 using System;
+using System.Collections.Generic;
+using System.Net;
 
 namespace MediaBrowser.Controller
 {
@@ -63,7 +65,7 @@ namespace MediaBrowser.Controller
         /// Gets the local ip address.
         /// </summary>
         /// <value>The local ip address.</value>
-        string LocalIpAddress { get; }
+        List<IPAddress> LocalIpAddresses { get; }
 
         /// <summary>
         /// Gets the local API URL.

+ 4 - 4
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -46,11 +46,14 @@
   <ItemGroup>
     <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
+      <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath>
     </Reference>
     <Reference Include="Interfaces.IO">
       <HintPath>..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll</HintPath>
     </Reference>
+    <Reference Include="MoreLinq">
+      <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
+    </Reference>
     <Reference Include="Patterns.Logging">
       <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
     </Reference>
@@ -65,9 +68,6 @@
     <Reference Include="ServiceStack.Interfaces">
       <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath>
     </Reference>
-    <Reference Include="MoreLinq">
-      <HintPath>..\packages\morelinq.1.1.1\lib\net35\MoreLinq.dll</HintPath>
-    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\SharedVersion.cs">

+ 0 - 6
MediaBrowser.Controller/Net/IHttpServer.cs

@@ -28,12 +28,6 @@ namespace MediaBrowser.Controller.Net
         /// the ssl certificate localtion on the file system.</param>
         void StartServer(IEnumerable<string> urlPrefixes, string certificatePath);
 
-        /// <summary>
-        /// Gets the local end points.
-        /// </summary>
-        /// <value>The local end points.</value>
-        IEnumerable<string> LocalEndPoints { get; }
-
         /// <summary>
         /// Stops this instance.
         /// </summary>

+ 2 - 2
MediaBrowser.Controller/packages.config

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
+  <package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
   <package id="Interfaces.IO" version="1.0.0.5" targetFramework="net45" />
-  <package id="morelinq" version="1.1.1" targetFramework="net45" />
+  <package id="morelinq" version="1.4.0" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
 </packages>

+ 25 - 18
MediaBrowser.Dlna/Main/DlnaEntryPoint.cs

@@ -43,19 +43,19 @@ namespace MediaBrowser.Dlna.Main
         private readonly List<string> _registeredServerIds = new List<string>();
         private bool _dlnaServerStarted;
 
-        public DlnaEntryPoint(IServerConfigurationManager config, 
-            ILogManager logManager, 
-            IServerApplicationHost appHost, 
-            INetworkManager network, 
-            ISessionManager sessionManager, 
-            IHttpClient httpClient, 
-            ILibraryManager libraryManager, 
-            IUserManager userManager, 
-            IDlnaManager dlnaManager, 
-            IImageProcessor imageProcessor, 
-            IUserDataManager userDataManager, 
-            ILocalizationManager localization, 
-            IMediaSourceManager mediaSourceManager, 
+        public DlnaEntryPoint(IServerConfigurationManager config,
+            ILogManager logManager,
+            IServerApplicationHost appHost,
+            INetworkManager network,
+            ISessionManager sessionManager,
+            IHttpClient httpClient,
+            ILibraryManager libraryManager,
+            IUserManager userManager,
+            IDlnaManager dlnaManager,
+            IImageProcessor imageProcessor,
+            IUserDataManager userDataManager,
+            ILocalizationManager localization,
+            IMediaSourceManager mediaSourceManager,
             ISsdpHandler ssdpHandler, IDeviceDiscovery deviceDiscovery)
         {
             _config = config;
@@ -148,13 +148,20 @@ namespace MediaBrowser.Dlna.Main
 
         private void RegisterServerEndpoints()
         {
-            foreach (var address in _network.GetLocalIpAddresses())
+            foreach (var address in _appHost.LocalIpAddresses)
             {
-                var guid = address.GetMD5();
+                //if (IPAddress.IsLoopback(address))
+                //{
+                //    // Should we allow this?
+                //    continue;
+                //}
+
+                var addressString = address.ToString();
+                var guid = addressString.GetMD5();
 
                 var descriptorURI = "/dlna/" + guid.ToString("N") + "/description.xml";
 
-                var uri = new Uri(_appHost.GetLocalApiUrl(address) + descriptorURI);
+                var uri = new Uri(_appHost.GetLocalApiUrl(addressString) + descriptorURI);
 
                 var services = new List<string>
                 {
@@ -165,8 +172,8 @@ namespace MediaBrowser.Dlna.Main
                     "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1",
                     "uuid:" + guid.ToString("N")
                 };
-                
-                _ssdpHandler.RegisterNotification(guid, uri, IPAddress.Parse(address), services);
+
+                _ssdpHandler.RegisterNotification(guid, uri, address, services);
 
                 _registeredServerIds.Add(guid.ToString("N"));
             }

+ 4 - 1
MediaBrowser.Dlna/MediaBrowser.Dlna.csproj

@@ -42,7 +42,10 @@
   <ItemGroup>
     <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
+      <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath>
+    </Reference>
+    <Reference Include="MoreLinq">
+      <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
     </Reference>
     <Reference Include="Patterns.Logging">
       <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>

+ 0 - 1
MediaBrowser.Dlna/Profiles/DefaultProfile.cs

@@ -37,7 +37,6 @@ namespace MediaBrowser.Dlna.Profiles
             MusicSyncBitrate = 128000;
 
             EnableAlbumArtInDidl = false;
-            EnableDlnaProtocol = true;
 
             TranscodingProfiles = new[]
             {

+ 1 - 3
MediaBrowser.Dlna/Profiles/Xbox360Profile.cs

@@ -14,7 +14,7 @@ namespace MediaBrowser.Dlna.Profiles
             Name = "Xbox 360";
 
             // Required according to above
-            ModelName = "Windows Media Connect";
+            ModelName = "Windows Media Player Sharing";
 
             ModelNumber = "12.0";
 
@@ -25,13 +25,11 @@ namespace MediaBrowser.Dlna.Profiles
             ManufacturerUrl = "http://www.microsoft.com";
             XDlnaDoc = "DMS-1.50";
             ModelDescription = "Emby : UPnP Media Server";
-            ModelNumber = "001";
 
             TimelineOffsetSeconds = 40;
             RequiresPlainFolders = true;
             RequiresPlainVideoItems = true;
             EnableMSMediaReceiverRegistrar = true;
-            EnableDlnaProtocol = false;
 
             Identification = new DeviceIdentification
             {

+ 1 - 1
MediaBrowser.Dlna/Profiles/XboxOneProfile.cs

@@ -81,7 +81,7 @@ namespace MediaBrowser.Dlna.Profiles
                 new DirectPlayProfile
                 {
                     Container = "mp4,mov,mkv",
-                    VideoCodec = "h264,mpeg4",
+                    VideoCodec = "h264,mpeg4,mpeg2video",
                     AudioCodec = "aac,ac3",
                     Type = DlnaProfileType.Video
                 },

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/BubbleUPnp.xml

@@ -34,7 +34,6 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes />
   <DirectPlayProfiles>
     <DirectPlayProfile container="avi,mpeg,mkv,ts,mp4,mov,m4v,asf,webm,ogg,ogv,iso" type="Video" />

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/Default.xml

@@ -28,7 +28,6 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes />
   <DirectPlayProfiles>
     <DirectPlayProfile container="mp3,wma" type="Audio" />

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml

@@ -33,7 +33,6 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes />
   <DirectPlayProfiles>
     <DirectPlayProfile container="mp3,flac,m4a,wma" type="Audio" />

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/DirecTV HD-DVR.xml

@@ -34,7 +34,6 @@
   <RequiresPlainFolders>true</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes />
   <DirectPlayProfiles>
     <DirectPlayProfile container="mpeg" audioCodec="mp2" videoCodec="mpeg2video" type="Video" />

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/Dish Hopper-Joey.xml

@@ -35,7 +35,6 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes />
   <DirectPlayProfiles>
     <DirectPlayProfile container="mp4,mkv,mpeg,ts" audioCodec="mp3,ac3,aac,he-aac,pcm" videoCodec="h264,mpeg2video" type="Video" />

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/Kodi.xml

@@ -34,7 +34,6 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes />
   <DirectPlayProfiles>
     <DirectPlayProfile container="" type="Video" />

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml

@@ -34,7 +34,6 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes />
   <DirectPlayProfiles>
     <DirectPlayProfile container="ts" audioCodec="aac,ac3,mp3" videoCodec="h264" type="Video" />

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml

@@ -32,7 +32,6 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes />
   <DirectPlayProfiles>
     <DirectPlayProfile container="mp3,flac,m4a,wma" type="Audio" />

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml

@@ -34,7 +34,6 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes />
   <DirectPlayProfiles>
     <DirectPlayProfile container="mp3" audioCodec="mp2,mp3" type="Audio" />

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml

@@ -35,7 +35,6 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes>
     <XmlAttribute name="xmlns:pv" value="http://www.pv.com/pvns/" />
   </XmlRootAttributes>

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/Popcorn Hour.xml

@@ -28,7 +28,6 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes />
   <DirectPlayProfiles>
     <DirectPlayProfile container="mp4,mov" audioCodec="aac" videoCodec="h264,mpeg4" type="Video" />

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml

@@ -34,7 +34,6 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes>
     <XmlAttribute name="xmlns:sec" value="http://www.sec.co.kr/" />
   </XmlRootAttributes>

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml

@@ -34,7 +34,6 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes>
     <XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
   </XmlRootAttributes>

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml

@@ -36,7 +36,6 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes>
     <XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
   </XmlRootAttributes>

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml

@@ -36,7 +36,6 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes>
     <XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
   </XmlRootAttributes>

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml

@@ -36,7 +36,6 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes>
     <XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
   </XmlRootAttributes>

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml

@@ -36,7 +36,6 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes>
     <XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
   </XmlRootAttributes>

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml

@@ -36,7 +36,6 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes>
     <XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
   </XmlRootAttributes>

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2014).xml

@@ -36,7 +36,6 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes>
     <XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
   </XmlRootAttributes>

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml

@@ -36,7 +36,6 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes />
   <DirectPlayProfiles>
     <DirectPlayProfile container="avi" audioCodec="mp2,mp3" videoCodec="mpeg4" type="Video" />

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 4.xml

@@ -36,7 +36,6 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes />
   <DirectPlayProfiles>
     <DirectPlayProfile container="avi" audioCodec="mp2,mp3" videoCodec="mpeg4" type="Video" />

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/Vlc.xml

@@ -34,7 +34,6 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes />
   <DirectPlayProfiles>
     <DirectPlayProfile container="avi,mpeg,mkv,ts,mp4,mov,m4v,asf,webm,ogg,ogv,iso" type="Video" />

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml

@@ -35,7 +35,6 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>true</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes />
   <DirectPlayProfiles>
     <DirectPlayProfile container="avi" audioCodec="ac3,dca,mp2,mp3,pcm" videoCodec="mpeg1video,mpeg2video,mpeg4,h264,vc1" type="Video" />

+ 2 - 3
MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml

@@ -11,9 +11,9 @@
   <FriendlyName>${HostName}: 1</FriendlyName>
   <Manufacturer>Microsoft Corporation</Manufacturer>
   <ManufacturerUrl>http://www.microsoft.com</ManufacturerUrl>
-  <ModelName>Windows Media Connect</ModelName>
+  <ModelName>Windows Media Player Sharing</ModelName>
   <ModelDescription>Emby : UPnP Media Server</ModelDescription>
-  <ModelNumber>001</ModelNumber>
+  <ModelNumber>12.0</ModelNumber>
   <ModelUrl>http://go.microsoft.com/fwlink/?LinkId=105926</ModelUrl>
   <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
   <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
@@ -35,7 +35,6 @@
   <RequiresPlainFolders>true</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>true</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>false</EnableDlnaProtocol>
   <XmlRootAttributes />
   <DirectPlayProfiles>
     <DirectPlayProfile container="avi" audioCodec="ac3,mp3" videoCodec="mpeg4" type="Video" />

+ 1 - 2
MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml

@@ -35,13 +35,12 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes />
   <DirectPlayProfiles>
     <DirectPlayProfile container="ts" audioCodec="ac3" videoCodec="h264" type="Video" />
     <DirectPlayProfile container="avi" audioCodec="ac3,mp3" videoCodec="mpeg4" type="Video" />
     <DirectPlayProfile container="avi" audioCodec="aac" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="mp4,mov,mkv" audioCodec="aac,ac3" videoCodec="h264,mpeg4" type="Video" />
+    <DirectPlayProfile container="mp4,mov,mkv" audioCodec="aac,ac3" videoCodec="h264,mpeg4,mpeg2video" type="Video" />
     <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro" videoCodec="wmv2,wmv3,vc1" type="Video" />
     <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" type="Audio" />
     <DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" />

+ 0 - 1
MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml

@@ -34,7 +34,6 @@
   <RequiresPlainFolders>false</RequiresPlainFolders>
   <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <EnableDlnaProtocol>true</EnableDlnaProtocol>
   <XmlRootAttributes />
   <DirectPlayProfiles>
     <DirectPlayProfile container="mp3" audioCodec="mp2,mp3" type="Audio" />

+ 8 - 17
MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs

@@ -54,14 +54,11 @@ namespace MediaBrowser.Dlna.Server
 
             var attributes = _profile.XmlRootAttributes.ToList();
 
-            if (_profile.EnableDlnaProtocol)
+            attributes.Insert(0, new XmlAttribute
             {
-                attributes.Insert(0, new XmlAttribute
-                {
-                    Name = "xmlns:dlna",
-                    Value = "urn:schemas-dlna-org:device-1-0"
-                });
-            }
+                Name = "xmlns:dlna",
+                Value = "urn:schemas-dlna-org:device-1-0"
+            });
             attributes.Insert(0, new XmlAttribute
             {
                 Name = "xmlns",
@@ -92,10 +89,7 @@ namespace MediaBrowser.Dlna.Server
             builder.Append("<device>");
             AppendDeviceProperties(builder);
 
-            if (_profile.EnableDlnaProtocol)
-            {
-                AppendIconList(builder);
-            }
+            AppendIconList(builder);
             AppendServiceList(builder);
             builder.Append("</device>");
         }
@@ -104,13 +98,10 @@ namespace MediaBrowser.Dlna.Server
         {
             builder.Append("<deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>");
 
-            if (_profile.EnableDlnaProtocol)
-            {
-                builder.Append("<dlna:X_DLNACAP>" + SecurityElement.Escape(_profile.XDlnaCap ?? string.Empty) + "</dlna:X_DLNACAP>");
+            builder.Append("<dlna:X_DLNACAP>" + SecurityElement.Escape(_profile.XDlnaCap ?? string.Empty) + "</dlna:X_DLNACAP>");
 
-                builder.Append("<dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">M-DMS-1.50</dlna:X_DLNADOC>");
-                builder.Append("<dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">" + SecurityElement.Escape(_profile.XDlnaDoc ?? string.Empty) + "</dlna:X_DLNADOC>");
-            }
+            builder.Append("<dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">M-DMS-1.50</dlna:X_DLNADOC>");
+            builder.Append("<dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">" + SecurityElement.Escape(_profile.XDlnaDoc ?? string.Empty) + "</dlna:X_DLNADOC>");
 
             builder.Append("<friendlyName>" + SecurityElement.Escape(GetFriendlyName()) + "</friendlyName>");
             builder.Append("<manufacturer>" + SecurityElement.Escape(_profile.Manufacturer ?? string.Empty) + "</manufacturer>");

+ 29 - 47
MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs

@@ -11,6 +11,8 @@ using System.Net.NetworkInformation;
 using System.Net.Sockets;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
+using MoreLinq;
 
 namespace MediaBrowser.Dlna.Ssdp
 {
@@ -26,50 +28,39 @@ namespace MediaBrowser.Dlna.Ssdp
 
         public event EventHandler<SsdpMessageEventArgs> DeviceDiscovered;
         public event EventHandler<SsdpMessageEventArgs> DeviceLeft;
+        private readonly INetworkManager _networkManager;
 
-        public DeviceDiscovery(ILogger logger, IServerConfigurationManager config, IServerApplicationHost appHost)
+        public DeviceDiscovery(ILogger logger, IServerConfigurationManager config, IServerApplicationHost appHost, INetworkManager networkManager)
         {
             _tokenSource = new CancellationTokenSource();
 
             _logger = logger;
             _config = config;
             _appHost = appHost;
+            _networkManager = networkManager;
         }
 
+		private List<IPAddress> GetLocalIpAddresses()
+		{
+		    return _networkManager.GetLocalIpAddresses().ToList();
+		}
+
         public void Start(SsdpHandler ssdpHandler)
         {
             _ssdpHandler = ssdpHandler;
             _ssdpHandler.MessageReceived += _ssdpHandler_MessageReceived;
 
-            foreach (var network in GetNetworkInterfaces())
-            {
-                _logger.Debug("Found interface: {0}. Type: {1}. Status: {2}", network.Name, network.NetworkInterfaceType, network.OperationalStatus);
-
-                if (!network.SupportsMulticast || OperationalStatus.Up != network.OperationalStatus || !network.GetIPProperties().MulticastAddresses.Any())
-                    continue;
-
-                var properties = network.GetIPProperties();
-                var ipV4 = properties.GetIPv4Properties();
-                if (null == ipV4)
-                    continue;
-
-                var localIps = properties.UnicastAddresses
-                    .Where(i => i.Address.AddressFamily == AddressFamily.InterNetwork)
-                    .Select(i => i.Address)
-                    .ToList();
-
-                foreach (var localIp in localIps)
-                {
-                    try
-                    {
-                        CreateListener(localIp);
-                    }
-                    catch (Exception e)
-                    {
-                        _logger.ErrorException("Failed to Initilize Socket", e);
-                    }
-                }
-            }
+            foreach (var localIp in GetLocalIpAddresses())
+			{
+				try
+				{
+					CreateListener(localIp);
+				}
+				catch (Exception e)
+				{
+					_logger.ErrorException("Failed to Initilize Socket", e);
+				}
+			}
         }
 
         void _ssdpHandler_MessageReceived(object sender, SsdpMessageEventArgs e)
@@ -89,8 +80,11 @@ namespace MediaBrowser.Dlna.Ssdp
             {
                 if (e.LocalEndPoint == null)
                 {
-                    var ip = _appHost.LocalIpAddress;
-                    e.LocalEndPoint = new IPEndPoint(IPAddress.Parse(ip), 0);
+                    var ip = _appHost.LocalIpAddresses.FirstOrDefault(i => !IPAddress.IsLoopback(i));
+                    if (ip != null)
+                    {
+                        e.LocalEndPoint = new IPEndPoint(ip, 0);
+                    }
                 }
 
                 if (e.LocalEndPoint != null)
@@ -107,29 +101,17 @@ namespace MediaBrowser.Dlna.Ssdp
             }
         }
 
-        private IEnumerable<NetworkInterface> GetNetworkInterfaces()
-        {
-            try
-            {
-                return NetworkInterface.GetAllNetworkInterfaces();
-            }
-            catch (Exception ex)
-            {
-                _logger.ErrorException("Error in GetAllNetworkInterfaces", ex);
-                return new List<NetworkInterface>();
-            }
-        }
         private void CreateListener(IPAddress localIp)
         {
             Task.Factory.StartNew(async (o) =>
             {
                 try
                 {
-                    var endPoint = new IPEndPoint(localIp, 1900);
+					_logger.Info("Creating SSDP listener on {0}", localIp);
 
-                    var socket = GetMulticastSocket(localIp, endPoint);
+					var endPoint = new IPEndPoint(localIp, 1900);
 
-                    _logger.Info("Creating SSDP listener on {0}", localIp);
+                    var socket = GetMulticastSocket(localIp, endPoint);
 
                     var receiveBuffer = new byte[64000];
 

+ 14 - 1
MediaBrowser.Dlna/Ssdp/SsdpHandler.cs

@@ -15,6 +15,7 @@ using System.Net.Sockets;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
+using Microsoft.Win32;
 
 namespace MediaBrowser.Dlna.Ssdp
 {
@@ -112,7 +113,9 @@ namespace MediaBrowser.Dlna.Ssdp
         {
             get
             {
-                return _devices.Values.SelectMany(i => i).ToList();
+                var devices = _devices.Values.ToList();
+
+                return devices.SelectMany(i => i).ToList();
             }
         }
 
@@ -121,6 +124,15 @@ namespace MediaBrowser.Dlna.Ssdp
             RestartSocketListener();
 
             ReloadAliveNotifier();
+            SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
+        }
+
+        void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
+        {
+            if (e.Mode == PowerModes.Resume)
+            {
+                NotifyAll();
+            }
         }
 
         public void SendSearchMessage(EndPoint localIp)
@@ -433,6 +445,7 @@ namespace MediaBrowser.Dlna.Ssdp
         public void Dispose()
         {
             _config.NamedConfigurationUpdated -= _config_ConfigurationUpdated;
+            SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
 
             _isDisposed = true;
             while (_messageQueue.Count != 0)

+ 2 - 1
MediaBrowser.Dlna/packages.config

@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
+  <package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
+  <package id="morelinq" version="1.4.0" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
 </packages>

+ 1 - 1
MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj

@@ -33,7 +33,7 @@
   <ItemGroup>
     <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
+      <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath>
     </Reference>
     <Reference Include="Patterns.Logging">
       <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>

+ 1 - 1
MediaBrowser.LocalMetadata/packages.config

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
+  <package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
 </packages>

+ 1 - 1
MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj

@@ -41,7 +41,7 @@
     </Reference>
     <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
+      <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath>
     </Reference>
     <Reference Include="DvdLib">
       <HintPath>..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\DvdLib.dll</HintPath>

+ 1 - 1
MediaBrowser.MediaEncoding/packages.config

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
+  <package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
   <package id="MediaBrowser.BdInfo" version="1.0.0.10" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
 </packages>

+ 0 - 1
MediaBrowser.Model/Dlna/DeviceProfile.cs

@@ -78,7 +78,6 @@ namespace MediaBrowser.Model.Dlna
 
         public bool EnableMSMediaReceiverRegistrar { get; set; }
         public bool IgnoreTranscodeByteRangeRequests { get; set; }
-        public bool EnableDlnaProtocol { get; set; }
 
         public XmlAttribute[] XmlRootAttributes { get; set; }
 

+ 6 - 0
MediaBrowser.Model/Search/SearchHint.cs

@@ -144,5 +144,11 @@ namespace MediaBrowser.Model.Search
         /// </summary>
         /// <value>The name of the channel.</value>
         public string ChannelName { get; set; }
+
+        /// <summary>
+        /// Gets or sets the primary image aspect ratio.
+        /// </summary>
+        /// <value>The primary image aspect ratio.</value>
+        public double? PrimaryImageAspectRatio { get; set; }
     }
 }

+ 8 - 5
MediaBrowser.Providers/Manager/ImageSaver.cs

@@ -133,6 +133,7 @@ namespace MediaBrowser.Providers.Manager
             source = memoryStream;
 
             var currentImage = GetCurrentImage(item, type, index);
+            var savedPaths = new List<string>();
 
             using (source)
             {
@@ -146,17 +147,17 @@ namespace MediaBrowser.Providers.Manager
                     {
                         retryPath = retryPaths[currentPathIndex];
                     }
-                    await SaveImageToLocation(source, path, retryPath, cancellationToken).ConfigureAwait(false);
-
+                    var savedPath = await SaveImageToLocation(source, path, retryPath, cancellationToken).ConfigureAwait(false);
+                    savedPaths.Add(savedPath);
                     currentPathIndex++;
                 }
             }
 
             // Set the path into the item
-            SetImagePath(item, type, imageIndex, paths[0]);
+            SetImagePath(item, type, imageIndex, savedPaths[0]);
 
             // Delete the current path
-            if (currentImage != null && currentImage.IsLocalFile && !paths.Contains(currentImage.Path, StringComparer.OrdinalIgnoreCase))
+            if (currentImage != null && currentImage.IsLocalFile && !savedPaths.Contains(currentImage.Path, StringComparer.OrdinalIgnoreCase))
             {
                 var currentPath = currentImage.Path;
 
@@ -184,11 +185,12 @@ namespace MediaBrowser.Providers.Manager
             }
         }
 
-        private async Task SaveImageToLocation(Stream source, string path, string retryPath, CancellationToken cancellationToken)
+        private async Task<string> SaveImageToLocation(Stream source, string path, string retryPath, CancellationToken cancellationToken)
         {
             try
             {
                 await SaveImageToLocation(source, path, cancellationToken).ConfigureAwait(false);
+                return path;
             }
             catch (UnauthorizedAccessException)
             {
@@ -207,6 +209,7 @@ namespace MediaBrowser.Providers.Manager
 
             source.Position = 0;
             await SaveImageToLocation(source, retryPath, cancellationToken).ConfigureAwait(false);
+            return retryPath;
         }
 
         /// <summary>

+ 0 - 16
MediaBrowser.Providers/Manager/ProviderManager.cs

@@ -795,22 +795,6 @@ namespace MediaBrowser.Providers.Manager
                             }
                         }
 
-                        // This is a workaround duplicate check for movies, where intersecting provider ids are not always available
-                        if (typeof(TItemType) == typeof(Movie) || typeof(TItemType) == typeof(Series))
-                        {
-                            var titleYearString = string.Format("{0} ({1})", result.Name, result.ProductionYear);
-
-                            if (foundTitleYearStrings.Contains(titleYearString))
-                            {
-                                bFound = true;
-                            }
-                            else
-                            {
-                                foundTitleYearStrings.Add(titleYearString);
-                            }
-
-                        }
-
                         if (!bFound && resultList.Count < maxResults)
                         {
                             resultList.Add(result);

+ 3 - 4
MediaBrowser.Providers/MediaBrowser.Providers.csproj

@@ -50,15 +50,14 @@
     </Reference>
     <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
+      <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath>
     </Reference>
     <Reference Include="DvdLib, Version=1.0.5167.21152, Culture=neutral, PublicKeyToken=7a2f3f5ec8d93575, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\DvdLib.dll</HintPath>
     </Reference>
-    <Reference Include="MoreLinq, Version=1.1.18418.0, Culture=neutral, PublicKeyToken=384d532d7e88985d, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\morelinq.1.1.1\lib\net35\MoreLinq.dll</HintPath>
+    <Reference Include="MoreLinq">
+      <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
     </Reference>
     <Reference Include="Patterns.Logging">
       <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>

+ 8 - 1
MediaBrowser.Providers/Music/FanArtAlbumProvider.cs

@@ -68,7 +68,14 @@ namespace MediaBrowser.Providers.Music
 
             var list = new List<RemoteImageInfo>();
 
-            var artistMusicBrainzId = album.MusicArtist.GetProviderId(MetadataProviders.MusicBrainzArtist);
+            var musicArtist = album.MusicArtist;
+
+            if (musicArtist == null)
+            {
+                return list;
+            }
+
+            var artistMusicBrainzId = musicArtist.GetProviderId(MetadataProviders.MusicBrainzArtist);
 
             if (!string.IsNullOrEmpty(artistMusicBrainzId))
             {

+ 3 - 13
MediaBrowser.Providers/Omdb/OmdbImageProvider.cs

@@ -51,25 +51,15 @@ namespace MediaBrowser.Providers.Omdb
             return Task.FromResult<IEnumerable<RemoteImageInfo>>(list);
         }
 
-        public async Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
+        public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
         {
-            var response = await _httpClient.GetResponse(new HttpRequestOptions
+            return _httpClient.GetResponse(new HttpRequestOptions
             {
                 CancellationToken = cancellationToken,
                 Url = url,
                 ResourcePool = OmdbProvider.ResourcePool
 
-            }).ConfigureAwait(false);
-
-            if (response.ContentLength == 11059)
-            {
-                throw new HttpException("File not found")
-                {
-                    StatusCode = HttpStatusCode.NotFound
-                };
-            }
-
-            return response;
+            });
         }
 
         public string Name

+ 16 - 4
MediaBrowser.Providers/Omdb/OmdbItemProvider.cs

@@ -57,7 +57,12 @@ namespace MediaBrowser.Providers.Omdb
             return GetSearchResults(searchInfo, "movie", cancellationToken);
         }
 
-        public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ItemLookupInfo searchInfo, string type, CancellationToken cancellationToken)
+        public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ItemLookupInfo searchInfo, string type, CancellationToken cancellationToken)
+        {
+            return GetSearchResultsInternal(searchInfo, type, true, cancellationToken);
+        }
+
+        private async Task<IEnumerable<RemoteSearchResult>> GetSearchResultsInternal(ItemLookupInfo searchInfo, string type, bool enableMultipleResults, CancellationToken cancellationToken)
         {
             bool isSearch = false;
 
@@ -86,7 +91,14 @@ namespace MediaBrowser.Providers.Omdb
                 }
 
                 // &s means search and returns a list of results as opposed to t
-                url += "&s=" + WebUtility.UrlEncode(name);
+                if (enableMultipleResults)
+                {
+                    url += "&s=" + WebUtility.UrlEncode(name);
+                }
+                else
+                {
+                    url += "&t=" + WebUtility.UrlEncode(name);
+                }
                 url += "&type=" + type;
                 isSearch = true;
             }
@@ -239,14 +251,14 @@ namespace MediaBrowser.Providers.Omdb
 
         private async Task<string> GetMovieImdbId(ItemLookupInfo info, CancellationToken cancellationToken)
         {
-            var results = await GetSearchResults(info, "movie", cancellationToken).ConfigureAwait(false);
+            var results = await GetSearchResultsInternal(info, "movie", false, cancellationToken).ConfigureAwait(false);
             var first = results.FirstOrDefault();
             return first == null ? null : first.GetProviderId(MetadataProviders.Imdb);
         }
 
         private async Task<string> GetSeriesImdbId(SeriesInfo info, CancellationToken cancellationToken)
         {
-            var results = await GetSearchResults(info, cancellationToken).ConfigureAwait(false);
+            var results = await GetSearchResultsInternal(info, "series", false, cancellationToken).ConfigureAwait(false);
             var first = results.FirstOrDefault();
             return first == null ? null : first.GetProviderId(MetadataProviders.Imdb);
         }

+ 17 - 26
MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs

@@ -18,6 +18,7 @@ using System.IO;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Model.Net;
 
 namespace MediaBrowser.Providers.Subtitles
 {
@@ -30,15 +31,6 @@ namespace MediaBrowser.Providers.Subtitles
         private readonly IServerConfigurationManager _config;
         private readonly IEncryptionManager _encryption;
 
-        private Timer _dailyTimer;
-
-        // This is limited to 200 per day
-        private int _dailyDownloadCount;
-
-        // It's 200 but this will be in-exact so buffer a little
-        // And the user may restart the server
-        private const int MaxDownloadsPerDay = 150;
-
         private readonly IJsonSerializer _json;
 
         public OpenSubtitleDownloader(ILogManager logManager, IHttpClient httpClient, IServerConfigurationManager config, IEncryptionManager encryption, IJsonSerializer json)
@@ -51,9 +43,6 @@ namespace MediaBrowser.Providers.Subtitles
 
             _config.NamedConfigurationUpdating += _config_NamedConfigurationUpdating;
 
-            // Reset the count every 24 hours
-            _dailyTimer = new Timer(state => _dailyDownloadCount = 0, null, TimeSpan.FromHours(24), TimeSpan.FromHours(24));
-
             Utilities.HttpClient = httpClient;
             OpenSubtitles.SetUserAgent("mediabrowser.tv");
         }
@@ -123,6 +112,7 @@ namespace MediaBrowser.Providers.Subtitles
             return GetSubtitlesInternal(id, GetOptions(), cancellationToken);
         }
 
+        private DateTime _lastRateLimitException;
         private async Task<SubtitleResponse> GetSubtitlesInternal(string id,
             SubtitleOptions options,
             CancellationToken cancellationToken)
@@ -132,12 +122,6 @@ namespace MediaBrowser.Providers.Subtitles
                 throw new ArgumentNullException("id");
             }
 
-            if (_dailyDownloadCount >= MaxDownloadsPerDay &&
-                !options.IsOpenSubtitleVipAccount)
-            {
-                throw new InvalidOperationException("Open Subtitle's daily download limit has been exceeded. Please try again tomorrow.");
-            }
-
             var idParts = id.Split(new[] { '-' }, 3);
 
             var format = idParts[0];
@@ -148,8 +132,19 @@ namespace MediaBrowser.Providers.Subtitles
 
             await Login(cancellationToken).ConfigureAwait(false);
 
+            if ((DateTime.UtcNow - _lastRateLimitException).TotalHours < 1)
+            {
+                throw new ApplicationException("OpenSubtitles rate limit reached");
+            }
+
             var resultDownLoad = await OpenSubtitles.DownloadSubtitlesAsync(downloadsList, cancellationToken).ConfigureAwait(false);
 
+            if ((resultDownLoad.Status ?? string.Empty).IndexOf("407", StringComparison.OrdinalIgnoreCase) != -1)
+            {
+                _lastRateLimitException = DateTime.UtcNow;
+                throw new ApplicationException("OpenSubtitles rate limit reached");
+            }
+
             if (!(resultDownLoad is MethodResponseSubtitleDownload))
             {
                 throw new ApplicationException("Invalid response type");
@@ -157,13 +152,15 @@ namespace MediaBrowser.Providers.Subtitles
 
             var results = ((MethodResponseSubtitleDownload)resultDownLoad).Results;
 
+            _lastRateLimitException = DateTime.MinValue;
+
             if (results.Count == 0)
             {
                 var msg = string.Format("Subtitle with Id {0} was not found. Name: {1}. Status: {2}. Message: {3}",
                     ossId,
                     resultDownLoad.Name ?? string.Empty,
-                    resultDownLoad.Message ?? string.Empty,
-                    resultDownLoad.Status ?? string.Empty);
+                    resultDownLoad.Status ?? string.Empty,
+                    resultDownLoad.Message ?? string.Empty);
 
                 throw new ResourceNotFoundException(msg);
             }
@@ -339,12 +336,6 @@ namespace MediaBrowser.Providers.Subtitles
         public void Dispose()
         {
             _config.NamedConfigurationUpdating -= _config_NamedConfigurationUpdating;
-
-            if (_dailyTimer != null)
-            {
-                _dailyTimer.Dispose();
-                _dailyTimer = null;
-            }
         }
     }
 }

+ 2 - 0
MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs

@@ -112,6 +112,8 @@ namespace MediaBrowser.Providers.TV
             if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds) && 
 				(searchInfo.IndexNumber.HasValue || searchInfo.PremiereDate.HasValue))
             {
+                await TvdbSeriesProvider.Current.EnsureSeriesInfo(searchInfo.SeriesProviderIds, searchInfo.MetadataLanguage, cancellationToken).ConfigureAwait(false);
+
                 var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, searchInfo.SeriesProviderIds);
 
                 var searchNumbers = new EpisodeNumbers();

+ 36 - 21
MediaBrowser.Providers/TV/TvdbSeriesProvider.cs

@@ -222,6 +222,11 @@ namespace MediaBrowser.Providers.TV
                 seriesId = await GetSeriesByRemoteId(seriesId, idType, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
             }
 
+            if (string.IsNullOrWhiteSpace(seriesId))
+            {
+                throw new ArgumentNullException("seriesId");
+            }
+
             var url = string.Format(SeriesGetZip, TVUtils.TvdbApiKey, seriesId, preferredMetadataLanguage);
 
             using (var zipStream = await _httpClient.Get(new HttpRequestOptions
@@ -324,38 +329,48 @@ namespace MediaBrowser.Providers.TV
             return false;
         }
 
+        private SemaphoreSlim _ensureSemaphore = new SemaphoreSlim(1,1);
         internal async Task<string> EnsureSeriesInfo(Dictionary<string, string> seriesProviderIds, string preferredMetadataLanguage, CancellationToken cancellationToken)
         {
-            string seriesId;
-            if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out seriesId))
-            {
-                var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds);
+            await _ensureSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
 
-                // Only download if not already there
-                // The post-scan task will take care of updates so we don't need to re-download here
-                if (!IsCacheValid(seriesDataPath, preferredMetadataLanguage))
+            try
+            {
+                string seriesId;
+                if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out seriesId))
                 {
-                    await DownloadSeriesZip(seriesId, MetadataProviders.Tvdb.ToString(), seriesDataPath, null, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
-                }
+                    var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds);
 
-                return seriesDataPath;
-            }
+                    // Only download if not already there
+                    // The post-scan task will take care of updates so we don't need to re-download here
+                    if (!IsCacheValid(seriesDataPath, preferredMetadataLanguage))
+                    {
+                        await DownloadSeriesZip(seriesId, MetadataProviders.Tvdb.ToString(), seriesDataPath, null, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
+                    }
 
-            if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out seriesId))
-            {
-                var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds);
+                    return seriesDataPath;
+                }
 
-                // Only download if not already there
-                // The post-scan task will take care of updates so we don't need to re-download here
-                if (!IsCacheValid(seriesDataPath, preferredMetadataLanguage))
+                if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out seriesId))
                 {
-                    await DownloadSeriesZip(seriesId, MetadataProviders.Imdb.ToString(), seriesDataPath, null, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
+                    var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds);
+
+                    // Only download if not already there
+                    // The post-scan task will take care of updates so we don't need to re-download here
+                    if (!IsCacheValid(seriesDataPath, preferredMetadataLanguage))
+                    {
+                        await DownloadSeriesZip(seriesId, MetadataProviders.Imdb.ToString(), seriesDataPath, null, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
+                    }
+
+                    return seriesDataPath;
                 }
 
-                return seriesDataPath;
+                return null;
+            }
+            finally
+            {
+                _ensureSemaphore.Release();
             }
-
-            return null;
         }
 
         private bool IsCacheValid(string seriesDataPath, string preferredMetadataLanguage)

+ 2 - 2
MediaBrowser.Providers/packages.config

@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
+  <package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
   <package id="MediaBrowser.BdInfo" version="1.0.0.10" targetFramework="net45" />
-  <package id="morelinq" version="1.1.1" targetFramework="net45" />
+  <package id="morelinq" version="1.4.0" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
   <package id="taglib" version="2.1.0.0" targetFramework="net45" />
 </packages>

+ 11 - 6
MediaBrowser.Server.Implementations/Dto/DtoService.cs

@@ -310,7 +310,7 @@ namespace MediaBrowser.Server.Implementations.Dto
             {
                 try
                 {
-                    AttachPrimaryImageAspectRatio(dto, item, fields);
+                    AttachPrimaryImageAspectRatio(dto, item);
                 }
                 catch (Exception ex)
                 {
@@ -1742,15 +1742,19 @@ namespace MediaBrowser.Server.Implementations.Dto
         /// </summary>
         /// <param name="dto">The dto.</param>
         /// <param name="item">The item.</param>
-        /// <param name="fields">The fields.</param>
         /// <returns>Task.</returns>
-        public void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item, List<ItemFields> fields)
+        public void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item)
+        {
+            dto.PrimaryImageAspectRatio = GetPrimaryImageAspectRatio(item);
+        }
+
+        public double? GetPrimaryImageAspectRatio(IHasImages item)
         {
             var imageInfo = item.GetImageInfo(ImageType.Primary, 0);
 
             if (imageInfo == null || !imageInfo.IsLocalFile)
             {
-                return;
+                return null;
             }
 
             ImageSize size;
@@ -1762,7 +1766,7 @@ namespace MediaBrowser.Server.Implementations.Dto
             catch (Exception ex)
             {
                 //_logger.ErrorException("Failed to determine primary image aspect ratio for {0}", ex, path);
-                return;
+                return null;
             }
 
             var supportedEnhancers = _imageProcessor.GetSupportedEnhancers(item, ImageType.Primary).ToList();
@@ -1781,8 +1785,9 @@ namespace MediaBrowser.Server.Implementations.Dto
 
             if (size.Width > 0 && size.Height > 0)
             {
-                dto.PrimaryImageAspectRatio = size.Width / size.Height;
+                return size.Width / size.Height;
             }
+            return null;
         }
     }
 }

+ 123 - 49
MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs

@@ -74,42 +74,52 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
 
             if (!string.IsNullOrEmpty(seriesName))
             {
-                var season = episodeInfo.SeasonNumber;
-
-                result.ExtractedSeasonNumber = season;
-
-                if (season.HasValue)
-                {
-                    // Passing in true will include a few extra regex's
-                    var episode = episodeInfo.EpisodeNumber;
-
-                    result.ExtractedEpisodeNumber = episode;
-
-                    if (episode.HasValue)
-                    {
-                        _logger.Debug("Extracted information from {0}. Series name {1}, Season {2}, Episode {3}", path, seriesName, season, episode);
-
-                        var endingEpisodeNumber = episodeInfo.EndingEpsiodeNumber;
-
-                        result.ExtractedEndingEpisodeNumber = endingEpisodeNumber;
-
-                        await OrganizeEpisode(path, seriesName, season.Value, episode.Value, endingEpisodeNumber, options, overwriteExisting, result, cancellationToken).ConfigureAwait(false);
-                    }
-                    else
-                    {
-                        var msg = string.Format("Unable to determine episode number from {0}", path);
-                        result.Status = FileSortingStatus.Failure;
-                        result.StatusMessage = msg;
-                        _logger.Warn(msg);
-                    }
-                }
-                else
-                {
-                    var msg = string.Format("Unable to determine season number from {0}", path);
-                    result.Status = FileSortingStatus.Failure;
-                    result.StatusMessage = msg;
-                    _logger.Warn(msg);
-                }
+                var seasonNumber = episodeInfo.SeasonNumber;
+
+				result.ExtractedSeasonNumber = seasonNumber;
+
+				// Passing in true will include a few extra regex's
+				var episodeNumber = episodeInfo.EpisodeNumber;
+
+				result.ExtractedEpisodeNumber = episodeNumber;
+
+				var premiereDate = episodeInfo.IsByDate ? 
+					new DateTime(episodeInfo.Year.Value, episodeInfo.Month.Value, episodeInfo.Day.Value) : 
+					(DateTime?)null;
+
+				if (episodeInfo.IsByDate || (seasonNumber.HasValue && episodeNumber.HasValue))
+				{
+					if (episodeInfo.IsByDate) 
+					{
+						_logger.Debug("Extracted information from {0}. Series name {1}, Date {2}", path, seriesName, premiereDate.Value);
+					} 
+					else 
+					{
+						_logger.Debug("Extracted information from {0}. Series name {1}, Season {2}, Episode {3}", path, seriesName, seasonNumber, episodeNumber);
+					}
+
+					var endingEpisodeNumber = episodeInfo.EndingEpsiodeNumber;
+
+					result.ExtractedEndingEpisodeNumber = endingEpisodeNumber;
+
+					await OrganizeEpisode(path, 
+						seriesName, 
+						seasonNumber, 
+						episodeNumber, 
+						endingEpisodeNumber, 
+						premiereDate,
+						options, 
+						overwriteExisting, 
+						result, 
+						cancellationToken).ConfigureAwait(false);
+				}
+				else
+				{
+					var msg = string.Format("Unable to determine episode number from {0}", path);
+					result.Status = FileSortingStatus.Failure;
+					result.StatusMessage = msg;
+					_logger.Warn(msg);
+				}
             }
             else
             {
@@ -141,14 +151,32 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
 
             var series = (Series)_libraryManager.GetItemById(new Guid(request.SeriesId));
 
-            await OrganizeEpisode(result.OriginalPath, series, request.SeasonNumber, request.EpisodeNumber, request.EndingEpisodeNumber, options, true, result, cancellationToken).ConfigureAwait(false);
+            await OrganizeEpisode(result.OriginalPath, 
+				series, 
+				request.SeasonNumber, 
+				request.EpisodeNumber, 
+				request.EndingEpisodeNumber, 
+				null,
+				options, 
+				true, 
+				result, 
+				cancellationToken).ConfigureAwait(false);
 
             await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false);
 
             return result;
         }
 
-        private Task OrganizeEpisode(string sourcePath, string seriesName, int seasonNumber, int episodeNumber, int? endingEpiosdeNumber, TvFileOrganizationOptions options, bool overwriteExisting, FileOrganizationResult result, CancellationToken cancellationToken)
+        private Task OrganizeEpisode(string sourcePath, 
+			string seriesName, 
+			int? seasonNumber, 
+			int? episodeNumber, 
+			int? endingEpiosdeNumber, 
+			DateTime? premiereDate,
+			TvFileOrganizationOptions options, 
+			bool overwriteExisting, 
+			FileOrganizationResult result, 
+			CancellationToken cancellationToken)
         {
             var series = GetMatchingSeries(seriesName, result);
 
@@ -161,15 +189,33 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
                 return Task.FromResult(true);
             }
 
-            return OrganizeEpisode(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, options, overwriteExisting, result, cancellationToken);
+            return OrganizeEpisode(sourcePath, 
+				series, 
+				seasonNumber, 
+				episodeNumber, 
+				endingEpiosdeNumber,
+				premiereDate,
+				options, 
+				overwriteExisting, 
+				result, 
+				cancellationToken);
         }
 
-        private async Task OrganizeEpisode(string sourcePath, Series series, int seasonNumber, int episodeNumber, int? endingEpiosdeNumber, TvFileOrganizationOptions options, bool overwriteExisting, FileOrganizationResult result, CancellationToken cancellationToken)
+        private async Task OrganizeEpisode(string sourcePath, 
+			Series series, 
+			int? seasonNumber, 
+			int? episodeNumber, 
+			int? endingEpiosdeNumber, 
+			DateTime? premiereDate,
+			TvFileOrganizationOptions options, 
+			bool overwriteExisting, 
+			FileOrganizationResult result, 
+			CancellationToken cancellationToken)
         {
             _logger.Info("Sorting file {0} into series {1}", sourcePath, series.Path);
 
             // Proceed to sort the file
-            var newPath = await GetNewPath(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, options, cancellationToken).ConfigureAwait(false);
+			var newPath = await GetNewPath(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, premiereDate, options, cancellationToken).ConfigureAwait(false);
 
             if (string.IsNullOrEmpty(newPath))
             {
@@ -278,8 +324,18 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             }
         }
 
-        private List<string> GetOtherDuplicatePaths(string targetPath, Series series, int seasonNumber, int episodeNumber, int? endingEpisodeNumber)
+        private List<string> GetOtherDuplicatePaths(string targetPath, 
+			Series series, 
+			int? seasonNumber, 
+			int? episodeNumber, 
+			int? endingEpisodeNumber)
         {
+			// TODO: Support date-naming?
+			if (!seasonNumber.HasValue || episodeNumber.HasValue) 
+			{
+				return new List<string> ();
+			}
+
             var episodePaths = series.GetRecursiveChildren()
                 .OfType<Episode>()
                 .Where(i =>
@@ -408,7 +464,14 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
         /// <param name="endingEpisodeNumber">The ending episode number.</param>
         /// <param name="options">The options.</param>
         /// <returns>System.String.</returns>
-        private async Task<string> GetNewPath(string sourcePath, Series series, int seasonNumber, int episodeNumber, int? endingEpisodeNumber, TvFileOrganizationOptions options, CancellationToken cancellationToken)
+        private async Task<string> GetNewPath(string sourcePath, 
+			Series series, 
+			int? seasonNumber, 
+			int? episodeNumber, 
+			int? endingEpisodeNumber, 
+			DateTime? premiereDate,
+			TvFileOrganizationOptions options, 
+			CancellationToken cancellationToken)
         {
             var episodeInfo = new EpisodeInfo
             {
@@ -417,7 +480,8 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
                 MetadataCountryCode = series.GetPreferredMetadataCountryCode(),
                 MetadataLanguage = series.GetPreferredMetadataLanguage(),
                 ParentIndexNumber = seasonNumber,
-                SeriesProviderIds = series.ProviderIds
+                SeriesProviderIds = series.ProviderIds,
+				PremiereDate = premiereDate
             };
 
             var searchResults = await _providerManager.GetRemoteSearchResults<Episode, EpisodeInfo>(new RemoteSearchQuery<EpisodeInfo>
@@ -427,14 +491,24 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             }, cancellationToken).ConfigureAwait(false);
 
             var episode = searchResults.FirstOrDefault();
+            
+            string episodeName = string.Empty;
 
             if (episode == null)
             {
-                _logger.Warn("No provider metadata found for {0} season {1} episode {2}", series.Name, seasonNumber, episodeNumber);
-                return null;
+                var msg = string.Format("No provider metadata found for {0} season {1} episode {2}", series.Name, seasonNumber, episodeNumber);
+                _logger.Warn(msg);
+                //throw new Exception(msg);
             }
+            else
+            {
+                episodeName = episode.Name;
+			}
+
+			seasonNumber = seasonNumber ?? episode.ParentIndexNumber;
+			episodeNumber = episodeNumber ?? episode.IndexNumber;
 
-            var newPath = GetSeasonFolderPath(series, seasonNumber, options);
+            var newPath = GetSeasonFolderPath(series, seasonNumber.Value, options);
 
             // MAX_PATH - trailing <NULL> charachter - drive component: 260 - 1 - 3 = 256
             // Usually newPath would include the drive component, but use 256 to be sure
@@ -449,7 +523,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             // Remove additional 4 chars to prevent PathTooLongException for downloaded subtitles (eg. filename.ext.eng.srt)
             maxFilenameLength -= 4;
 
-            var episodeFileName = GetEpisodeFileName(sourcePath, series.Name, seasonNumber, episodeNumber, endingEpisodeNumber, episode.Name, options, maxFilenameLength);
+            var episodeFileName = GetEpisodeFileName(sourcePath, series.Name, seasonNumber.Value, episodeNumber.Value, endingEpisodeNumber, episodeName, options, maxFilenameLength);
 
             if (string.IsNullOrEmpty(episodeFileName))
             {
@@ -505,7 +579,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
         {
             seriesName = _fileSystem.GetValidFilename(seriesName).Trim();
 
-            if (episodeTitle == null)
+            if (string.IsNullOrEmpty(episodeTitle))
             {
                 episodeTitle = string.Empty;
             }

+ 4 - 43
MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs

@@ -19,6 +19,7 @@ using System.Linq;
 using System.Reflection;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Security;
 
 namespace MediaBrowser.Server.Implementations.HttpServer
@@ -39,40 +40,20 @@ namespace MediaBrowser.Server.Implementations.HttpServer
         public event EventHandler<WebSocketConnectEventArgs> WebSocketConnected;
         public event EventHandler<WebSocketConnectingEventArgs> WebSocketConnecting;
 
-        private readonly List<string> _localEndpoints = new List<string>();
-
-        private readonly ReaderWriterLockSlim _localEndpointLock = new ReaderWriterLockSlim();
-
         public string CertificatePath { get; private set; }
 
         private readonly IServerConfigurationManager _config;
-
-        /// <summary>
-        /// Gets the local end points.
-        /// </summary>
-        /// <value>The local end points.</value>
-        public IEnumerable<string> LocalEndPoints
-        {
-            get
-            {
-                _localEndpointLock.EnterReadLock();
-
-                var list = _localEndpoints.ToList();
-
-                _localEndpointLock.ExitReadLock();
-
-                return list;
-            }
-        }
+        private readonly INetworkManager _networkManager;
 
         public HttpListenerHost(IApplicationHost applicationHost,
             ILogManager logManager,
             IServerConfigurationManager config,
             string serviceName,
-            string defaultRedirectPath, params Assembly[] assembliesWithServices)
+            string defaultRedirectPath, INetworkManager networkManager, params Assembly[] assembliesWithServices)
             : base(serviceName, assembliesWithServices)
         {
             DefaultRedirectPath = defaultRedirectPath;
+            _networkManager = networkManager;
             _config = config;
 
             _logger = logManager.GetLogger("HttpServer");
@@ -175,26 +156,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer
 
         private void OnRequestReceived(string localEndPoint)
         {
-            var ignore = localEndPoint.IndexOf("::", StringComparison.OrdinalIgnoreCase) != -1 ||
-
-                localEndPoint.StartsWith("127.", StringComparison.OrdinalIgnoreCase) ||
-                localEndPoint.StartsWith("localhost", StringComparison.OrdinalIgnoreCase) ||
-                localEndPoint.StartsWith("169.", StringComparison.OrdinalIgnoreCase);
-
-            if (ignore)
-            {
-                return;
-            }
-
-            if (_localEndpointLock.TryEnterWriteLock(100))
-            {
-                var list = _localEndpoints.ToList();
-
-                list.Remove(localEndPoint);
-                list.Insert(0, localEndPoint);
-
-                _localEndpointLock.ExitWriteLock();
-            }
         }
 
         /// <summary>

+ 4 - 1
MediaBrowser.Server.Implementations/HttpServer/ResponseFilter.cs

@@ -58,7 +58,10 @@ namespace MediaBrowser.Server.Implementations.HttpServer
 
             if (hasOptions != null)
             {
-                hasOptions.Options["Server"] = "Mono-HTTPAPI/1.1";
+                if (!hasOptions.Options.ContainsKey("Server"))
+                {
+                    hasOptions.Options["Server"] = "Mono-HTTPAPI/1.1, UPnP/1.0 DLNADOC/1.50";
+                }
 
                 // Content length has to be explicitly set on on HttpListenerResponse or it won't be happy
                 string contentLength;

+ 4 - 1
MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs

@@ -1,4 +1,5 @@
 using MediaBrowser.Common;
+using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.Logging;
@@ -17,18 +18,20 @@ namespace MediaBrowser.Server.Implementations.HttpServer
         /// <param name="applicationHost">The application host.</param>
         /// <param name="logManager">The log manager.</param>
         /// <param name="config">The configuration.</param>
+        /// <param name="_networkmanager">The _networkmanager.</param>
         /// <param name="serverName">Name of the server.</param>
         /// <param name="defaultRedirectpath">The default redirectpath.</param>
         /// <returns>IHttpServer.</returns>
         public static IHttpServer CreateServer(IApplicationHost applicationHost,
             ILogManager logManager,
             IServerConfigurationManager config, 
+            INetworkManager _networkmanager,
             string serverName, 
             string defaultRedirectpath)
         {
             LogManager.LogFactory = new ServerLogFactory(logManager);
 
-            return new HttpListenerHost(applicationHost, logManager, config, serverName, defaultRedirectpath);
+            return new HttpListenerHost(applicationHost, logManager, config, serverName, defaultRedirectpath, _networkmanager);
         }
     }
 }

+ 941 - 0
MediaBrowser.Server.Implementations/HttpServer/SocketSharp/HttpUtility.cs

@@ -0,0 +1,941 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Text;
+
+namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
+{
+    public static class MyHttpUtility
+    {
+        sealed class HttpQSCollection : NameValueCollection
+        {
+            public override string ToString()
+            {
+                int count = Count;
+                if (count == 0)
+                    return "";
+                StringBuilder sb = new StringBuilder();
+                string[] keys = AllKeys;
+                for (int i = 0; i < count; i++)
+                {
+                    sb.AppendFormat("{0}={1}&", keys[i], this[keys[i]]);
+                }
+                if (sb.Length > 0)
+                    sb.Length--;
+                return sb.ToString();
+            }
+        }
+
+        // Must be sorted
+        static readonly long[] entities = new long[] {
+			(long)'A' << 56 | (long)'E' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24, 
+			(long)'A' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, 
+			(long)'A' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, 
+			(long)'A' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, 
+			(long)'A' << 56 | (long)'l' << 48 | (long)'p' << 40 | (long)'h' << 32 | (long)'a' << 24, 
+			(long)'A' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'g' << 24, 
+			(long)'A' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, 
+			(long)'A' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, 
+			(long)'B' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32, 
+			(long)'C' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'d' << 32 | (long)'i' << 24 | (long)'l' << 16, 
+			(long)'C' << 56 | (long)'h' << 48 | (long)'i' << 40, 
+			(long)'D' << 56 | (long)'a' << 48 | (long)'g' << 40 | (long)'g' << 32 | (long)'e' << 24 | (long)'r' << 16, 
+			(long)'D' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'t' << 32 | (long)'a' << 24, 
+			(long)'E' << 56 | (long)'T' << 48 | (long)'H' << 40, 
+			(long)'E' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, 
+			(long)'E' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, 
+			(long)'E' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, 
+			(long)'E' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8, 
+			(long)'E' << 56 | (long)'t' << 48 | (long)'a' << 40, 
+			(long)'E' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, 
+			(long)'G' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'m' << 32 | (long)'a' << 24, 
+			(long)'I' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, 
+			(long)'I' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, 
+			(long)'I' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, 
+			(long)'I' << 56 | (long)'o' << 48 | (long)'t' << 40 | (long)'a' << 32, 
+			(long)'I' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, 
+			(long)'K' << 56 | (long)'a' << 48 | (long)'p' << 40 | (long)'p' << 32 | (long)'a' << 24, 
+			(long)'L' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'b' << 32 | (long)'d' << 24 | (long)'a' << 16, 
+			(long)'M' << 56 | (long)'u' << 48, 
+			(long)'N' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, 
+			(long)'N' << 56 | (long)'u' << 48, 
+			(long)'O' << 56 | (long)'E' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24, 
+			(long)'O' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, 
+			(long)'O' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, 
+			(long)'O' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, 
+			(long)'O' << 56 | (long)'m' << 48 | (long)'e' << 40 | (long)'g' << 32 | (long)'a' << 24, 
+			(long)'O' << 56 | (long)'m' << 48 | (long)'i' << 40 | (long)'c' << 32 | (long)'r' << 24 | (long)'o' << 16 | (long)'n' << 8, 
+			(long)'O' << 56 | (long)'s' << 48 | (long)'l' << 40 | (long)'a' << 32 | (long)'s' << 24 | (long)'h' << 16, 
+			(long)'O' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, 
+			(long)'O' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, 
+			(long)'P' << 56 | (long)'h' << 48 | (long)'i' << 40, 
+			(long)'P' << 56 | (long)'i' << 48, 
+			(long)'P' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'m' << 32 | (long)'e' << 24, 
+			(long)'P' << 56 | (long)'s' << 48 | (long)'i' << 40, 
+			(long)'R' << 56 | (long)'h' << 48 | (long)'o' << 40, 
+			(long)'S' << 56 | (long)'c' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'o' << 24 | (long)'n' << 16, 
+			(long)'S' << 56 | (long)'i' << 48 | (long)'g' << 40 | (long)'m' << 32 | (long)'a' << 24, 
+			(long)'T' << 56 | (long)'H' << 48 | (long)'O' << 40 | (long)'R' << 32 | (long)'N' << 24, 
+			(long)'T' << 56 | (long)'a' << 48 | (long)'u' << 40, 
+			(long)'T' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'t' << 32 | (long)'a' << 24, 
+			(long)'U' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, 
+			(long)'U' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, 
+			(long)'U' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, 
+			(long)'U' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8, 
+			(long)'U' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, 
+			(long)'X' << 56 | (long)'i' << 48, 
+			(long)'Y' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, 
+			(long)'Y' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, 
+			(long)'Z' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32, 
+			(long)'a' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, 
+			(long)'a' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, 
+			(long)'a' << 56 | (long)'c' << 48 | (long)'u' << 40 | (long)'t' << 32 | (long)'e' << 24, 
+			(long)'a' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24, 
+			(long)'a' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, 
+			(long)'a' << 56 | (long)'l' << 48 | (long)'e' << 40 | (long)'f' << 32 | (long)'s' << 24 | (long)'y' << 16 | (long)'m' << 8, 
+			(long)'a' << 56 | (long)'l' << 48 | (long)'p' << 40 | (long)'h' << 32 | (long)'a' << 24, 
+			(long)'a' << 56 | (long)'m' << 48 | (long)'p' << 40, 
+			(long)'a' << 56 | (long)'n' << 48 | (long)'d' << 40, 
+			(long)'a' << 56 | (long)'n' << 48 | (long)'g' << 40, 
+			(long)'a' << 56 | (long)'p' << 48 | (long)'o' << 40 | (long)'s' << 32,
+			(long)'a' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'g' << 24, 
+			(long)'a' << 56 | (long)'s' << 48 | (long)'y' << 40 | (long)'m' << 32 | (long)'p' << 24, 
+			(long)'a' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, 
+			(long)'a' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, 
+			(long)'b' << 56 | (long)'d' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, 
+			(long)'b' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32, 
+			(long)'b' << 56 | (long)'r' << 48 | (long)'v' << 40 | (long)'b' << 32 | (long)'a' << 24 | (long)'r' << 16, 
+			(long)'b' << 56 | (long)'u' << 48 | (long)'l' << 40 | (long)'l' << 32, 
+			(long)'c' << 56 | (long)'a' << 48 | (long)'p' << 40, 
+			(long)'c' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'d' << 32 | (long)'i' << 24 | (long)'l' << 16, 
+			(long)'c' << 56 | (long)'e' << 48 | (long)'d' << 40 | (long)'i' << 32 | (long)'l' << 24, 
+			(long)'c' << 56 | (long)'e' << 48 | (long)'n' << 40 | (long)'t' << 32, 
+			(long)'c' << 56 | (long)'h' << 48 | (long)'i' << 40, 
+			(long)'c' << 56 | (long)'i' << 48 | (long)'r' << 40 | (long)'c' << 32, 
+			(long)'c' << 56 | (long)'l' << 48 | (long)'u' << 40 | (long)'b' << 32 | (long)'s' << 24, 
+			(long)'c' << 56 | (long)'o' << 48 | (long)'n' << 40 | (long)'g' << 32, 
+			(long)'c' << 56 | (long)'o' << 48 | (long)'p' << 40 | (long)'y' << 32, 
+			(long)'c' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'r' << 24, 
+			(long)'c' << 56 | (long)'u' << 48 | (long)'p' << 40, 
+			(long)'c' << 56 | (long)'u' << 48 | (long)'r' << 40 | (long)'r' << 32 | (long)'e' << 24 | (long)'n' << 16, 
+			(long)'d' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32, 
+			(long)'d' << 56 | (long)'a' << 48 | (long)'g' << 40 | (long)'g' << 32 | (long)'e' << 24 | (long)'r' << 16, 
+			(long)'d' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32, 
+			(long)'d' << 56 | (long)'e' << 48 | (long)'g' << 40, 
+			(long)'d' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'t' << 32 | (long)'a' << 24, 
+			(long)'d' << 56 | (long)'i' << 48 | (long)'a' << 40 | (long)'m' << 32 | (long)'s' << 24, 
+			(long)'d' << 56 | (long)'i' << 48 | (long)'v' << 40 | (long)'i' << 32 | (long)'d' << 24 | (long)'e' << 16, 
+			(long)'e' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, 
+			(long)'e' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, 
+			(long)'e' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, 
+			(long)'e' << 56 | (long)'m' << 48 | (long)'p' << 40 | (long)'t' << 32 | (long)'y' << 24, 
+			(long)'e' << 56 | (long)'m' << 48 | (long)'s' << 40 | (long)'p' << 32, 
+			(long)'e' << 56 | (long)'n' << 48 | (long)'s' << 40 | (long)'p' << 32, 
+			(long)'e' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8, 
+			(long)'e' << 56 | (long)'q' << 48 | (long)'u' << 40 | (long)'i' << 32 | (long)'v' << 24, 
+			(long)'e' << 56 | (long)'t' << 48 | (long)'a' << 40, 
+			(long)'e' << 56 | (long)'t' << 48 | (long)'h' << 40, 
+			(long)'e' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, 
+			(long)'e' << 56 | (long)'u' << 48 | (long)'r' << 40 | (long)'o' << 32, 
+			(long)'e' << 56 | (long)'x' << 48 | (long)'i' << 40 | (long)'s' << 32 | (long)'t' << 24, 
+			(long)'f' << 56 | (long)'n' << 48 | (long)'o' << 40 | (long)'f' << 32, 
+			(long)'f' << 56 | (long)'o' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'l' << 24 | (long)'l' << 16, 
+			(long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'c' << 32 | (long)'1' << 24 | (long)'2' << 16, 
+			(long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'c' << 32 | (long)'1' << 24 | (long)'4' << 16, 
+			(long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'c' << 32 | (long)'3' << 24 | (long)'4' << 16, 
+			(long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'s' << 32 | (long)'l' << 24, 
+			(long)'g' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'m' << 32 | (long)'a' << 24, 
+			(long)'g' << 56 | (long)'e' << 48, 
+			(long)'g' << 56 | (long)'t' << 48, 
+			(long)'h' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32, 
+			(long)'h' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32, 
+			(long)'h' << 56 | (long)'e' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'t' << 24 | (long)'s' << 16, 
+			(long)'h' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'l' << 32 | (long)'i' << 24 | (long)'p' << 16, 
+			(long)'i' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, 
+			(long)'i' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, 
+			(long)'i' << 56 | (long)'e' << 48 | (long)'x' << 40 | (long)'c' << 32 | (long)'l' << 24, 
+			(long)'i' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, 
+			(long)'i' << 56 | (long)'m' << 48 | (long)'a' << 40 | (long)'g' << 32 | (long)'e' << 24, 
+			(long)'i' << 56 | (long)'n' << 48 | (long)'f' << 40 | (long)'i' << 32 | (long)'n' << 24, 
+			(long)'i' << 56 | (long)'n' << 48 | (long)'t' << 40, 
+			(long)'i' << 56 | (long)'o' << 48 | (long)'t' << 40 | (long)'a' << 32, 
+			(long)'i' << 56 | (long)'q' << 48 | (long)'u' << 40 | (long)'e' << 32 | (long)'s' << 24 | (long)'t' << 16, 
+			(long)'i' << 56 | (long)'s' << 48 | (long)'i' << 40 | (long)'n' << 32, 
+			(long)'i' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, 
+			(long)'k' << 56 | (long)'a' << 48 | (long)'p' << 40 | (long)'p' << 32 | (long)'a' << 24, 
+			(long)'l' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32, 
+			(long)'l' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'b' << 32 | (long)'d' << 24 | (long)'a' << 16, 
+			(long)'l' << 56 | (long)'a' << 48 | (long)'n' << 40 | (long)'g' << 32, 
+			(long)'l' << 56 | (long)'a' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, 
+			(long)'l' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32, 
+			(long)'l' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'i' << 32 | (long)'l' << 24, 
+			(long)'l' << 56 | (long)'d' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, 
+			(long)'l' << 56 | (long)'e' << 48, 
+			(long)'l' << 56 | (long)'f' << 48 | (long)'l' << 40 | (long)'o' << 32 | (long)'o' << 24 | (long)'r' << 16, 
+			(long)'l' << 56 | (long)'o' << 48 | (long)'w' << 40 | (long)'a' << 32 | (long)'s' << 24 | (long)'t' << 16, 
+			(long)'l' << 56 | (long)'o' << 48 | (long)'z' << 40, 
+			(long)'l' << 56 | (long)'r' << 48 | (long)'m' << 40, 
+			(long)'l' << 56 | (long)'s' << 48 | (long)'a' << 40 | (long)'q' << 32 | (long)'u' << 24 | (long)'o' << 16, 
+			(long)'l' << 56 | (long)'s' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, 
+			(long)'l' << 56 | (long)'t' << 48, 
+			(long)'m' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'r' << 32, 
+			(long)'m' << 56 | (long)'d' << 48 | (long)'a' << 40 | (long)'s' << 32 | (long)'h' << 24, 
+			(long)'m' << 56 | (long)'i' << 48 | (long)'c' << 40 | (long)'r' << 32 | (long)'o' << 24, 
+			(long)'m' << 56 | (long)'i' << 48 | (long)'d' << 40 | (long)'d' << 32 | (long)'o' << 24 | (long)'t' << 16, 
+			(long)'m' << 56 | (long)'i' << 48 | (long)'n' << 40 | (long)'u' << 32 | (long)'s' << 24, 
+			(long)'m' << 56 | (long)'u' << 48, 
+			(long)'n' << 56 | (long)'a' << 48 | (long)'b' << 40 | (long)'l' << 32 | (long)'a' << 24, 
+			(long)'n' << 56 | (long)'b' << 48 | (long)'s' << 40 | (long)'p' << 32, 
+			(long)'n' << 56 | (long)'d' << 48 | (long)'a' << 40 | (long)'s' << 32 | (long)'h' << 24, 
+			(long)'n' << 56 | (long)'e' << 48, 
+			(long)'n' << 56 | (long)'i' << 48, 
+			(long)'n' << 56 | (long)'o' << 48 | (long)'t' << 40, 
+			(long)'n' << 56 | (long)'o' << 48 | (long)'t' << 40 | (long)'i' << 32 | (long)'n' << 24, 
+			(long)'n' << 56 | (long)'s' << 48 | (long)'u' << 40 | (long)'b' << 32, 
+			(long)'n' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, 
+			(long)'n' << 56 | (long)'u' << 48, 
+			(long)'o' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, 
+			(long)'o' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, 
+			(long)'o' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24, 
+			(long)'o' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, 
+			(long)'o' << 56 | (long)'l' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'e' << 24, 
+			(long)'o' << 56 | (long)'m' << 48 | (long)'e' << 40 | (long)'g' << 32 | (long)'a' << 24, 
+			(long)'o' << 56 | (long)'m' << 48 | (long)'i' << 40 | (long)'c' << 32 | (long)'r' << 24 | (long)'o' << 16 | (long)'n' << 8, 
+			(long)'o' << 56 | (long)'p' << 48 | (long)'l' << 40 | (long)'u' << 32 | (long)'s' << 24, 
+			(long)'o' << 56 | (long)'r' << 48, 
+			(long)'o' << 56 | (long)'r' << 48 | (long)'d' << 40 | (long)'f' << 32, 
+			(long)'o' << 56 | (long)'r' << 48 | (long)'d' << 40 | (long)'m' << 32, 
+			(long)'o' << 56 | (long)'s' << 48 | (long)'l' << 40 | (long)'a' << 32 | (long)'s' << 24 | (long)'h' << 16, 
+			(long)'o' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, 
+			(long)'o' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'m' << 32 | (long)'e' << 24 | (long)'s' << 16, 
+			(long)'o' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, 
+			(long)'p' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'a' << 32, 
+			(long)'p' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'t' << 32, 
+			(long)'p' << 56 | (long)'e' << 48 | (long)'r' << 40 | (long)'m' << 32 | (long)'i' << 24 | (long)'l' << 16, 
+			(long)'p' << 56 | (long)'e' << 48 | (long)'r' << 40 | (long)'p' << 32, 
+			(long)'p' << 56 | (long)'h' << 48 | (long)'i' << 40, 
+			(long)'p' << 56 | (long)'i' << 48, 
+			(long)'p' << 56 | (long)'i' << 48 | (long)'v' << 40, 
+			(long)'p' << 56 | (long)'l' << 48 | (long)'u' << 40 | (long)'s' << 32 | (long)'m' << 24 | (long)'n' << 16, 
+			(long)'p' << 56 | (long)'o' << 48 | (long)'u' << 40 | (long)'n' << 32 | (long)'d' << 24, 
+			(long)'p' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'m' << 32 | (long)'e' << 24, 
+			(long)'p' << 56 | (long)'r' << 48 | (long)'o' << 40 | (long)'d' << 32, 
+			(long)'p' << 56 | (long)'r' << 48 | (long)'o' << 40 | (long)'p' << 32, 
+			(long)'p' << 56 | (long)'s' << 48 | (long)'i' << 40, 
+			(long)'q' << 56 | (long)'u' << 48 | (long)'o' << 40 | (long)'t' << 32, 
+			(long)'r' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32, 
+			(long)'r' << 56 | (long)'a' << 48 | (long)'d' << 40 | (long)'i' << 32 | (long)'c' << 24, 
+			(long)'r' << 56 | (long)'a' << 48 | (long)'n' << 40 | (long)'g' << 32, 
+			(long)'r' << 56 | (long)'a' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, 
+			(long)'r' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32, 
+			(long)'r' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'i' << 32 | (long)'l' << 24, 
+			(long)'r' << 56 | (long)'d' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, 
+			(long)'r' << 56 | (long)'e' << 48 | (long)'a' << 40 | (long)'l' << 32, 
+			(long)'r' << 56 | (long)'e' << 48 | (long)'g' << 40, 
+			(long)'r' << 56 | (long)'f' << 48 | (long)'l' << 40 | (long)'o' << 32 | (long)'o' << 24 | (long)'r' << 16, 
+			(long)'r' << 56 | (long)'h' << 48 | (long)'o' << 40, 
+			(long)'r' << 56 | (long)'l' << 48 | (long)'m' << 40, 
+			(long)'r' << 56 | (long)'s' << 48 | (long)'a' << 40 | (long)'q' << 32 | (long)'u' << 24 | (long)'o' << 16, 
+			(long)'r' << 56 | (long)'s' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, 
+			(long)'s' << 56 | (long)'b' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, 
+			(long)'s' << 56 | (long)'c' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'o' << 24 | (long)'n' << 16, 
+			(long)'s' << 56 | (long)'d' << 48 | (long)'o' << 40 | (long)'t' << 32, 
+			(long)'s' << 56 | (long)'e' << 48 | (long)'c' << 40 | (long)'t' << 32, 
+			(long)'s' << 56 | (long)'h' << 48 | (long)'y' << 40, 
+			(long)'s' << 56 | (long)'i' << 48 | (long)'g' << 40 | (long)'m' << 32 | (long)'a' << 24, 
+			(long)'s' << 56 | (long)'i' << 48 | (long)'g' << 40 | (long)'m' << 32 | (long)'a' << 24 | (long)'f' << 16, 
+			(long)'s' << 56 | (long)'i' << 48 | (long)'m' << 40, 
+			(long)'s' << 56 | (long)'p' << 48 | (long)'a' << 40 | (long)'d' << 32 | (long)'e' << 24 | (long)'s' << 16, 
+			(long)'s' << 56 | (long)'u' << 48 | (long)'b' << 40, 
+			(long)'s' << 56 | (long)'u' << 48 | (long)'b' << 40 | (long)'e' << 32, 
+			(long)'s' << 56 | (long)'u' << 48 | (long)'m' << 40, 
+			(long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40, 
+			(long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'1' << 32, 
+			(long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'2' << 32, 
+			(long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'3' << 32, 
+			(long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'e' << 32, 
+			(long)'s' << 56 | (long)'z' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24, 
+			(long)'t' << 56 | (long)'a' << 48 | (long)'u' << 40, 
+			(long)'t' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'r' << 32 | (long)'e' << 24 | (long)'4' << 16, 
+			(long)'t' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'t' << 32 | (long)'a' << 24, 
+			(long)'t' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'t' << 32 | (long)'a' << 24 | (long)'s' << 16 | (long)'y' << 8 | (long)'m' << 0, 
+			(long)'t' << 56 | (long)'h' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'s' << 24 | (long)'p' << 16, 
+			(long)'t' << 56 | (long)'h' << 48 | (long)'o' << 40 | (long)'r' << 32 | (long)'n' << 24, 
+			(long)'t' << 56 | (long)'i' << 48 | (long)'l' << 40 | (long)'d' << 32 | (long)'e' << 24, 
+			(long)'t' << 56 | (long)'i' << 48 | (long)'m' << 40 | (long)'e' << 32 | (long)'s' << 24, 
+			(long)'t' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'d' << 32 | (long)'e' << 24, 
+			(long)'u' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32, 
+			(long)'u' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, 
+			(long)'u' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32, 
+			(long)'u' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, 
+			(long)'u' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, 
+			(long)'u' << 56 | (long)'m' << 48 | (long)'l' << 40, 
+			(long)'u' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'h' << 24, 
+			(long)'u' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8, 
+			(long)'u' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, 
+			(long)'w' << 56 | (long)'e' << 48 | (long)'i' << 40 | (long)'e' << 32 | (long)'r' << 24 | (long)'p' << 16, 
+			(long)'x' << 56 | (long)'i' << 48, 
+			(long)'y' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, 
+			(long)'y' << 56 | (long)'e' << 48 | (long)'n' << 40, 
+			(long)'y' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, 
+			(long)'z' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32, 
+			(long)'z' << 56 | (long)'w' << 48 | (long)'j' << 40, 
+			(long)'z' << 56 | (long)'w' << 48 | (long)'n' << 40 | (long)'j' << 32
+		};
+
+        static readonly char[] entities_values = new char[] {
+			'\u00C6',
+			'\u00C1',
+			'\u00C2',
+			'\u00C0',
+			'\u0391',
+			'\u00C5',
+			'\u00C3',
+			'\u00C4',
+			'\u0392',
+			'\u00C7',
+			'\u03A7',
+			'\u2021',
+			'\u0394',
+			'\u00D0',
+			'\u00C9',
+			'\u00CA',
+			'\u00C8',
+			'\u0395',
+			'\u0397',
+			'\u00CB',
+			'\u0393',
+			'\u00CD',
+			'\u00CE',
+			'\u00CC',
+			'\u0399',
+			'\u00CF',
+			'\u039A',
+			'\u039B',
+			'\u039C',
+			'\u00D1',
+			'\u039D',
+			'\u0152',
+			'\u00D3',
+			'\u00D4',
+			'\u00D2',
+			'\u03A9',
+			'\u039F',
+			'\u00D8',
+			'\u00D5',
+			'\u00D6',
+			'\u03A6',
+			'\u03A0',
+			'\u2033',
+			'\u03A8',
+			'\u03A1',
+			'\u0160',
+			'\u03A3',
+			'\u00DE',
+			'\u03A4',
+			'\u0398',
+			'\u00DA',
+			'\u00DB',
+			'\u00D9',
+			'\u03A5',
+			'\u00DC',
+			'\u039E',
+			'\u00DD',
+			'\u0178',
+			'\u0396',
+			'\u00E1',
+			'\u00E2',
+			'\u00B4',
+			'\u00E6',
+			'\u00E0',
+			'\u2135',
+			'\u03B1',
+			'\u0026',
+			'\u2227',
+			'\u2220',
+			'\u0027',
+			'\u00E5',
+			'\u2248',
+			'\u00E3',
+			'\u00E4',
+			'\u201E',
+			'\u03B2',
+			'\u00A6',
+			'\u2022',
+			'\u2229',
+			'\u00E7',
+			'\u00B8',
+			'\u00A2',
+			'\u03C7',
+			'\u02C6',
+			'\u2663',
+			'\u2245',
+			'\u00A9',
+			'\u21B5',
+			'\u222A',
+			'\u00A4',
+			'\u21D3',
+			'\u2020',
+			'\u2193',
+			'\u00B0',
+			'\u03B4',
+			'\u2666',
+			'\u00F7',
+			'\u00E9',
+			'\u00EA',
+			'\u00E8',
+			'\u2205',
+			'\u2003',
+			'\u2002',
+			'\u03B5',
+			'\u2261',
+			'\u03B7',
+			'\u00F0',
+			'\u00EB',
+			'\u20AC',
+			'\u2203',
+			'\u0192',
+			'\u2200',
+			'\u00BD',
+			'\u00BC',
+			'\u00BE',
+			'\u2044',
+			'\u03B3',
+			'\u2265',
+			'\u003E',
+			'\u21D4',
+			'\u2194',
+			'\u2665',
+			'\u2026',
+			'\u00ED',
+			'\u00EE',
+			'\u00A1',
+			'\u00EC',
+			'\u2111',
+			'\u221E',
+			'\u222B',
+			'\u03B9',
+			'\u00BF',
+			'\u2208',
+			'\u00EF',
+			'\u03BA',
+			'\u21D0',
+			'\u03BB',
+			'\u2329',
+			'\u00AB',
+			'\u2190',
+			'\u2308',
+			'\u201C',
+			'\u2264',
+			'\u230A',
+			'\u2217',
+			'\u25CA',
+			'\u200E',
+			'\u2039',
+			'\u2018',
+			'\u003C',
+			'\u00AF',
+			'\u2014',
+			'\u00B5',
+			'\u00B7',
+			'\u2212',
+			'\u03BC',
+			'\u2207',
+			'\u00A0',
+			'\u2013',
+			'\u2260',
+			'\u220B',
+			'\u00AC',
+			'\u2209',
+			'\u2284',
+			'\u00F1',
+			'\u03BD',
+			'\u00F3',
+			'\u00F4',
+			'\u0153',
+			'\u00F2',
+			'\u203E',
+			'\u03C9',
+			'\u03BF',
+			'\u2295',
+			'\u2228',
+			'\u00AA',
+			'\u00BA',
+			'\u00F8',
+			'\u00F5',
+			'\u2297',
+			'\u00F6',
+			'\u00B6',
+			'\u2202',
+			'\u2030',
+			'\u22A5',
+			'\u03C6',
+			'\u03C0',
+			'\u03D6',
+			'\u00B1',
+			'\u00A3',
+			'\u2032',
+			'\u220F',
+			'\u221D',
+			'\u03C8',
+			'\u0022',
+			'\u21D2',
+			'\u221A',
+			'\u232A',
+			'\u00BB',
+			'\u2192',
+			'\u2309',
+			'\u201D',
+			'\u211C',
+			'\u00AE',
+			'\u230B',
+			'\u03C1',
+			'\u200F',
+			'\u203A',
+			'\u2019',
+			'\u201A',
+			'\u0161',
+			'\u22C5',
+			'\u00A7',
+			'\u00AD',
+			'\u03C3',
+			'\u03C2',
+			'\u223C',
+			'\u2660',
+			'\u2282',
+			'\u2286',
+			'\u2211',
+			'\u2283',
+			'\u00B9',
+			'\u00B2',
+			'\u00B3',
+			'\u2287',
+			'\u00DF',
+			'\u03C4',
+			'\u2234',
+			'\u03B8',
+			'\u03D1',
+			'\u2009',
+			'\u00FE',
+			'\u02DC',
+			'\u00D7',
+			'\u2122',
+			'\u21D1',
+			'\u00FA',
+			'\u2191',
+			'\u00FB',
+			'\u00F9',
+			'\u00A8',
+			'\u03D2',
+			'\u03C5',
+			'\u00FC',
+			'\u2118',
+			'\u03BE',
+			'\u00FD',
+			'\u00A5',
+			'\u00FF',
+			'\u03B6',
+			'\u200D',
+			'\u200C'
+		};
+
+        #region Methods
+
+        static void WriteCharBytes(IList buf, char ch, Encoding e)
+        {
+            if (ch > 255)
+            {
+                foreach (byte b in e.GetBytes(new char[] { ch }))
+                    buf.Add(b);
+            }
+            else
+                buf.Add((byte)ch);
+        }
+
+        public static string UrlDecode(string s, Encoding e)
+        {
+            if (null == s)
+                return null;
+
+            if (s.IndexOf('%') == -1 && s.IndexOf('+') == -1)
+                return s;
+
+            if (e == null)
+                e = Encoding.UTF8;
+
+            long len = s.Length;
+            var bytes = new List<byte>();
+            int xchar;
+            char ch;
+
+            for (int i = 0; i < len; i++)
+            {
+                ch = s[i];
+                if (ch == '%' && i + 2 < len && s[i + 1] != '%')
+                {
+                    if (s[i + 1] == 'u' && i + 5 < len)
+                    {
+                        // unicode hex sequence
+                        xchar = GetChar(s, i + 2, 4);
+                        if (xchar != -1)
+                        {
+                            WriteCharBytes(bytes, (char)xchar, e);
+                            i += 5;
+                        }
+                        else
+                            WriteCharBytes(bytes, '%', e);
+                    }
+                    else if ((xchar = GetChar(s, i + 1, 2)) != -1)
+                    {
+                        WriteCharBytes(bytes, (char)xchar, e);
+                        i += 2;
+                    }
+                    else
+                    {
+                        WriteCharBytes(bytes, '%', e);
+                    }
+                    continue;
+                }
+
+                if (ch == '+')
+                    WriteCharBytes(bytes, ' ', e);
+                else
+                    WriteCharBytes(bytes, ch, e);
+            }
+
+            byte[] buf = bytes.ToArray();
+            bytes = null;
+            return e.GetString(buf);
+
+        }
+
+        static int GetInt(byte b)
+        {
+            char c = (char)b;
+            if (c >= '0' && c <= '9')
+                return c - '0';
+
+            if (c >= 'a' && c <= 'f')
+                return c - 'a' + 10;
+
+            if (c >= 'A' && c <= 'F')
+                return c - 'A' + 10;
+
+            return -1;
+        }
+
+        static int GetChar(string str, int offset, int length)
+        {
+            int val = 0;
+            int end = length + offset;
+            for (int i = offset; i < end; i++)
+            {
+                char c = str[i];
+                if (c > 127)
+                    return -1;
+
+                int current = GetInt((byte)c);
+                if (current == -1)
+                    return -1;
+                val = (val << 4) + current;
+            }
+
+            return val;
+        }
+
+        static bool TryConvertKeyToEntity(string key, out char value)
+        {
+            var token = CalculateKeyValue(key);
+            if (token == 0)
+            {
+                value = '\0';
+                return false;
+            }
+
+            var idx = Array.BinarySearch(entities, token);
+            if (idx < 0)
+            {
+                value = '\0';
+                return false;
+            }
+
+            value = entities_values[idx];
+            return true;
+        }
+
+        static long CalculateKeyValue(string s)
+        {
+            if (s.Length > 8)
+                return 0;
+
+            long key = 0;
+            for (int i = 0; i < s.Length; ++i)
+            {
+                long ch = s[i];
+                if (ch > 'z' || ch < '0')
+                    return 0;
+
+                key |= ch << ((7 - i) * 8);
+            }
+
+            return key;
+        }
+
+        /// <summary>
+        /// Decodes an HTML-encoded string and returns the decoded string.
+        /// </summary>
+        /// <param name="s">The HTML string to decode. </param>
+        /// <returns>The decoded text.</returns>
+        public static string HtmlDecode(string s)
+        {
+            if (s == null)
+                throw new ArgumentNullException("s");
+
+            if (s.IndexOf('&') == -1)
+                return s;
+
+            StringBuilder entity = new StringBuilder();
+            StringBuilder output = new StringBuilder();
+            int len = s.Length;
+            // 0 -> nothing,
+            // 1 -> right after '&'
+            // 2 -> between '&' and ';' but no '#'
+            // 3 -> '#' found after '&' and getting numbers
+            int state = 0;
+            int number = 0;
+            int digit_start = 0;
+            bool hex_number = false;
+
+            for (int i = 0; i < len; i++)
+            {
+                char c = s[i];
+                if (state == 0)
+                {
+                    if (c == '&')
+                    {
+                        entity.Append(c);
+                        state = 1;
+                    }
+                    else
+                    {
+                        output.Append(c);
+                    }
+                    continue;
+                }
+
+                if (c == '&')
+                {
+                    state = 1;
+                    if (digit_start > 0)
+                    {
+                        entity.Append(s, digit_start, i - digit_start);
+                        digit_start = 0;
+                    }
+
+                    output.Append(entity.ToString());
+                    entity.Length = 0;
+                    entity.Append('&');
+                    continue;
+                }
+
+                switch (state)
+                {
+                    case 1:
+                        if (c == ';')
+                        {
+                            state = 0;
+                            output.Append(entity.ToString());
+                            output.Append(c);
+                            entity.Length = 0;
+                            break;
+                        }
+
+                        number = 0;
+                        hex_number = false;
+                        if (c != '#')
+                        {
+                            state = 2;
+                        }
+                        else
+                        {
+                            state = 3;
+                        }
+                        entity.Append(c);
+
+                        break;
+                    case 2:
+                        entity.Append(c);
+                        if (c == ';')
+                        {
+                            string key = entity.ToString();
+                            state = 0;
+                            entity.Length = 0;
+
+                            if (key.Length > 1)
+                            {
+                                var skey = key.Substring(1, key.Length - 2);
+                                if (TryConvertKeyToEntity(skey, out c))
+                                {
+                                    output.Append(c);
+                                    break;
+                                }
+                            }
+
+                            output.Append(key);
+                        }
+
+                        break;
+                    case 3:
+                        if (c == ';')
+                        {
+                            if (number < 0x10000)
+                            {
+                                output.Append((char)number);
+                            }
+                            else
+                            {
+                                output.Append((char)(0xd800 + ((number - 0x10000) >> 10)));
+                                output.Append((char)(0xdc00 + ((number - 0x10000) & 0x3ff)));
+                            }
+                            state = 0;
+                            entity.Length = 0;
+                            digit_start = 0;
+                            break;
+                        }
+
+                        if (c == 'x' || c == 'X' && !hex_number)
+                        {
+                            digit_start = i;
+                            hex_number = true;
+                            break;
+                        }
+
+                        if (Char.IsDigit(c))
+                        {
+                            if (digit_start == 0)
+                                digit_start = i;
+
+                            number = number * (hex_number ? 16 : 10) + ((int)c - '0');
+                            break;
+                        }
+
+                        if (hex_number)
+                        {
+                            if (c >= 'a' && c <= 'f')
+                            {
+                                number = number * 16 + 10 + ((int)c - 'a');
+                                break;
+                            }
+                            if (c >= 'A' && c <= 'F')
+                            {
+                                number = number * 16 + 10 + ((int)c - 'A');
+                                break;
+                            }
+                        }
+
+                        state = 2;
+                        if (digit_start > 0)
+                        {
+                            entity.Append(s, digit_start, i - digit_start);
+                            digit_start = 0;
+                        }
+
+                        entity.Append(c);
+                        break;
+                }
+            }
+
+            if (entity.Length > 0)
+            {
+                output.Append(entity);
+            }
+            else if (digit_start > 0)
+            {
+                output.Append(s, digit_start, s.Length - digit_start);
+            }
+            return output.ToString();
+        }
+
+        public static NameValueCollection ParseQueryString(string query)
+        {
+            return ParseQueryString(query, Encoding.UTF8);
+        }
+
+        public static NameValueCollection ParseQueryString(string query, Encoding encoding)
+        {
+            if (query == null)
+                throw new ArgumentNullException("query");
+            if (encoding == null)
+                throw new ArgumentNullException("encoding");
+            if (query.Length == 0 || (query.Length == 1 && query[0] == '?'))
+                return new NameValueCollection();
+            if (query[0] == '?')
+                query = query.Substring(1);
+
+            NameValueCollection result = new HttpQSCollection();
+            ParseQueryString(query, encoding, result);
+            return result;
+        }
+
+        internal static void ParseQueryString(string query, Encoding encoding, NameValueCollection result)
+        {
+            if (query.Length == 0)
+                return;
+
+            string decoded = HtmlDecode(query);
+            int decodedLength = decoded.Length;
+            int namePos = 0;
+            bool first = true;
+            while (namePos <= decodedLength)
+            {
+                int valuePos = -1, valueEnd = -1;
+                for (int q = namePos; q < decodedLength; q++)
+                {
+                    if (valuePos == -1 && decoded[q] == '=')
+                    {
+                        valuePos = q + 1;
+                    }
+                    else if (decoded[q] == '&')
+                    {
+                        valueEnd = q;
+                        break;
+                    }
+                }
+
+                if (first)
+                {
+                    first = false;
+                    if (decoded[namePos] == '?')
+                        namePos++;
+                }
+
+                string name, value;
+                if (valuePos == -1)
+                {
+                    name = null;
+                    valuePos = namePos;
+                }
+                else
+                {
+                    name = UrlDecode(decoded.Substring(namePos, valuePos - namePos - 1), encoding);
+                }
+                if (valueEnd < 0)
+                {
+                    namePos = -1;
+                    valueEnd = decoded.Length;
+                }
+                else
+                {
+                    namePos = valueEnd + 1;
+                }
+                value = UrlDecode(decoded.Substring(valuePos, valueEnd - valuePos), encoding);
+
+                result.Add(name, value);
+                if (namePos == -1)
+                    break;
+            }
+        }
+        #endregion // Methods
+    }
+}

+ 2 - 1
MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.IO;
 using System.Text;
+using System.Web;
 using Funq;
 using MediaBrowser.Model.Logging;
 using ServiceStack;
@@ -236,7 +237,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
         private NameValueCollectionWrapper queryString;
         public INameValueCollection QueryString
         {
-            get { return queryString ?? (queryString = new NameValueCollectionWrapper(HttpUtility.ParseQueryString(request.Url.Query))); }
+            get { return queryString ?? (queryString = new NameValueCollectionWrapper(MyHttpUtility.ParseQueryString(request.Url.Query))); }
         }
 
         private NameValueCollectionWrapper formData;

+ 1 - 4
MediaBrowser.Server.Implementations/Library/UserManager.cs

@@ -403,10 +403,7 @@ namespace MediaBrowser.Server.Implementations.Library
 
                 try
                 {
-                    _dtoServiceFactory().AttachPrimaryImageAspectRatio(dto, user, new List<ItemFields>
-                    {
-                        ItemFields.PrimaryImageAspectRatio
-                    });
+                    _dtoServiceFactory().AttachPrimaryImageAspectRatio(dto, user);
                 }
                 catch (Exception ex)
                 {

+ 0 - 59
MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/EmbyListings.cs

@@ -1,59 +0,0 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.LiveTv;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.Listings.Emby
-{
-    public class EmbyGuide : IListingsProvider
-    {
-        private readonly IHttpClient _httpClient;
-        private readonly IJsonSerializer _jsonSerializer;
-
-        public EmbyGuide(IHttpClient httpClient, IJsonSerializer jsonSerializer)
-        {
-            _httpClient = httpClient;
-            _jsonSerializer = jsonSerializer;
-        }
-
-        public string Name
-        {
-            get { return "Emby Guide"; }
-        }
-
-        public string Type
-        {
-            get { return "emby"; }
-        }
-
-        public Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelNumber, string channelName, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken)
-        {
-            return GetListingsProvider(info.Country).GetProgramsAsync(info, channelNumber, startDateUtc, endDateUtc, cancellationToken);
-        }
-
-        public Task AddMetadata(ListingsProviderInfo info, List<ChannelInfo> channels, CancellationToken cancellationToken)
-        {
-            return GetListingsProvider(info.Country).AddMetadata(info, channels, cancellationToken);
-        }
-
-        public Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings)
-        {
-            return GetListingsProvider(info.Country).Validate(info, validateLogin, validateListings);
-        }
-
-        public Task<List<NameIdPair>> GetLineups(ListingsProviderInfo info, string country, string location)
-        {
-            return GetListingsProvider(country).GetLineups(country, location);
-        }
-
-        private IEmbyListingProvider GetListingsProvider(string country)
-        {
-            return new EmbyListingsNorthAmerica(_httpClient, _jsonSerializer);
-        }
-    }
-}

+ 0 - 366
MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/EmbyListingsNorthAmerica.cs

@@ -1,366 +0,0 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.LiveTv;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.Listings.Emby
-{
-    public class EmbyListingsNorthAmerica : IEmbyListingProvider
-    {
-        private readonly IHttpClient _httpClient;
-        private readonly IJsonSerializer _jsonSerializer;
-
-        public EmbyListingsNorthAmerica(IHttpClient httpClient, IJsonSerializer jsonSerializer)
-        {
-            _httpClient = httpClient;
-            _jsonSerializer = jsonSerializer;
-        }
-
-        public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelNumber, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken)
-        {
-            channelNumber = NormalizeNumber(channelNumber);
-
-            var url = "https://data.emby.media/service/listings?id=" + info.ListingsId;
-
-            // Normalize
-            startDateUtc = startDateUtc.Date;
-            endDateUtc = startDateUtc.AddDays(7);
-
-            url += "&start=" + startDateUtc.ToString("s", CultureInfo.InvariantCulture) + "Z";
-            url += "&end=" + endDateUtc.ToString("s", CultureInfo.InvariantCulture) + "Z";
-
-            var response = await GetResponse<ListingInfo[]>(url).ConfigureAwait(false);
-
-            return response.Where(i => IncludeInResults(i, channelNumber)).Select(GetProgramInfo).OrderBy(i => i.StartDate);
-        }
-
-        private ProgramInfo GetProgramInfo(ListingInfo info)
-        {
-            var showType = info.showType ?? string.Empty;
-
-            var program = new ProgramInfo
-            {
-                Id = info.listingID.ToString(CultureInfo.InvariantCulture),
-                Name = GetStringValue(info.showName),
-                HomePageUrl = GetStringValue(info.webLink),
-                Overview = info.description,
-                IsHD = info.hd,
-                IsLive = info.live,
-                IsPremiere = info.seasonPremiere || info.seriesPremiere,
-                IsMovie = showType.IndexOf("Movie", StringComparison.OrdinalIgnoreCase) != -1,
-                IsKids = showType.IndexOf("Children", StringComparison.OrdinalIgnoreCase) != -1,
-                IsNews = showType.IndexOf("News", StringComparison.OrdinalIgnoreCase) != -1,
-                IsSports = showType.IndexOf("Sports", StringComparison.OrdinalIgnoreCase) != -1
-            };
-
-            if (!string.IsNullOrWhiteSpace(info.listDateTime))
-            {
-                program.StartDate = DateTime.ParseExact(info.listDateTime, "yyyy'-'MM'-'dd' 'HH':'mm':'ss", CultureInfo.InvariantCulture);
-                program.StartDate = DateTime.SpecifyKind(program.StartDate, DateTimeKind.Utc);
-                program.EndDate = program.StartDate.AddMinutes(info.duration);
-            }
-
-            if (info.starRating > 0)
-            {
-                program.CommunityRating = info.starRating*2;
-            }
-
-            if (!string.IsNullOrWhiteSpace(info.rating))
-            {
-                // They don't have dashes so try to normalize
-                program.OfficialRating = info.rating.Replace("TV", "TV-").Replace("--", "-");
-
-                var invalid = new[] { "N/A", "Approved", "Not Rated" };
-                if (invalid.Contains(program.OfficialRating, StringComparer.OrdinalIgnoreCase))
-                {
-                    program.OfficialRating = null;
-                }
-            }
-
-            if (!string.IsNullOrWhiteSpace(info.year))
-            {
-                program.ProductionYear = int.Parse(info.year, CultureInfo.InvariantCulture);
-            }
-
-            if (info.showID > 0)
-            {
-                program.ShowId = info.showID.ToString(CultureInfo.InvariantCulture);
-            }
-
-            if (info.seriesID > 0)
-            {
-                program.SeriesId = info.seriesID.ToString(CultureInfo.InvariantCulture);
-                program.IsSeries = true;
-                program.IsRepeat = info.repeat;
-
-                program.EpisodeTitle = GetStringValue(info.episodeTitle);
-
-                if (string.Equals(program.Name, program.EpisodeTitle, StringComparison.OrdinalIgnoreCase))
-                {
-                    program.EpisodeTitle = null;
-                }
-            }
-
-            if (info.starRating > 0)
-            {
-                program.CommunityRating = info.starRating * 2;
-            }
-
-            if (string.Equals(info.showName, "Movie", StringComparison.OrdinalIgnoreCase))
-            {
-                // Sometimes the movie title will be in here
-                if (!string.IsNullOrWhiteSpace(info.episodeTitle))
-                {
-                    program.Name = info.episodeTitle;
-                }
-            }
-
-            return program;
-        }
-
-        private string GetStringValue(string s)
-        {
-            return string.IsNullOrWhiteSpace(s) ? null : s;
-        }
-
-        private bool IncludeInResults(ListingInfo info, string itemNumber)
-        {
-            if (string.Equals(itemNumber, NormalizeNumber(info.number), StringComparison.OrdinalIgnoreCase))
-            {
-                return true;
-            }
-
-            var channelNumber = info.channelNumber.ToString(CultureInfo.InvariantCulture);
-            if (info.subChannelNumber > 0)
-            {
-                channelNumber += "." + info.subChannelNumber.ToString(CultureInfo.InvariantCulture);
-            }
-
-            return string.Equals(channelNumber, itemNumber, StringComparison.OrdinalIgnoreCase);
-        }
-
-        public async Task AddMetadata(ListingsProviderInfo info, List<ChannelInfo> channels, CancellationToken cancellationToken)
-        {
-            var response = await GetResponse<LineupDetailResponse>("https://data.emby.media/service/lineups?id=" + info.ListingsId).ConfigureAwait(false);
-
-            foreach (var channel in channels)
-            {
-                var station = response.stations.FirstOrDefault(i =>
-                {
-                    var itemNumber = NormalizeNumber(channel.Number);
-
-                    if (string.Equals(itemNumber, NormalizeNumber(i.number), StringComparison.OrdinalIgnoreCase))
-                    {
-                        return true;
-                    }
-
-                    var channelNumber = i.channelNumber.ToString(CultureInfo.InvariantCulture);
-                    if (i.subChannelNumber > 0)
-                    {
-                        channelNumber += "." + i.subChannelNumber.ToString(CultureInfo.InvariantCulture);
-                    }
-
-                    return string.Equals(channelNumber, itemNumber, StringComparison.OrdinalIgnoreCase);
-                });
-
-                if (station != null)
-                {
-                    //channel.Name = station.name;
-
-                    if (!string.IsNullOrWhiteSpace(station.logoFilename))
-                    {
-                        channel.HasImage = true;
-                        channel.ImageUrl = "http://cdn.tvpassport.com/image/station/100x100/" + station.logoFilename;
-                    }
-                }
-            }
-        }
-
-        private string NormalizeNumber(string number)
-        {
-            return number.Replace('-', '.');
-        }
-
-        public Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings)
-        {
-            return Task.FromResult(true);
-        }
-
-        public async Task<List<NameIdPair>> GetLineups(string country, string location)
-        {
-            // location = postal code
-            var response = await GetResponse<LineupInfo[]>("https://data.emby.media/service/lineups?postalCode=" + location).ConfigureAwait(false);
-
-            return response.Select(i => new NameIdPair
-            {
-                Name = GetName(i),
-                Id = i.lineupID
-
-            }).OrderBy(i => i.Name).ToList();
-        }
-
-        private string GetName(LineupInfo info)
-        {
-            var name = info.lineupName;
-
-            if (string.Equals(info.lineupType, "cab", StringComparison.OrdinalIgnoreCase))
-            {
-                name += " - Cable";
-            }
-            else if (string.Equals(info.lineupType, "sat", StringComparison.OrdinalIgnoreCase))
-            {
-                name += " - SAT";
-            }
-            else if (string.Equals(info.lineupType, "ota", StringComparison.OrdinalIgnoreCase))
-            {
-                name += " - OTA";
-            }
-
-            return name;
-        }
-
-        private async Task<T> GetResponse<T>(string url, Func<string, string> filter = null)
-            where T : class
-        {
-            using (var stream = await _httpClient.Get(new HttpRequestOptions
-            {
-                Url = url,
-                CacheLength = TimeSpan.FromDays(1),
-                CacheMode = CacheMode.Unconditional
-
-            }).ConfigureAwait(false))
-            {
-                using (var reader = new StreamReader(stream))
-                {
-                    var path = await reader.ReadToEndAsync().ConfigureAwait(false);
-
-                    using (var secondStream = await _httpClient.Get(new HttpRequestOptions
-                    {
-                        Url = "https://www.mb3admin.com" + path,
-                        CacheLength = TimeSpan.FromDays(1),
-                        CacheMode = CacheMode.Unconditional
-
-                    }).ConfigureAwait(false))
-                    {
-                        return ParseResponse<T>(secondStream, filter);
-                    }
-                }
-            }
-        }
-
-        private T ParseResponse<T>(Stream response, Func<string,string> filter)
-        {
-            using (var reader = new StreamReader(response))
-            {
-                var json = reader.ReadToEnd();
-
-                if (filter != null)
-                {
-                    json = filter(json);
-                }
-
-                return _jsonSerializer.DeserializeFromString<T>(json);
-            }
-        }
-
-        private class LineupInfo
-        {
-            public string lineupID { get; set; }
-            public string lineupName { get; set; }
-            public string lineupType { get; set; }
-            public string providerID { get; set; }
-            public string providerName { get; set; }
-            public string serviceArea { get; set; }
-            public string country { get; set; }
-        }
-
-        private class Station
-        {
-            public string number { get; set; }
-            public int channelNumber { get; set; }
-            public int subChannelNumber { get; set; }
-            public int stationID { get; set; }
-            public string name { get; set; }
-            public string callsign { get; set; }
-            public string network { get; set; }
-            public string stationType { get; set; }
-            public int NTSC_TSID { get; set; }
-            public int DTV_TSID { get; set; }
-            public string webLink { get; set; }
-            public string logoFilename { get; set; }
-        }
-
-        private class LineupDetailResponse
-        {
-            public string lineupID { get; set; }
-            public string lineupName { get; set; }
-            public string lineupType { get; set; }
-            public string providerID { get; set; }
-            public string providerName { get; set; }
-            public string serviceArea { get; set; }
-            public string country { get; set; }
-            public List<Station> stations { get; set; }
-        }
-
-        private class ListingInfo
-        {
-            public string number { get; set; }
-            public int channelNumber { get; set; }
-            public int subChannelNumber { get; set; }
-            public int stationID { get; set; }
-            public string name { get; set; }
-            public string callsign { get; set; }
-            public string network { get; set; }
-            public string stationType { get; set; }
-            public string webLink { get; set; }
-            public string logoFilename { get; set; }
-            public int listingID { get; set; }
-            public string listDateTime { get; set; }
-            public int duration { get; set; }
-            public int showID { get; set; }
-            public int seriesID { get; set; }
-            public string showName { get; set; }
-            public string episodeTitle { get; set; }
-            public string episodeNumber { get; set; }
-            public int parts { get; set; }
-            public int partNum { get; set; }
-            public bool seriesPremiere { get; set; }
-            public bool seasonPremiere { get; set; }
-            public bool seriesFinale { get; set; }
-            public bool seasonFinale { get; set; }
-            public bool repeat { get; set; }
-            public bool @new { get; set; }
-            public string rating { get; set; }
-            public bool captioned { get; set; }
-            public bool educational { get; set; }
-            public bool blackWhite { get; set; }
-            public bool subtitled { get; set; }
-            public bool live { get; set; }
-            public bool hd { get; set; }
-            public bool descriptiveVideo { get; set; }
-            public bool inProgress { get; set; }
-            public string showTypeID { get; set; }
-            public int breakoutLevel { get; set; }
-            public string showType { get; set; }
-            public string year { get; set; }
-            public string guest { get; set; }
-            public string cast { get; set; }
-            public string director { get; set; }
-            public int starRating { get; set; }
-            public string description { get; set; }
-            public string league { get; set; }
-            public string team1 { get; set; }
-            public string team2 { get; set; }
-            public string @event { get; set; }
-            public string location { get; set; }
-        }
-    }
-}

+ 0 - 18
MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/IEmbyListingProvider.cs

@@ -1,18 +0,0 @@
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.LiveTv;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.Listings.Emby
-{
-    public interface IEmbyListingProvider
-    {
-        Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelNumber, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken);
-        Task AddMetadata(ListingsProviderInfo info, List<ChannelInfo> channels, CancellationToken cancellationToken);
-        Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings);
-        Task<List<NameIdPair>> GetLineups(string country, string location);
-    }
-}

+ 53 - 23
MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs

@@ -114,7 +114,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
             var requestString = _jsonSerializer.SerializeToString(requestList);
             _logger.Debug("Request string for schedules is: " + requestString);
             httpOptions.RequestContent = requestString;
-            using (var response = await Post(httpOptions).ConfigureAwait(false))
+            using (var response = await Post(httpOptions, true, info).ConfigureAwait(false))
             {
                 StreamReader reader = new StreamReader(response.Content);
                 string responseString = reader.ReadToEnd();
@@ -138,7 +138,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
                 var requestBody = "[\"" + string.Join("\", \"", programsID) + "\"]";
                 httpOptions.RequestContent = requestBody;
 
-                using (var innerResponse = await Post(httpOptions).ConfigureAwait(false))
+                using (var innerResponse = await Post(httpOptions, true, info).ConfigureAwait(false))
                 {
                     StreamReader innerReader = new StreamReader(innerResponse.Content);
                     responseString = innerReader.ReadToEnd();
@@ -148,7 +148,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
                             responseString);
                     var programDict = programDetails.ToDictionary(p => p.programID, y => y);
 
-                    var images = await GetImageForPrograms(programDetails.Where(p => p.hasImageArtwork).Select(p => p.programID).ToList(), cancellationToken);
+                    var images = await GetImageForPrograms(info, programDetails.Where(p => p.hasImageArtwork).Select(p => p.programID).ToList(), cancellationToken);
 
                     var schedules = dailySchedules.SelectMany(d => d.programs);
                     foreach (ScheduleDirect.Program schedule in schedules)
@@ -229,7 +229,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
 
             httpOptions.RequestHeaders["token"] = token;
 
-            using (var response = await Get(httpOptions).ConfigureAwait(false))
+            using (var response = await Get(httpOptions, true, info).ConfigureAwait(false))
             {
                 var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Channel>(response);
                 _logger.Info("Found " + root.map.Count() + " channels on the lineup on ScheduleDirect");
@@ -447,7 +447,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
             return url;
         }
 
-        private async Task<List<ScheduleDirect.ShowImages>> GetImageForPrograms(List<string> programIds,
+        private async Task<List<ScheduleDirect.ShowImages>> GetImageForPrograms(
+			ListingsProviderInfo info,
+			List<string> programIds,
            CancellationToken cancellationToken)
         {
             var imageIdString = "[";
@@ -472,7 +474,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
                 TimeoutMs = 60000
             };
             List<ScheduleDirect.ShowImages> images;
-            using (var innerResponse2 = await Post(httpOptions).ConfigureAwait(false))
+            using (var innerResponse2 = await Post(httpOptions, true, info).ConfigureAwait(false))
             {
                 images = _jsonSerializer.DeserializeFromStream<List<ScheduleDirect.ShowImages>>(
                     innerResponse2.Content);
@@ -504,7 +506,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
 
             try
             {
-                using (Stream responce = await Get(options).ConfigureAwait(false))
+				using (Stream responce = await Get(options, false, info).ConfigureAwait(false))
                 {
                     var root = _jsonSerializer.DeserializeFromStream<List<ScheduleDirect.Headends>>(responce);
 
@@ -606,30 +608,58 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
             }
         }
 
-        private async Task<HttpResponseInfo> Post(HttpRequestOptions options)
+		private async Task<HttpResponseInfo> Post(HttpRequestOptions options, 
+			bool enableRetry,
+			ListingsProviderInfo providerInfo)
         {
             try
             {
                 return await _httpClient.Post(options).ConfigureAwait(false);
-            }
-            catch
-            {
-                _tokens.Clear();
-                throw;
-            }
+			}
+			catch (HttpException ex)
+			{
+				_tokens.Clear();
+
+				if (!ex.StatusCode.HasValue || (int)ex.StatusCode.Value >= 500) 
+				{
+					enableRetry = false;
+				}
+
+				if (!enableRetry) {
+					throw;
+				}
+			}
+
+			var newToken = await GetToken (providerInfo, options.CancellationToken).ConfigureAwait (false);
+			options.RequestHeaders ["token"] = newToken;
+			return await Post (options, false, providerInfo).ConfigureAwait (false);
         }
 
-        private async Task<Stream> Get(HttpRequestOptions options)
+		private async Task<Stream> Get(HttpRequestOptions options, 
+			bool enableRetry,
+			ListingsProviderInfo providerInfo)
         {
             try
             {
                 return await _httpClient.Get(options).ConfigureAwait(false);
-            }
-            catch
-            {
-                _tokens.Clear();
-                throw;
-            }
+			}
+			catch (HttpException ex)
+			{
+				_tokens.Clear();
+
+				if (!ex.StatusCode.HasValue || (int)ex.StatusCode.Value >= 500) 
+				{
+					enableRetry = false;
+				}
+
+				if (!enableRetry) {
+					throw;
+				}
+			}
+
+			var newToken = await GetToken (providerInfo, options.CancellationToken).ConfigureAwait (false);
+			options.RequestHeaders ["token"] = newToken;
+			return await Get (options, false, providerInfo).ConfigureAwait (false);
         }
 
         private async Task<string> GetTokenInternal(string username, string password,
@@ -646,7 +676,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
             //_logger.Info("Obtaining token from Schedules Direct from addres: " + httpOptions.Url + " with body " +
             // httpOptions.RequestContent);
 
-            using (var responce = await Post(httpOptions).ConfigureAwait(false))
+			using (var responce = await Post(httpOptions, false, null).ConfigureAwait(false))
             {
                 var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Token>(responce.Content);
                 if (root.message == "OK")
@@ -728,7 +758,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings
 
             try
             {
-                using (var response = await Get(options).ConfigureAwait(false))
+				using (var response = await Get(options, false, null).ConfigureAwait(false))
                 {
                     var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Lineups>(response);
 

+ 1 - 4
MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs

@@ -231,10 +231,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             {
                 dto.ImageTags[ImageType.Primary] = imageTag;
 
-                _dtoService.AttachPrimaryImageAspectRatio(dto, info, new List<ItemFields>
-                    {
-                        ItemFields.PrimaryImageAspectRatio
-                    });
+                _dtoService.AttachPrimaryImageAspectRatio(dto, info);
             }
 
             if (currentProgram != null)

+ 54 - 0
MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp.cs

@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.LiveTv;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Serialization;
+
+namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
+{
+    public class SatIp : BaseTunerHost
+    {
+        public SatIp(IConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder)
+            : base(config, logger, jsonSerializer, mediaEncoder)
+        {
+        }
+
+        protected override Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken)
+        {
+            throw new NotImplementedException();
+        }
+
+        public override string Type
+        {
+            get { return "SatIp"; }
+        }
+
+        protected override Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken)
+        {
+            throw new NotImplementedException();
+        }
+
+        protected override Task<MediaSourceInfo> GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken)
+        {
+            throw new NotImplementedException();
+        }
+
+        protected override Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken)
+        {
+            throw new NotImplementedException();
+        }
+
+        protected override bool IsValidChannelId(string channelId)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 2 - 2
MediaBrowser.Server.Implementations/Localization/Core/ca.json

@@ -71,7 +71,7 @@
     "ViewTypeTvShowSeries": "S\u00e8ries:",
     "ViewTypeTvGenres": "G\u00e8neres",
     "ViewTypeTvFavoriteSeries": "S\u00e8ries Preferides",
-    "ViewTypeTvFavoriteEpisodes": "Episodis preferits",
+    "ViewTypeTvFavoriteEpisodes": "Episodis Preferits",
     "ViewTypeMovieResume": "Resume",
     "ViewTypeMovieLatest": "Darrers",
     "ViewTypeMovieMovies": "Pel\u00b7l\u00edcules",
@@ -173,5 +173,5 @@
     "HeaderWriter": "Escriptors",
     "HeaderParentalRatings": "Parental Ratings",
     "HeaderCommunityRatings": "Qualificacions de la comunitat",
-    "StartupEmbyServerIsLoading": "El servidor d'Emby s'est\u00e0 carregant. Si us plau, torneu-ho a provar de nou en breu."
+    "StartupEmbyServerIsLoading": "El servidor d'Emby s'est&agrave; carregant. Si et plau, tornau-ho a provar de nou en breu."
 }

+ 8 - 9
MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

@@ -43,7 +43,7 @@
   <ItemGroup>
     <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
+      <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath>
     </Reference>
     <Reference Include="Interfaces.IO">
       <HintPath>..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll</HintPath>
@@ -52,15 +52,18 @@
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\packages\MediaBrowser.Naming.1.0.0.41\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll</HintPath>
     </Reference>
+    <Reference Include="MoreLinq">
+      <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>
+    </Reference>
     <Reference Include="Patterns.Logging">
       <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
     </Reference>
     <Reference Include="ServiceStack.Api.Swagger">
       <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Api.Swagger.dll</HintPath>
     </Reference>
-    <Reference Include="SocketHttpListener, Version=1.0.5754.42244, Culture=neutral, processorArchitecture=MSIL">
+    <Reference Include="SocketHttpListener, Version=1.0.5840.28948, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\SocketHttpListener.1.0.0.10\lib\net45\SocketHttpListener.dll</HintPath>
+      <HintPath>..\packages\SocketHttpListener.1.0.0.25\lib\net45\SocketHttpListener.dll</HintPath>
     </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
@@ -70,6 +73,7 @@
     <Reference Include="Microsoft.CSharp" />
     <Reference Include="System.Data" />
     <Reference Include="System.Net" />
+    <Reference Include="System.Runtime.Serialization" />
     <Reference Include="System.Security" />
     <Reference Include="System.Web" />
     <Reference Include="System.Xml" />
@@ -94,9 +98,6 @@
     <Reference Include="Mono.Nat">
       <HintPath>..\packages\Mono.Nat.1.2.24.0\lib\net40\Mono.Nat.dll</HintPath>
     </Reference>
-    <Reference Include="MoreLinq">
-      <HintPath>..\packages\morelinq.1.1.1\lib\net35\MoreLinq.dll</HintPath>
-    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\SharedVersion.cs">
@@ -160,6 +161,7 @@
     <Compile Include="HttpServer\ServerLogFactory.cs" />
     <Compile Include="HttpServer\ServerLogger.cs" />
     <Compile Include="HttpServer\Security\SessionContext.cs" />
+    <Compile Include="HttpServer\SocketSharp\HttpUtility.cs" />
     <Compile Include="HttpServer\SocketSharp\SharpWebSocket.cs" />
     <Compile Include="HttpServer\StreamWriter.cs" />
     <Compile Include="HttpServer\SwaggerService.cs" />
@@ -216,9 +218,6 @@
     <Compile Include="LiveTv\EmbyTV\RecordingHelper.cs" />
     <Compile Include="LiveTv\EmbyTV\SeriesTimerManager.cs" />
     <Compile Include="LiveTv\EmbyTV\TimerManager.cs" />
-    <Compile Include="LiveTv\Listings\Emby\EmbyListings.cs" />
-    <Compile Include="LiveTv\Listings\Emby\EmbyListingsNorthAmerica.cs" />
-    <Compile Include="LiveTv\Listings\Emby\IEmbyListingProvider.cs" />
     <Compile Include="LiveTv\Listings\SchedulesDirect.cs" />
     <Compile Include="LiveTv\Listings\XmlTv.cs" />
     <Compile Include="LiveTv\LiveTvConfigurationFactory.cs" />

+ 1 - 1
MediaBrowser.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs

@@ -72,7 +72,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
 
                 try
                 {
-                    await _installationManager.InstallPackage(i, new Progress<double>(), cancellationToken).ConfigureAwait(false);
+                    await _installationManager.InstallPackage(i, true, new Progress<double>(), cancellationToken).ConfigureAwait(false);
                 }
                 catch (OperationCanceledException)
                 {

+ 3 - 3
MediaBrowser.Server.Implementations/packages.config

@@ -1,10 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
+  <package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
   <package id="Interfaces.IO" version="1.0.0.5" targetFramework="net45" />
   <package id="MediaBrowser.Naming" version="1.0.0.41" targetFramework="net45" />
   <package id="Mono.Nat" version="1.2.24.0" targetFramework="net45" />
-  <package id="morelinq" version="1.1.1" targetFramework="net45" />
+  <package id="morelinq" version="1.4.0" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
-  <package id="SocketHttpListener" version="1.0.0.10" targetFramework="net45" />
+  <package id="SocketHttpListener" version="1.0.0.25" targetFramework="net45" />
 </packages>

+ 1 - 1
MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj

@@ -54,7 +54,7 @@
   <ItemGroup>
     <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
+      <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath>
     </Reference>
     <Reference Include="Mono.Posix, Version=4.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>

+ 1 - 1
MediaBrowser.Server.Mono/Program.cs

@@ -82,7 +82,7 @@ namespace MediaBrowser.Server.Mono
 
             var nativeApp = new NativeApp(options);
 
-            _appHost = new ApplicationHost(appPaths, logManager, options, fileSystem, "MBServer.Mono", nativeApp);
+            _appHost = new ApplicationHost(appPaths, logManager, options, fileSystem, "emby.mono.zip", nativeApp);
 
             if (options.ContainsOption("-v"))
             {

+ 1 - 1
MediaBrowser.Server.Mono/packages.config

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
+  <package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
   <package id="Mono.Posix" version="4.0.0.0" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
 </packages>

+ 82 - 42
MediaBrowser.Server.Startup.Common/ApplicationHost.cs

@@ -91,14 +91,17 @@ using MediaBrowser.Server.Startup.Common.Migrations;
 using MediaBrowser.WebDashboard.Api;
 using MediaBrowser.XbmcMetadata.Providers;
 using System;
+using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
 using System.Linq;
+using System.Net;
 using System.Reflection;
 using System.Threading;
 using System.Threading.Tasks;
 using CommonIO;
+using MediaBrowser.Common.Implementations.Updates;
 
 namespace MediaBrowser.Server.Startup.Common
 {
@@ -204,9 +207,10 @@ namespace MediaBrowser.Server.Startup.Common
         private IPlaylistManager PlaylistManager { get; set; }
 
         private readonly StartupOptions _startupOptions;
-        private readonly string _remotePackageName;
+        private readonly string _releaseAssetFilename;
 
         internal INativeApp NativeApp { get; set; }
+        private Timer _ipAddressCacheTimer;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="ApplicationHost" /> class.
@@ -215,21 +219,23 @@ namespace MediaBrowser.Server.Startup.Common
         /// <param name="logManager">The log manager.</param>
         /// <param name="options">The options.</param>
         /// <param name="fileSystem">The file system.</param>
-        /// <param name="remotePackageName">Name of the remote package.</param>
+        /// <param name="releaseAssetFilename">The release asset filename.</param>
         /// <param name="nativeApp">The native application.</param>
         public ApplicationHost(ServerApplicationPaths applicationPaths,
             ILogManager logManager,
             StartupOptions options,
             IFileSystem fileSystem,
-            string remotePackageName,
+            string releaseAssetFilename,
             INativeApp nativeApp)
             : base(applicationPaths, logManager, fileSystem)
         {
             _startupOptions = options;
-            _remotePackageName = remotePackageName;
+            _releaseAssetFilename = releaseAssetFilename;
             NativeApp = nativeApp;
 
             SetBaseExceptionMessage();
+
+            _ipAddressCacheTimer = new Timer(OnCacheClearTimerFired, null, TimeSpan.FromMinutes(3), TimeSpan.FromMinutes(3));
         }
 
         private Version _version;
@@ -316,6 +322,7 @@ namespace MediaBrowser.Server.Startup.Common
         {
             await base.RunStartupTasks().ConfigureAwait(false);
 
+            Logger.Info("ServerId: {0}", SystemId);
             Logger.Info("Core startup complete");
             HttpServer.GlobalResponse = null;
 
@@ -436,7 +443,7 @@ namespace MediaBrowser.Server.Startup.Common
 
             RegisterSingleInstance<ISearchEngine>(() => new SearchEngine(LogManager, LibraryManager, UserManager));
 
-            HttpServer = ServerFactory.CreateServer(this, LogManager, ServerConfigurationManager, "Emby", "web/index.html");
+            HttpServer = ServerFactory.CreateServer(this, LogManager, ServerConfigurationManager, NetworkManager, "Emby", "web/index.html");
             HttpServer.GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading");
             RegisterSingleInstance(HttpServer, false);
             progress.Report(10);
@@ -515,7 +522,7 @@ namespace MediaBrowser.Server.Startup.Common
             SubtitleManager = new SubtitleManager(LogManager.GetLogger("SubtitleManager"), FileSystemManager, LibraryMonitor, LibraryManager, MediaSourceManager);
             RegisterSingleInstance(SubtitleManager);
 
-            RegisterSingleInstance<IDeviceDiscovery>(new DeviceDiscovery(LogManager.GetLogger("IDeviceDiscovery"), ServerConfigurationManager, this));
+            RegisterSingleInstance<IDeviceDiscovery>(new DeviceDiscovery(LogManager.GetLogger("IDeviceDiscovery"), ServerConfigurationManager, this, NetworkManager));
 
             ChapterManager = new ChapterManager(LibraryManager, LogManager.GetLogger("ChapterManager"), ServerConfigurationManager, ItemRepository);
             RegisterSingleInstance(ChapterManager);
@@ -963,6 +970,10 @@ namespace MediaBrowser.Server.Startup.Common
         {
             get
             {
+				if (!ServerConfigurationManager.Configuration.EnableAutoUpdate) 
+				{
+					return false;
+				}
 #if DEBUG
                 return false;
 #endif
@@ -1108,14 +1119,14 @@ namespace MediaBrowser.Server.Startup.Common
                 try
                 {
                     // Return the first matched address, if found, or the first known local address
-                    var address = LocalIpAddress;
+                    var address = LocalIpAddresses.FirstOrDefault(i => !IPAddress.IsLoopback(i));
 
-                    if (!string.IsNullOrWhiteSpace(address))
+                    if (address != null)
                     {
-                        address = GetLocalApiUrl(address);
+                        return GetLocalApiUrl(address.ToString());
                     }
 
-                    return address;
+                    return null;
                 }
                 catch (Exception ex)
                 {
@@ -1133,40 +1144,71 @@ namespace MediaBrowser.Server.Startup.Common
                 HttpPort.ToString(CultureInfo.InvariantCulture));
         }
 
-        public string LocalIpAddress
+        public List<IPAddress> LocalIpAddresses
         {
             get
             {
-                return HttpServerIpAddresses.FirstOrDefault();
+                var localAddresses = NetworkManager.GetLocalIpAddresses()
+                    .Where(IsIpAddressValid)
+                    .ToList();
+
+                return localAddresses;
             }
         }
 
-        private IEnumerable<string> HttpServerIpAddresses
+        private readonly ConcurrentDictionary<string, bool> _validAddressResults = new ConcurrentDictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
+        private bool IsIpAddressValid(IPAddress address)
         {
-            get
+            if (IPAddress.IsLoopback(address))
             {
-                var localAddresses = NetworkManager.GetLocalIpAddresses()
-                    .ToList();
+                return true;
+            }
 
-                var httpServerAddresses = HttpServer.LocalEndPoints
-                    .Select(i => i.Split(':').FirstOrDefault())
-                    .Where(i => !string.IsNullOrEmpty(i))
-                    .ToList();
+            var apiUrl = GetLocalApiUrl(address.ToString());
+            apiUrl += "/system/ping";
 
-                // Cross-check the local ip addresses with addresses that have been received on with the http server
-                var matchedAddresses = httpServerAddresses
-                    .Where(i => localAddresses.Contains(i, StringComparer.OrdinalIgnoreCase))
-                    .ToList();
+            bool cachedResult;
+            if (_validAddressResults.TryGetValue(apiUrl, out cachedResult))
+            {
+                return cachedResult;
+            }
+
+            try
+            {
+                using (var response = HttpClient.SendAsync(new HttpRequestOptions
+                {
+                    Url = apiUrl,
+                    LogErrorResponseBody = false,
+                    LogErrors = false,
+                    LogRequest = false
 
-                if (matchedAddresses.Count == 0)
+                }, "POST").Result)
                 {
-                    return localAddresses;
+                    using (var reader = new StreamReader(response.Content))
+                    {
+                        var result = reader.ReadToEnd();
+                        var valid = string.Equals(Name, result, StringComparison.OrdinalIgnoreCase);
+
+                        _validAddressResults.AddOrUpdate(apiUrl, valid, (k, v) => valid);
+                        Logger.Debug("Ping test result to {0}. Success: {1}", apiUrl, valid);
+                        return valid;
+                    }
                 }
+            }
+            catch
+            {
+                Logger.Debug("Ping test result to {0}. Success: {1}", apiUrl, false);
 
-                return matchedAddresses;
+                _validAddressResults.AddOrUpdate(apiUrl, false, (k, v) => false);
+                return false;
             }
         }
 
+        private void OnCacheClearTimerFired(object state)
+        {
+            _validAddressResults.Clear();
+        }
+
         public string FriendlyName
         {
             get
@@ -1263,25 +1305,23 @@ namespace MediaBrowser.Server.Startup.Common
         /// <returns>Task{CheckForUpdateResult}.</returns>
         public override async Task<CheckForUpdateResult> CheckForApplicationUpdate(CancellationToken cancellationToken, IProgress<double> progress)
         {
-            var availablePackages = await InstallationManager.GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false);
-
-            var version = InstallationManager.GetLatestCompatibleVersion(availablePackages, _remotePackageName, null, ApplicationVersion, ConfigurationManager.CommonConfiguration.SystemUpdateLevel);
+            var cacheLength = TimeSpan.FromHours(3);
+            var updateLevel = ConfigurationManager.CommonConfiguration.SystemUpdateLevel;
 
-            var versionObject = version == null || string.IsNullOrWhiteSpace(version.versionStr) ? null : new Version(version.versionStr);
-
-            var isUpdateAvailable = versionObject != null && versionObject > ApplicationVersion;
+            if (updateLevel == PackageVersionClass.Beta)
+            {
+                cacheLength = TimeSpan.FromHours(1);
+            }
+            else if (updateLevel == PackageVersionClass.Dev)
+            {
+                cacheLength = TimeSpan.FromMinutes(5);
+            }
 
-            var result = versionObject != null ?
-                new CheckForUpdateResult { AvailableVersion = versionObject.ToString(), IsUpdateAvailable = isUpdateAvailable, Package = version } :
-                new CheckForUpdateResult { AvailableVersion = ApplicationVersion.ToString(), IsUpdateAvailable = false };
+            var result = await new GithubUpdater(HttpClient, JsonSerializer, cacheLength).CheckForUpdateResult("MediaBrowser", "Emby", ApplicationVersion, updateLevel, _releaseAssetFilename,
+                    "MBServer", "Mbserver.zip", cancellationToken).ConfigureAwait(false);
 
             HasUpdateAvailable = result.IsUpdateAvailable;
 
-            if (result.IsUpdateAvailable)
-            {
-                Logger.Info("New application version is available: {0}", result.AvailableVersion);
-            }
-
             return result;
         }
 
@@ -1294,7 +1334,7 @@ namespace MediaBrowser.Server.Startup.Common
         /// <returns>Task.</returns>
         public override async Task UpdateApplication(PackageVersionInfo package, CancellationToken cancellationToken, IProgress<double> progress)
         {
-            await InstallationManager.InstallPackage(package, progress, cancellationToken).ConfigureAwait(false);
+            await InstallationManager.InstallPackage(package, false, progress, cancellationToken).ConfigureAwait(false);
 
             HasUpdateAvailable = false;
 

+ 1 - 1
MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj

@@ -33,7 +33,7 @@
   <ItemGroup>
     <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath>
+      <HintPath>..\packages\CommonIO.1.0.0.7\lib\net45\CommonIO.dll</HintPath>
     </Reference>
     <Reference Include="Mono.Posix, Version=4.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません