Browse Source

Merge remote-tracking branch 'upstream/master'

Tim Hobbs 11 years ago
parent
commit
38a0af6e86
71 changed files with 4078 additions and 1642 deletions
  1. 3 6
      MediaBrowser.Api/LocalizationService.cs
  2. 2 3
      MediaBrowser.Api/Movies/MoviesService.cs
  3. 4 5
      MediaBrowser.Api/NewsService.cs
  4. 7 12
      MediaBrowser.Api/NotificationsService.cs
  5. 10 12
      MediaBrowser.Api/PackageReviewService.cs
  6. 5 10
      MediaBrowser.Api/PackageService.cs
  7. 64 54
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  8. 2 2
      MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
  9. 2 2
      MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
  10. 30 20
      MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
  11. 6 6
      MediaBrowser.Api/Playback/StreamRequest.cs
  12. 0 10
      MediaBrowser.Api/Playback/StreamState.cs
  13. 7 14
      MediaBrowser.Api/PluginService.cs
  14. 21 1
      MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs
  15. 4 5
      MediaBrowser.Api/SearchService.cs
  16. 10 19
      MediaBrowser.Api/SessionsService.cs
  17. 4 7
      MediaBrowser.Api/SystemService.cs
  18. 6 11
      MediaBrowser.Api/TvShowsService.cs
  19. 9 18
      MediaBrowser.Api/UserService.cs
  20. 11 7
      MediaBrowser.Common/Net/BasePeriodicWebSocketListener.cs
  21. 15 3
      MediaBrowser.Controller/Dlna/CodecProfile.cs
  22. 22 0
      MediaBrowser.Controller/Dlna/ContainerProfile.cs
  23. 5 5
      MediaBrowser.Controller/Dlna/DeviceIdentification.cs
  24. 10 1
      MediaBrowser.Controller/Dlna/DeviceProfile.cs
  25. 3 7
      MediaBrowser.Controller/Dlna/DirectPlayProfile.cs
  26. 7 0
      MediaBrowser.Controller/Dlna/MediaProfile.cs
  27. 18 5
      MediaBrowser.Controller/Dlna/TranscodingProfile.cs
  28. 1 0
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  29. 35 871
      MediaBrowser.Dlna/DlnaManager.cs
  30. 22 3
      MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
  31. 4 1
      MediaBrowser.Dlna/PlayTo/Device.cs
  32. 4 1
      MediaBrowser.Dlna/PlayTo/DeviceInfo.cs
  33. 87 13
      MediaBrowser.Dlna/PlayTo/DlnaController.cs
  34. 9 11
      MediaBrowser.Dlna/PlayTo/PlayToManager.cs
  35. 18 4
      MediaBrowser.Dlna/PlayTo/PlaylistItem.cs
  36. 117 107
      MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs
  37. 3 3
      MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs
  38. 31 158
      MediaBrowser.Dlna/PlayTo/StreamHelper.cs
  39. 59 0
      MediaBrowser.Dlna/Profiles/DefaultProfile.cs
  40. 27 0
      MediaBrowser.Dlna/Profiles/DenonAvrProfile.cs
  41. 192 0
      MediaBrowser.Dlna/Profiles/LgTvProfile.cs
  42. 33 0
      MediaBrowser.Dlna/Profiles/LinksysDMA2100Profile.cs
  43. 186 0
      MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs
  44. 315 0
      MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs
  45. 19 0
      MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs
  46. 35 0
      MediaBrowser.Dlna/Profiles/SonyBlurayPlayerProfile.cs
  47. 286 0
      MediaBrowser.Dlna/Profiles/SonyBravia2010Profile.cs
  48. 304 0
      MediaBrowser.Dlna/Profiles/SonyBravia2011Profile.cs
  49. 246 0
      MediaBrowser.Dlna/Profiles/SonyBravia2012Profile.cs
  50. 264 0
      MediaBrowser.Dlna/Profiles/SonyBravia2013Profile.cs
  51. 233 0
      MediaBrowser.Dlna/Profiles/SonyPs3Profile.cs
  52. 241 0
      MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs
  53. 312 0
      MediaBrowser.Dlna/Profiles/Xbox360Profile.cs
  54. 54 0
      MediaBrowser.Dlna/Profiles/XboxOneProfile.cs
  55. 115 0
      MediaBrowser.Dlna/Server/DlnaServerEntryPoint.cs
  56. 164 0
      MediaBrowser.Dlna/Server/Headers.cs
  57. 16 0
      MediaBrowser.Dlna/Server/RawHeaders.cs
  58. 260 0
      MediaBrowser.Dlna/Server/SsdpHandler.cs
  59. 28 0
      MediaBrowser.Dlna/Server/UpnpDevice.cs
  60. 1 1
      MediaBrowser.Model/ApiClient/IApiClient.cs
  61. 5 1
      MediaBrowser.Model/Configuration/DlnaOptions.cs
  62. 2 66
      MediaBrowser.Model/Dto/StreamOptions.cs
  63. 3 101
      MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs
  64. 48 44
      MediaBrowser.Model/LiveTv/TimerInfoDto.cs
  65. 1 1
      MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
  66. 2 2
      MediaBrowser.ServerApplication/ApplicationHost.cs
  67. 3 3
      MediaBrowser.WebDashboard/ApiClient.js
  68. 1 1
      MediaBrowser.WebDashboard/packages.config
  69. 2 2
      Nuget/MediaBrowser.Common.Internal.nuspec
  70. 1 1
      Nuget/MediaBrowser.Common.nuspec
  71. 2 2
      Nuget/MediaBrowser.Server.Core.nuspec

+ 3 - 6
MediaBrowser.Api/LocalizationService.cs

@@ -10,8 +10,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class GetCultures
     /// </summary>
-    [Route("/Localization/Cultures", "GET")]
-    [Api(Description = "Gets known cultures")]
+    [Route("/Localization/Cultures", "GET", Summary = "Gets known cultures")]
     public class GetCultures : IReturn<List<CultureDto>>
     {
     }
@@ -19,8 +18,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class GetCountries
     /// </summary>
-    [Route("/Localization/Countries", "GET")]
-    [Api(Description = "Gets known countries")]
+    [Route("/Localization/Countries", "GET", Summary = "Gets known countries")]
     public class GetCountries : IReturn<List<CountryInfo>>
     {
     }
@@ -28,8 +26,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class ParentalRatings
     /// </summary>
-    [Route("/Localization/ParentalRatings", "GET")]
-    [Api(Description = "Gets known parental ratings")]
+    [Route("/Localization/ParentalRatings", "GET", Summary = "Gets known parental ratings")]
     public class GetParentalRatings : IReturn<List<ParentalRating>>
     {
     }

+ 2 - 3
MediaBrowser.Api/Movies/MoviesService.cs

@@ -117,10 +117,9 @@ namespace MediaBrowser.Api.Movies
 
         public object Get(GetMovieRecommendations request)
         {
-            var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null;
+            var user = _userManager.GetUserById(request.UserId.Value);
 
-            var folder = user.RootFolder;
-            var movies = folder.RecursiveChildren.OfType<Movie>().ToList();
+            var movies = user.RootFolder.GetRecursiveChildren(user).OfType<Movie>().ToList();
 
             var result = GetRecommendationCategories(user, movies, request.CategoryLimit, request.ItemLimit, request.GetItemFields().ToList());
 

+ 4 - 5
MediaBrowser.Api/NewsService.cs

@@ -5,8 +5,7 @@ using ServiceStack;
 
 namespace MediaBrowser.Api
 {
-    [Route("/News/Product", "GET")]
-    [Api(Description = "Gets the latest product news.")]
+    [Route("/News/Product", "GET", Summary = "Gets the latest product news.")]
     public class GetProductNews : IReturn<QueryResult<NewsItem>>
     {
         /// <summary>
@@ -23,7 +22,7 @@ namespace MediaBrowser.Api
         [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
         public int? Limit { get; set; }
     }
-    
+
     public class NewsService : BaseApiService
     {
         private readonly INewsService _newsService;
@@ -37,8 +36,8 @@ namespace MediaBrowser.Api
         {
             var result = _newsService.GetProductNews(new NewsQuery
             {
-                 StartIndex = request.StartIndex,
-                 Limit = request.Limit
+                StartIndex = request.StartIndex,
+                Limit = request.Limit
 
             });
 

+ 7 - 12
MediaBrowser.Api/NotificationsService.cs

@@ -1,15 +1,14 @@
 using MediaBrowser.Controller.Notifications;
 using MediaBrowser.Model.Notifications;
+using ServiceStack;
 using System;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
-using ServiceStack;
 
 namespace MediaBrowser.Api
 {
-    [Route("/Notifications/{UserId}", "GET")]
-    [Api(Description = "Gets notifications")]
+    [Route("/Notifications/{UserId}", "GET", Summary = "Gets notifications")]
     public class GetNotifications : IReturn<NotificationResult>
     {
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
@@ -25,16 +24,14 @@ namespace MediaBrowser.Api
         public int? Limit { get; set; }
     }
 
-    [Route("/Notifications/{UserId}/Summary", "GET")]
-    [Api(Description = "Gets a notification summary for a user")]
+    [Route("/Notifications/{UserId}/Summary", "GET", Summary = "Gets a notification summary for a user")]
     public class GetNotificationsSummary : IReturn<NotificationsSummary>
     {
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
         public Guid UserId { get; set; }
     }
 
-    [Route("/Notifications/{UserId}", "POST")]
-    [Api(Description = "Adds a notifications")]
+    [Route("/Notifications/{UserId}", "POST", Summary = "Adds a notifications")]
     public class AddUserNotification : IReturn<Notification>
     {
         [ApiMember(Name = "Id", Description = "The Id of the new notification. If unspecified one will be provided.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
@@ -61,9 +58,8 @@ namespace MediaBrowser.Api
         [ApiMember(Name = "Level", Description = "The notification level", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
         public NotificationLevel Level { get; set; }
     }
-    
-    [Route("/Notifications/{UserId}/Read", "POST")]
-    [Api(Description = "Marks notifications as read")]
+
+    [Route("/Notifications/{UserId}/Read", "POST", Summary = "Marks notifications as read")]
     public class MarkRead : IReturnVoid
     {
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
@@ -73,8 +69,7 @@ namespace MediaBrowser.Api
         public string Ids { get; set; }
     }
 
-    [Route("/Notifications/{UserId}/Unread", "POST")]
-    [Api(Description = "Marks notifications as unread")]
+    [Route("/Notifications/{UserId}/Unread", "POST", Summary = "Marks notifications as unread")]
     public class MarkUnread : IReturnVoid
     {
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]

+ 10 - 12
MediaBrowser.Api/PackageReviewService.cs

@@ -1,21 +1,20 @@
-using System.Collections.Generic;
-using System.Globalization;
-using System.Net;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Constants;
+using MediaBrowser.Common.Constants;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Serialization;
 using ServiceStack;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
 
 namespace MediaBrowser.Api
 {
     /// <summary>
     /// Class InstallPackage
     /// </summary>
-    [Route("/PackageReviews/{Id}", "POST")]
-    [Api(("Creates or updates a package review"))]
+    [Route("/Packages/Reviews/{Id}", "POST", Summary = "Creates or updates a package review")]
     public class CreateReviewRequest : IReturnVoid
     {
         /// <summary>
@@ -57,8 +56,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class InstallPackage
     /// </summary>
-    [Route("/PackageReviews/{Id}", "GET")]
-    [Api(("Retrieve reviews for a package"))]
+    [Route("/Packages/{Id}/Reviews", "GET", Summary = "Gets reviews for a package")]
     public class ReviewRequest : IReturn<List<PackageReviewInfo>>
     {
         /// <summary>
@@ -114,7 +112,7 @@ namespace MediaBrowser.Api
         public object Get(ReviewRequest request)
         {
             var parms = "?id=" + request.Id;
-            
+
             if (request.MaxRating > 0)
             {
                 parms += "&max=" + request.MaxRating;
@@ -132,7 +130,7 @@ namespace MediaBrowser.Api
                 parms += "&title=true";
             }
 
-            var result = _httpClient.Get(Constants.MbAdminUrl + "/service/packageReview/retrieve"+parms, CancellationToken.None).Result;
+            var result = _httpClient.Get(Constants.MbAdminUrl + "/service/packageReview/retrieve" + parms, CancellationToken.None).Result;
 
             var reviews = _serializer.DeserializeFromStream<List<PackageReviewInfo>>(result);
 

+ 5 - 10
MediaBrowser.Api/PackageService.cs

@@ -14,8 +14,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class GetPackage
     /// </summary>
-    [Route("/Packages/{Name}", "GET")]
-    [Api(("Gets a package, by name or assembly guid"))]
+    [Route("/Packages/{Name}", "GET", Summary = "Gets a package, by name or assembly guid")]
     public class GetPackage : IReturn<PackageInfo>
     {
         /// <summary>
@@ -36,8 +35,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class GetPackages
     /// </summary>
-    [Route("/Packages", "GET")]
-    [Api(("Gets available packages"))]
+    [Route("/Packages", "GET", Summary = "Gets available packages")]
     public class GetPackages : IReturn<List<PackageInfo>>
     {
         /// <summary>
@@ -57,8 +55,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class GetPackageVersionUpdates
     /// </summary>
-    [Route("/Packages/Updates", "GET")]
-    [Api(("Gets available package updates for currently installed packages"))]
+    [Route("/Packages/Updates", "GET", Summary = "Gets available package updates for currently installed packages")]
     public class GetPackageVersionUpdates : IReturn<List<PackageVersionInfo>>
     {
         /// <summary>
@@ -72,8 +69,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class InstallPackage
     /// </summary>
-    [Route("/Packages/Installed/{Name}", "POST")]
-    [Api(("Installs a package"))]
+    [Route("/Packages/Installed/{Name}", "POST", Summary = "Installs a package")]
     public class InstallPackage : IReturnVoid
     {
         /// <summary>
@@ -108,8 +104,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class CancelPackageInstallation
     /// </summary>
-    [Route("/Packages/Installing/{Id}", "DELETE")]
-    [Api(("Cancels a package installation"))]
+    [Route("/Packages/Installing/{Id}", "DELETE", Summary = "Cancels a package installation")]
     public class CancelPackageInstallation : IReturnVoid
     {
         /// <summary>

+ 64 - 54
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -9,7 +9,6 @@ using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Drawing;
-using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Library;
@@ -735,14 +734,21 @@ namespace MediaBrowser.Api.Playback
         {
             if (audioStream != null)
             {
-                if (audioStream.Channels > 2 && request.AudioCodec.HasValue)
+                if (audioStream.Channels > 2 && string.Equals(request.AudioCodec, "wma", StringComparison.OrdinalIgnoreCase))
                 {
-                    if (request.AudioCodec.Value == AudioCodecs.Wma)
-                    {
-                        // wmav2 currently only supports two channel output
-                        return 2;
-                    }
+                    // wmav2 currently only supports two channel output
+                    return 2;
+                }
+            }
+
+            if (request.MaxAudioChannels.HasValue)
+            {
+                if (audioStream != null && audioStream.Channels.HasValue)
+                {
+                    return Math.Min(request.MaxAudioChannels.Value, audioStream.Channels.Value);
                 }
+
+                return request.MaxAudioChannels.Value;
             }
 
             return request.AudioChannels;
@@ -768,26 +774,26 @@ namespace MediaBrowser.Api.Playback
         {
             var codec = request.AudioCodec;
 
-            if (codec.HasValue)
+            if (!string.IsNullOrEmpty(codec))
             {
-                if (codec == AudioCodecs.Aac)
+                if (string.Equals(codec, "aac", StringComparison.OrdinalIgnoreCase))
                 {
                     return "aac -strict experimental";
                 }
-                if (codec == AudioCodecs.Mp3)
+                if (string.Equals(codec, "mp3", StringComparison.OrdinalIgnoreCase))
                 {
                     return "libmp3lame";
                 }
-                if (codec == AudioCodecs.Vorbis)
+                if (string.Equals(codec, "vorbis", StringComparison.OrdinalIgnoreCase))
                 {
                     return "libvorbis";
                 }
-                if (codec == AudioCodecs.Wma)
+                if (string.Equals(codec, "wma", StringComparison.OrdinalIgnoreCase))
                 {
                     return "wmav2";
                 }
 
-                return codec.ToString().ToLower();
+                return codec.ToLower();
             }
 
             return "copy";
@@ -802,26 +808,26 @@ namespace MediaBrowser.Api.Playback
         {
             var codec = request.VideoCodec;
 
-            if (codec.HasValue)
+            if (!string.IsNullOrEmpty(codec))
             {
-                if (codec == VideoCodecs.H264)
+                if (string.Equals(codec, "h264", StringComparison.OrdinalIgnoreCase))
                 {
                     return "libx264";
                 }
-                if (codec == VideoCodecs.Vpx)
+                if (string.Equals(codec, "vpx", StringComparison.OrdinalIgnoreCase))
                 {
                     return "libvpx";
                 }
-                if (codec == VideoCodecs.Wmv)
+                if (string.Equals(codec, "wmv", StringComparison.OrdinalIgnoreCase))
                 {
                     return "msmpeg4";
                 }
-                if (codec == VideoCodecs.Theora)
+                if (string.Equals(codec, "theora", StringComparison.OrdinalIgnoreCase))
                 {
                     return "libtheora";
                 }
 
-                return codec.ToString().ToLower();
+                return codec.ToLower();
             }
 
             return "copy";
@@ -1201,77 +1207,81 @@ namespace MediaBrowser.Api.Playback
 
                 if (i == 0)
                 {
-                    request.DeviceId = val;
+                    // Device profile name
                 }
                 else if (i == 1)
                 {
-                    request.Static = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
+                    request.DeviceId = val;
                 }
                 else if (i == 2)
+                {
+                    request.MediaSourceId = val;
+                }
+                else if (i == 3)
+                {
+                    request.Static = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
+                }
+                else if (i == 4)
                 {
                     if (videoRequest != null)
                     {
-                        videoRequest.VideoCodec = (VideoCodecs)Enum.Parse(typeof(VideoCodecs), val, true);
+                        videoRequest.VideoCodec = val;
                     }
                 }
-                else if (i == 3)
+                else if (i == 5)
                 {
-                    request.AudioCodec = (AudioCodecs)Enum.Parse(typeof(AudioCodecs), val, true);
+                    request.AudioCodec = val;
                 }
-                else if (i == 4)
+                else if (i == 6)
                 {
                     if (videoRequest != null)
                     {
                         videoRequest.AudioStreamIndex = int.Parse(val, UsCulture);
                     }
                 }
-                else if (i == 5)
+                else if (i == 7)
                 {
                     if (videoRequest != null)
                     {
                         videoRequest.SubtitleStreamIndex = int.Parse(val, UsCulture);
                     }
                 }
-                else if (i == 6)
+                else if (i == 8)
                 {
                     if (videoRequest != null)
                     {
                         videoRequest.VideoBitRate = int.Parse(val, UsCulture);
                     }
                 }
-                else if (i == 7)
+                else if (i == 9)
                 {
                     request.AudioBitRate = int.Parse(val, UsCulture);
                 }
-                else if (i == 8)
+                else if (i == 10)
                 {
-                    request.AudioChannels = int.Parse(val, UsCulture);
+                    request.MaxAudioChannels = int.Parse(val, UsCulture);
                 }
-                else if (i == 9)
+                else if (i == 11)
                 {
                     if (videoRequest != null)
                     {
                         request.StartTimeTicks = long.Parse(val, UsCulture);
                     }
                 }
-                else if (i == 10)
+                else if (i == 12)
                 {
                     if (videoRequest != null)
                     {
                         videoRequest.Profile = val;
                     }
                 }
-                else if (i == 11)
+                else if (i == 13)
                 {
                     if (videoRequest != null)
                     {
                         videoRequest.Level = val;
                     }
                 }
-                else if (i == 12)
-                {
-                    request.ForcedMimeType = val;
-                }
             }
         }
 
@@ -1297,7 +1307,7 @@ namespace MediaBrowser.Api.Playback
 
             var url = Request.PathInfo;
 
-            if (!request.AudioCodec.HasValue)
+            if (string.IsNullOrEmpty(request.AudioCodec))
             {
                 request.AudioCodec = InferAudioCodec(url);
             }
@@ -1425,7 +1435,7 @@ namespace MediaBrowser.Api.Playback
 
             if (videoRequest != null)
             {
-                if (!videoRequest.VideoCodec.HasValue)
+                if (string.IsNullOrEmpty(videoRequest.VideoCodec))
                 {
                     videoRequest.VideoCodec = InferVideoCodec(url);
                 }
@@ -1532,41 +1542,41 @@ namespace MediaBrowser.Api.Playback
         /// </summary>
         /// <param name="url">The URL.</param>
         /// <returns>System.Nullable{AudioCodecs}.</returns>
-        private AudioCodecs? InferAudioCodec(string url)
+        private string InferAudioCodec(string url)
         {
             var ext = Path.GetExtension(url);
 
             if (string.Equals(ext, ".mp3", StringComparison.OrdinalIgnoreCase))
             {
-                return AudioCodecs.Mp3;
+                return "mp3";
             }
             if (string.Equals(ext, ".aac", StringComparison.OrdinalIgnoreCase))
             {
-                return AudioCodecs.Aac;
+                return "aac";
             }
             if (string.Equals(ext, ".wma", StringComparison.OrdinalIgnoreCase))
             {
-                return AudioCodecs.Wma;
+                return "wma";
             }
             if (string.Equals(ext, ".ogg", StringComparison.OrdinalIgnoreCase))
             {
-                return AudioCodecs.Vorbis;
+                return "vorbis";
             }
             if (string.Equals(ext, ".oga", StringComparison.OrdinalIgnoreCase))
             {
-                return AudioCodecs.Vorbis;
+                return "vorbis";
             }
             if (string.Equals(ext, ".ogv", StringComparison.OrdinalIgnoreCase))
             {
-                return AudioCodecs.Vorbis;
+                return "vorbis";
             }
             if (string.Equals(ext, ".webm", StringComparison.OrdinalIgnoreCase))
             {
-                return AudioCodecs.Vorbis;
+                return "vorbis";
             }
             if (string.Equals(ext, ".webma", StringComparison.OrdinalIgnoreCase))
             {
-                return AudioCodecs.Vorbis;
+                return "vorbis";
             }
 
             return null;
@@ -1577,28 +1587,28 @@ namespace MediaBrowser.Api.Playback
         /// </summary>
         /// <param name="url">The URL.</param>
         /// <returns>System.Nullable{VideoCodecs}.</returns>
-        private VideoCodecs? InferVideoCodec(string url)
+        private string InferVideoCodec(string url)
         {
             var ext = Path.GetExtension(url);
 
             if (string.Equals(ext, ".asf", StringComparison.OrdinalIgnoreCase))
             {
-                return VideoCodecs.Wmv;
+                return "wmv";
             }
             if (string.Equals(ext, ".webm", StringComparison.OrdinalIgnoreCase))
             {
-                return VideoCodecs.Vpx;
+                return "vpx";
             }
             if (string.Equals(ext, ".ogg", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ogv", StringComparison.OrdinalIgnoreCase))
             {
-                return VideoCodecs.Theora;
+                return "theora";
             }
             if (string.Equals(ext, ".m3u8", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ts", StringComparison.OrdinalIgnoreCase))
             {
-                return VideoCodecs.H264;
+                return "h264";
             }
 
-            return VideoCodecs.Copy;
+            return "copy";
         }
     }
 }

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

@@ -91,11 +91,11 @@ namespace MediaBrowser.Api.Playback.Hls
         {
             var state = GetState(request, CancellationToken.None).Result;
 
-            if (!state.VideoRequest.VideoBitRate.HasValue && (!state.VideoRequest.VideoCodec.HasValue || state.VideoRequest.VideoCodec.Value != VideoCodecs.Copy))
+            if (!state.VideoRequest.VideoBitRate.HasValue && (string.IsNullOrEmpty(state.VideoRequest.VideoCodec) || !string.Equals(state.VideoRequest.VideoCodec, "copy", StringComparison.OrdinalIgnoreCase)))
             {
                 throw new ArgumentException("A video bitrate is required");
             }
-            if (!state.Request.AudioBitRate.HasValue && (!state.Request.AudioCodec.HasValue || state.Request.AudioCodec.Value != AudioCodecs.Copy))
+            if (!state.Request.AudioBitRate.HasValue && (string.IsNullOrEmpty(state.Request.AudioCodec) || !string.Equals(state.Request.AudioCodec, "copy", StringComparison.OrdinalIgnoreCase)))
             {
                 throw new ArgumentException("An audio bitrate is required");
             }

+ 2 - 2
MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs

@@ -136,11 +136,11 @@ namespace MediaBrowser.Api.Playback.Hls
         {
             var state = await GetState(request, CancellationToken.None).ConfigureAwait(false);
 
-            if (!state.VideoRequest.VideoBitRate.HasValue && (!state.VideoRequest.VideoCodec.HasValue || state.VideoRequest.VideoCodec.Value != VideoCodecs.Copy))
+            if (!state.VideoRequest.VideoBitRate.HasValue && (string.IsNullOrEmpty(state.VideoRequest.VideoCodec) || !string.Equals(state.VideoRequest.VideoCodec, "copy", StringComparison.OrdinalIgnoreCase)))
             {
                 throw new ArgumentException("A video bitrate is required");
             }
-            if (!state.Request.AudioBitRate.HasValue && (!state.Request.AudioCodec.HasValue || state.Request.AudioCodec.Value != AudioCodecs.Copy))
+            if (!state.Request.AudioBitRate.HasValue && (string.IsNullOrEmpty(state.Request.AudioCodec) || !string.Equals(state.Request.AudioCodec, "copy", StringComparison.OrdinalIgnoreCase)))
             {
                 throw new ArgumentException("An audio bitrate is required");
             }

+ 30 - 20
MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs

@@ -50,39 +50,49 @@ namespace MediaBrowser.Api.Playback.Progressive
             var videoRequest = state.Request as VideoStreamRequest;
 
             // Try to infer based on the desired video codec
-            if (videoRequest != null && videoRequest.VideoCodec.HasValue)
+            if (videoRequest != null && !string.IsNullOrEmpty(videoRequest.VideoCodec))
             {
                 if (state.IsInputVideo)
                 {
-                    switch (videoRequest.VideoCodec.Value)
+                    if (string.Equals(videoRequest.VideoCodec, "h264", StringComparison.OrdinalIgnoreCase))
                     {
-                        case VideoCodecs.H264:
-                            return ".ts";
-                        case VideoCodecs.Theora:
-                            return ".ogv";
-                        case VideoCodecs.Vpx:
-                            return ".webm";
-                        case VideoCodecs.Wmv:
-                            return ".asf";
+                        return ".ts";
+                    }
+                    if (string.Equals(videoRequest.VideoCodec, "theora", StringComparison.OrdinalIgnoreCase))
+                    {
+                        return ".ogv";
+                    }
+                    if (string.Equals(videoRequest.VideoCodec, "vpx", StringComparison.OrdinalIgnoreCase))
+                    {
+                        return ".webm";
+                    }
+                    if (string.Equals(videoRequest.VideoCodec, "wmv", StringComparison.OrdinalIgnoreCase))
+                    {
+                        return ".asf";
                     }
                 }
             }
 
             // Try to infer based on the desired audio codec
-            if (state.Request.AudioCodec.HasValue)
+            if (!string.IsNullOrEmpty(state.Request.AudioCodec))
             {
                 if (!state.IsInputVideo)
                 {
-                    switch (state.Request.AudioCodec.Value)
+                    if (string.Equals("aac", state.Request.AudioCodec, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return ".aac";
+                    }
+                    if (string.Equals("mp3", state.Request.AudioCodec, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return ".mp3";
+                    }
+                    if (string.Equals("vorbis", state.Request.AudioCodec, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return ".ogg";
+                    }
+                    if (string.Equals("wma", state.Request.AudioCodec, StringComparison.OrdinalIgnoreCase))
                     {
-                        case AudioCodecs.Aac:
-                            return ".aac";
-                        case AudioCodecs.Mp3:
-                            return ".mp3";
-                        case AudioCodecs.Vorbis:
-                            return ".ogg";
-                        case AudioCodecs.Wma:
-                            return ".wma";
+                        return ".wma";
                     }
                 }
             }

+ 6 - 6
MediaBrowser.Api/Playback/StreamRequest.cs

@@ -1,5 +1,4 @@
-using MediaBrowser.Model.Dto;
-using ServiceStack;
+using ServiceStack;
 
 namespace MediaBrowser.Api.Playback
 {
@@ -26,7 +25,7 @@ namespace MediaBrowser.Api.Playback
         /// </summary>
         /// <value>The audio codec.</value>
         [ApiMember(Name = "AudioCodec", Description = "Optional. Specify a audio codec to encode to, e.g. mp3. If omitted the server will auto-select using the url's extension. Options: aac, mp3, vorbis, wma.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public AudioCodecs? AudioCodec { get; set; }
+        public string AudioCodec { get; set; }
 
         /// <summary>
         /// Gets or sets the start time ticks.
@@ -49,6 +48,9 @@ namespace MediaBrowser.Api.Playback
         [ApiMember(Name = "AudioChannels", Description = "Optional. Specify a specific number of audio channels to encode to, e.g. 2", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
         public int? AudioChannels { get; set; }
 
+        [ApiMember(Name = "MaxAudioChannels", Description = "Optional. Specify a maximum number of audio channels to encode to, e.g. 2", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+        public int? MaxAudioChannels { get; set; }
+        
         /// <summary>
         /// Gets or sets the audio sample rate.
         /// </summary>
@@ -69,8 +71,6 @@ namespace MediaBrowser.Api.Playback
         public bool ThrowDebugError { get; set; }
 
         public string Params { get; set; }
-
-        public string ForcedMimeType { get; set; }
     }
 
     public class VideoStreamRequest : StreamRequest
@@ -80,7 +80,7 @@ namespace MediaBrowser.Api.Playback
         /// </summary>
         /// <value>The video codec.</value>
         [ApiMember(Name = "VideoCodec", Description = "Optional. Specify a video codec to encode to, e.g. h264. If omitted the server will auto-select using the url's extension. Options: h264, mpeg4, theora, vpx, wmv.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public VideoCodecs? VideoCodec { get; set; }
+        public string VideoCodec { get; set; }
 
         /// <summary>
         /// Gets or sets the video bit rate.

+ 0 - 10
MediaBrowser.Api/Playback/StreamState.cs

@@ -79,16 +79,6 @@ namespace MediaBrowser.Api.Playback
 
         public string GetMimeType(string outputPath)
         {
-            if (!string.IsNullOrWhiteSpace(Request.ForcedMimeType))
-            {
-                if (VideoRequest == null)
-                {
-                    return "audio/" + Request.ForcedMimeType;
-                }
-
-                return "video/" + Request.ForcedMimeType;
-            }
-
             return MimeTypes.GetMimeType(outputPath);
         }
     }

+ 7 - 14
MediaBrowser.Api/PluginService.cs

@@ -18,8 +18,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class Plugins
     /// </summary>
-    [Route("/Plugins", "GET")]
-    [Api(("Gets a list of currently installed plugins"))]
+    [Route("/Plugins", "GET", Summary = "Gets a list of currently installed plugins")]
     public class GetPlugins : IReturn<List<PluginInfo>>
     {
     }
@@ -27,8 +26,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class UninstallPlugin
     /// </summary>
-    [Route("/Plugins/{Id}", "DELETE")]
-    [Api(("Uninstalls a plugin"))]
+    [Route("/Plugins/{Id}", "DELETE", Summary = "Uninstalls a plugin")]
     public class UninstallPlugin : IReturnVoid
     {
         /// <summary>
@@ -42,8 +40,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class GetPluginConfiguration
     /// </summary>
-    [Route("/Plugins/{Id}/Configuration", "GET")]
-    [Api(("Gets a plugin's configuration"))]
+    [Route("/Plugins/{Id}/Configuration", "GET", Summary = "Gets a plugin's configuration")]
     public class GetPluginConfiguration
     {
         /// <summary>
@@ -57,8 +54,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class UpdatePluginConfiguration
     /// </summary>
-    [Route("/Plugins/{Id}/Configuration", "POST")]
-    [Api(("Updates a plugin's configuration"))]
+    [Route("/Plugins/{Id}/Configuration", "POST", Summary = "Updates a plugin's configuration")]
     public class UpdatePluginConfiguration : IRequiresRequestStream, IReturnVoid
     {
         /// <summary>
@@ -78,8 +74,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class GetPluginSecurityInfo
     /// </summary>
-    [Route("/Plugins/SecurityInfo", "GET")]
-    [Api(("Gets plugin registration information"))]
+    [Route("/Plugins/SecurityInfo", "GET", Summary = "Gets plugin registration information")]
     public class GetPluginSecurityInfo : IReturn<PluginSecurityInfo>
     {
     }
@@ -87,14 +82,12 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class UpdatePluginSecurityInfo
     /// </summary>
-    [Route("/Plugins/SecurityInfo", "POST")]
-    [Api("Updates plugin registration information")]
+    [Route("/Plugins/SecurityInfo", "POST", Summary = "Updates plugin registration information")]
     public class UpdatePluginSecurityInfo : PluginSecurityInfo, IReturnVoid
     {
     }
 
-    [Route("/Plugins/RegistrationRecords/{Name}", "GET")]
-    [Api("Gets registration status for a feature")]
+    [Route("/Plugins/RegistrationRecords/{Name}", "GET", Summary = "Gets registration status for a feature")]
     public class GetRegistrationStatus
     {
         [ApiMember(Name = "Name", Description = "Feature Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]

+ 21 - 1
MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs

@@ -39,6 +39,8 @@ namespace MediaBrowser.Api.ScheduledTasks
             TaskManager = taskManager;
         }
 
+        private bool _lastResponseHadTasksRunning = true;
+
         /// <summary>
         /// Gets the data to send.
         /// </summary>
@@ -46,7 +48,25 @@ namespace MediaBrowser.Api.ScheduledTasks
         /// <returns>Task{IEnumerable{TaskInfo}}.</returns>
         protected override Task<IEnumerable<TaskInfo>> GetDataToSend(object state)
         {
-            return Task.FromResult(TaskManager.ScheduledTasks
+            var tasks = TaskManager.ScheduledTasks.ToList();
+
+            var anyRunning = tasks.Any(i => i.State != TaskState.Idle);
+
+            if (anyRunning)
+            {
+                _lastResponseHadTasksRunning = true;
+            }
+            else
+            {
+                if (!_lastResponseHadTasksRunning)
+                {
+                    return Task.FromResult<IEnumerable<TaskInfo>>(null);
+                }
+
+                _lastResponseHadTasksRunning = false;
+            }
+
+            return Task.FromResult(tasks
                 .OrderBy(i => i.Name)
                 .Select(ScheduledTaskHelpers.GetTaskInfo)
                 .Where(i => !i.IsHidden));

+ 4 - 5
MediaBrowser.Api/SearchService.cs

@@ -1,5 +1,4 @@
-using System;
-using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
@@ -8,6 +7,7 @@ using MediaBrowser.Controller.Library;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Search;
 using ServiceStack;
+using System;
 using System.Linq;
 using System.Threading.Tasks;
 
@@ -16,8 +16,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class GetSearchHints
     /// </summary>
-    [Route("/Search/Hints", "GET")]
-    [Api(Description = "Gets search hints based on a search term")]
+    [Route("/Search/Hints", "GET", Summary = "Gets search hints based on a search term")]
     public class GetSearchHints : IReturn<SearchHintResult>
     {
         /// <summary>
@@ -66,7 +65,7 @@ namespace MediaBrowser.Api
 
         [ApiMember(Name = "IncludeItemTypes", Description = "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
         public string IncludeItemTypes { get; set; }
-        
+
         public GetSearchHints()
         {
             IncludeArtists = true;

+ 10 - 19
MediaBrowser.Api/SessionsService.cs

@@ -14,8 +14,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class GetSessions
     /// </summary>
-    [Route("/Sessions", "GET")]
-    [Api(("Gets a list of sessions"))]
+    [Route("/Sessions", "GET", Summary = "Gets a list of sessions")]
     public class GetSessions : IReturn<List<SessionInfoDto>>
     {
         /// <summary>
@@ -28,15 +27,14 @@ namespace MediaBrowser.Api
         [ApiMember(Name = "ControllableByUserId", Description = "Optional. Filter by sessions that a given user is allowed to remote control.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public Guid? ControllableByUserId { get; set; }
 
-        [ApiMember(Name = "DeviceId", Description = "Optional. Filter by device id.", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+        [ApiMember(Name = "DeviceId", Description = "Optional. Filter by device id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string DeviceId { get; set; }
     }
 
     /// <summary>
     /// Class BrowseTo
     /// </summary>
-    [Route("/Sessions/{Id}/Viewing", "POST")]
-    [Api(("Instructs a session to browse to an item or view"))]
+    [Route("/Sessions/{Id}/Viewing", "POST", Summary = "Instructs a session to browse to an item or view")]
     public class BrowseTo : IReturnVoid
     {
         /// <summary>
@@ -77,8 +75,7 @@ namespace MediaBrowser.Api
         public string Context { get; set; }
     }
 
-    [Route("/Sessions/{Id}/Playing", "POST")]
-    [Api(("Instructs a session to play an item"))]
+    [Route("/Sessions/{Id}/Playing", "POST", Summary = "Instructs a session to play an item")]
     public class Play : IReturnVoid
     {
         /// <summary>
@@ -110,8 +107,7 @@ namespace MediaBrowser.Api
         public PlayCommand PlayCommand { get; set; }
     }
 
-    [Route("/Sessions/{Id}/Playing/{Command}", "POST")]
-    [Api(("Issues a playstate command to a client"))]
+    [Route("/Sessions/{Id}/Playing/{Command}", "POST", Summary = "Issues a playstate command to a client")]
     public class SendPlaystateCommand : IReturnVoid
     {
         /// <summary>
@@ -135,8 +131,7 @@ namespace MediaBrowser.Api
         public PlaystateCommand Command { get; set; }
     }
 
-    [Route("/Sessions/{Id}/System/{Command}", "POST")]
-    [Api(("Issues a system command to a client"))]
+    [Route("/Sessions/{Id}/System/{Command}", "POST", Summary = "Issues a system command to a client")]
     public class SendSystemCommand : IReturnVoid
     {
         /// <summary>
@@ -154,8 +149,7 @@ namespace MediaBrowser.Api
         public SystemCommand Command { get; set; }
     }
 
-    [Route("/Sessions/{Id}/Message", "POST")]
-    [Api(("Issues a command to a client to display a message to the user"))]
+    [Route("/Sessions/{Id}/Message", "POST", Summary = "Issues a command to a client to display a message to the user")]
     public class SendMessageCommand : IReturnVoid
     {
         /// <summary>
@@ -175,8 +169,7 @@ namespace MediaBrowser.Api
         public long? TimeoutMs { get; set; }
     }
 
-    [Route("/Sessions/{Id}/Users/{UserId}", "POST")]
-    [Api(("Adds an additional user to a session"))]
+    [Route("/Sessions/{Id}/Users/{UserId}", "POST", Summary = "Adds an additional user to a session")]
     public class AddUserToSession : IReturnVoid
     {
         [ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
@@ -186,8 +179,7 @@ namespace MediaBrowser.Api
         public Guid UserId { get; set; }
     }
 
-    [Route("/Sessions/{Id}/Users/{UserId}", "DELETE")]
-    [Api(("Removes an additional user from a session"))]
+    [Route("/Sessions/{Id}/Users/{UserId}", "DELETE", Summary = "Removes an additional user from a session")]
     public class RemoveUserFromSession : IReturnVoid
     {
         [ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
@@ -197,8 +189,7 @@ namespace MediaBrowser.Api
         public Guid UserId { get; set; }
     }
 
-    [Route("/Sessions/{Id}/Capabilities", "POST")]
-    [Api(("Updates capabilities for a device"))]
+    [Route("/Sessions/{Id}/Capabilities", "POST", Summary = "Updates capabilities for a device")]
     public class PostCapabilities : IReturnVoid
     {
         /// <summary>

+ 4 - 7
MediaBrowser.Api/SystemService.cs

@@ -8,8 +8,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class GetSystemInfo
     /// </summary>
-    [Route("/System/Info", "GET")]
-    [Api(Description = "Gets information about the server")]
+    [Route("/System/Info", "GET", Summary = "Gets information about the server")]
     public class GetSystemInfo : IReturn<SystemInfo>
     {
 
@@ -18,18 +17,16 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class RestartApplication
     /// </summary>
-    [Route("/System/Restart", "POST")]
-    [Api(("Restarts the application, if needed"))]
+    [Route("/System/Restart", "POST", Summary = "Restarts the application, if needed")]
     public class RestartApplication
     {
     }
 
-    [Route("/System/Shutdown", "POST")]
-    [Api(("Shuts down the application"))]
+    [Route("/System/Shutdown", "POST", Summary = "Shuts down the application")]
     public class ShutdownApplication
     {
     }
-    
+
     /// <summary>
     /// Class SystemInfoService
     /// </summary>

+ 6 - 11
MediaBrowser.Api/TvShowsService.cs

@@ -17,8 +17,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class GetNextUpEpisodes
     /// </summary>
-    [Route("/Shows/NextUp", "GET")]
-    [Api(("Gets a list of next up episodes"))]
+    [Route("/Shows/NextUp", "GET", Summary = "Gets a list of next up episodes")]
     public class GetNextUpEpisodes : IReturn<ItemsResult>, IHasItemFields
     {
         /// <summary>
@@ -53,8 +52,7 @@ namespace MediaBrowser.Api
         public string SeriesId { get; set; }
     }
 
-    [Route("/Shows/Upcoming", "GET")]
-    [Api(("Gets a list of upcoming episodes"))]
+    [Route("/Shows/Upcoming", "GET", Summary = "Gets a list of upcoming episodes")]
     public class GetUpcomingEpisodes : IReturn<ItemsResult>, IHasItemFields
     {
         /// <summary>
@@ -86,14 +84,12 @@ namespace MediaBrowser.Api
         public string Fields { get; set; }
     }
 
-    [Route("/Shows/{Id}/Similar", "GET")]
-    [Api(Description = "Finds tv shows similar to a given one.")]
+    [Route("/Shows/{Id}/Similar", "GET", Summary = "Finds tv shows similar to a given one.")]
     public class GetSimilarShows : BaseGetSimilarItemsFromItem
     {
     }
 
-    [Route("/Shows/{Id}/Episodes", "GET")]
-    [Api(Description = "Gets episodes for a tv season")]
+    [Route("/Shows/{Id}/Episodes", "GET", Summary = "Gets episodes for a tv season")]
     public class GetEpisodes : IReturn<ItemsResult>, IHasItemFields
     {
         /// <summary>
@@ -129,8 +125,7 @@ namespace MediaBrowser.Api
         public string AdjacentTo { get; set; }
     }
 
-    [Route("/Shows/{Id}/Seasons", "GET")]
-    [Api(Description = "Gets seasons for a tv series")]
+    [Route("/Shows/{Id}/Seasons", "GET", Summary = "Gets seasons for a tv series")]
     public class GetSeasons : IReturn<ItemsResult>, IHasItemFields
     {
         /// <summary>
@@ -320,7 +315,7 @@ namespace MediaBrowser.Api
 
                     return 0;
                 })
-                .ThenByDescending(i =>i.Item2)
+                .ThenByDescending(i => i.Item2)
                 .ThenByDescending(i => i.Item1.PremiereDate ?? DateTime.MinValue)
                 .Select(i => i.Item1);
         }

+ 9 - 18
MediaBrowser.Api/UserService.cs

@@ -17,8 +17,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class GetUsers
     /// </summary>
-    [Route("/Users", "GET")]
-    [Api(Description = "Gets a list of users")]
+    [Route("/Users", "GET", Summary = "Gets a list of users")]
     public class GetUsers : IReturn<List<UserDto>>
     {
         [ApiMember(Name = "IsHidden", Description = "Optional filter by IsHidden=true or false", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
@@ -28,8 +27,7 @@ namespace MediaBrowser.Api
         public bool? IsDisabled { get; set; }
     }
 
-    [Route("/Users/Public", "GET")]
-    [Api(Description = "Gets a list of publicly visible users for display on a login screen.")]
+    [Route("/Users/Public", "GET", Summary = "Gets a list of publicly visible users for display on a login screen.")]
     public class GetPublicUsers : IReturn<List<UserDto>>
     {
     }
@@ -37,8 +35,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class GetUser
     /// </summary>
-    [Route("/Users/{Id}", "GET")]
-    [Api(Description = "Gets a user by Id")]
+    [Route("/Users/{Id}", "GET", Summary = "Gets a user by Id")]
     public class GetUser : IReturn<UserDto>
     {
         /// <summary>
@@ -52,8 +49,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class DeleteUser
     /// </summary>
-    [Route("/Users/{Id}", "DELETE")]
-    [Api(Description = "Deletes a user")]
+    [Route("/Users/{Id}", "DELETE", Summary = "Deletes a user")]
     public class DeleteUser : IReturnVoid
     {
         /// <summary>
@@ -67,8 +63,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class AuthenticateUser
     /// </summary>
-    [Route("/Users/{Id}/Authenticate", "POST")]
-    [Api(Description = "Authenticates a user")]
+    [Route("/Users/{Id}/Authenticate", "POST", Summary = "Authenticates a user")]
     public class AuthenticateUser : IReturn<AuthenticationResult>
     {
         /// <summary>
@@ -89,8 +84,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class AuthenticateUser
     /// </summary>
-    [Route("/Users/AuthenticateByName", "POST")]
-    [Api(Description = "Authenticates a user")]
+    [Route("/Users/AuthenticateByName", "POST", Summary = "Authenticates a user")]
     public class AuthenticateUserByName : IReturn<AuthenticationResult>
     {
         /// <summary>
@@ -111,8 +105,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class UpdateUserPassword
     /// </summary>
-    [Route("/Users/{Id}/Password", "POST")]
-    [Api(Description = "Updates a user's password")]
+    [Route("/Users/{Id}/Password", "POST", Summary = "Updates a user's password")]
     public class UpdateUserPassword : IReturnVoid
     {
         /// <summary>
@@ -143,8 +136,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class UpdateUser
     /// </summary>
-    [Route("/Users/{Id}", "POST")]
-    [Api(Description = "Updates a user")]
+    [Route("/Users/{Id}", "POST", Summary = "Updates a user")]
     public class UpdateUser : UserDto, IReturnVoid
     {
     }
@@ -152,8 +144,7 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class CreateUser
     /// </summary>
-    [Route("/Users", "POST")]
-    [Api(Description = "Creates a user")]
+    [Route("/Users", "POST", Summary = "Creates a user")]
     public class CreateUser : UserDto, IReturn<UserDto>
     {
     }

+ 11 - 7
MediaBrowser.Common/Net/BasePeriodicWebSocketListener.cs

@@ -1,11 +1,11 @@
-using System.Globalization;
-using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Net;
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
-using MediaBrowser.Model.Net;
 
 namespace MediaBrowser.Common.Net
 {
@@ -16,6 +16,7 @@ namespace MediaBrowser.Common.Net
     /// <typeparam name="TStateType">The type of the T state type.</typeparam>
     public abstract class BasePeriodicWebSocketListener<TReturnDataType, TStateType> : IWebSocketListener, IDisposable
         where TStateType : class, new()
+        where TReturnDataType : class
     {
         /// <summary>
         /// The _active connections
@@ -144,12 +145,15 @@ namespace MediaBrowser.Common.Net
 
                 var data = await GetDataToSend(tuple.Item4).ConfigureAwait(false);
 
-                await connection.SendAsync(new WebSocketMessage<TReturnDataType>
+                if (data != null)
                 {
-                    MessageType = Name,
-                    Data = data
+                    await connection.SendAsync(new WebSocketMessage<TReturnDataType>
+                    {
+                        MessageType = Name,
+                        Data = data
 
-                }, tuple.Item2.Token).ConfigureAwait(false);
+                    }, tuple.Item2.Token).ConfigureAwait(false);
+                }
 
                 tuple.Item5.Release();
             }

+ 15 - 3
MediaBrowser.Controller/Dlna/CodecProfile.cs

@@ -6,12 +6,12 @@ namespace MediaBrowser.Controller.Dlna
     public class CodecProfile
     {
         public CodecType Type { get; set; }
-        public List<ProfileCondition> Conditions { get; set; }
+        public ProfileCondition[] Conditions { get; set; }
         public string Codec { get; set; }
 
         public CodecProfile()
         {
-            Conditions = new List<ProfileCondition>();
+            Conditions = new ProfileCondition[] {};
         }
 
         public List<string> GetCodecs()
@@ -32,6 +32,12 @@ namespace MediaBrowser.Controller.Dlna
         public ProfileConditionType Condition { get; set; }
         public ProfileConditionValue Property { get; set; }
         public string Value { get; set; }
+        public bool IsRequired { get; set; }
+
+        public ProfileCondition()
+        {
+            IsRequired = true;
+        }
     }
 
     public enum ProfileConditionType
@@ -46,11 +52,17 @@ namespace MediaBrowser.Controller.Dlna
     {
         AudioChannels,
         AudioBitrate,
+        AudioProfile,
         Filesize,
         Width,
         Height,
+        Has64BitOffsets,
+        VideoBitDepth,
         VideoBitrate,
         VideoFramerate,
-        VideoLevel
+        VideoLevel,
+        VideoPacketLength,
+        VideoProfile,
+        VideoTimestamp
     }
 }

+ 22 - 0
MediaBrowser.Controller/Dlna/ContainerProfile.cs

@@ -0,0 +1,22 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MediaBrowser.Controller.Dlna
+{
+    public class ContainerProfile
+    {
+        public DlnaProfileType Type { get; set; }
+        public ProfileCondition[] Conditions { get; set; }
+        public string Container { get; set; }
+
+        public ContainerProfile()
+        {
+            Conditions = new ProfileCondition[] { };
+        }
+
+        public List<string> GetContainers()
+        {
+            return (Container ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+        }
+    }
+}

+ 5 - 5
MediaBrowser.Controller/Dlna/DeviceIdentification.cs

@@ -1,5 +1,4 @@
-using System.Collections.Generic;
-
+
 namespace MediaBrowser.Controller.Dlna
 {
     public class DeviceIdentification
@@ -55,11 +54,11 @@ namespace MediaBrowser.Controller.Dlna
         /// Gets or sets the headers.
         /// </summary>
         /// <value>The headers.</value>
-        public List<HttpHeaderInfo> Headers { get; set; }
+        public HttpHeaderInfo[] Headers { get; set; }
 
         public DeviceIdentification()
         {
-            Headers = new List<HttpHeaderInfo>();
+            Headers = new HttpHeaderInfo[] {};
         }
     }
 
@@ -73,6 +72,7 @@ namespace MediaBrowser.Controller.Dlna
     public enum HeaderMatchType
     {
         Equals = 0,
-        Substring = 1
+        Regex = 1,
+        Substring = 2
     }
 }

+ 10 - 1
MediaBrowser.Controller/Dlna/DeviceProfile.cs

@@ -20,13 +20,15 @@ namespace MediaBrowser.Controller.Dlna
         /// </summary>
         /// <value>The transcoding profiles.</value>
         public TranscodingProfile[] TranscodingProfiles { get; set; }
-        
+
         /// <summary>
         /// Gets or sets the direct play profiles.
         /// </summary>
         /// <value>The direct play profiles.</value>
         public DirectPlayProfile[] DirectPlayProfiles { get; set; }
 
+        public ContainerProfile[] ContainerProfiles { get; set; }
+
         /// <summary>
         /// Gets or sets the identification.
         /// </summary>
@@ -40,6 +42,9 @@ namespace MediaBrowser.Controller.Dlna
         public string ModelDescription { get; set; }
         public string ModelNumber { get; set; }
         public string ModelUrl { get; set; }
+        public bool IgnoreTranscodeByteRangeRequests { get; set; }
+        public bool SupportsAlbumArtInDidl { get; set; }
+
         /// <summary>
         /// Controls the content of the X_DLNADOC element in the urn:schemas-dlna-org:device-1-0 namespace.
         /// </summary>
@@ -60,12 +65,16 @@ namespace MediaBrowser.Controller.Dlna
 
         public int TimelineOffsetSeconds { get; set; }
 
+        public bool RequiresPlainVideoItems { get; set; }
+        public bool RequiresPlainFolders { get; set; }
+
         public DeviceProfile()
         {
             DirectPlayProfiles = new DirectPlayProfile[] { };
             TranscodingProfiles = new TranscodingProfile[] { };
             MediaProfiles = new MediaProfile[] { };
             CodecProfiles = new CodecProfile[] { };
+            ContainerProfiles = new ContainerProfile[] { };
         }
     }
 }

+ 3 - 7
MediaBrowser.Controller/Dlna/DirectPlayProfile.cs

@@ -5,19 +5,15 @@ namespace MediaBrowser.Controller.Dlna
 {
     public class DirectPlayProfile
     {
-        public string[] Containers { get; set; }
+        public string Container { get; set; }
         public string AudioCodec { get; set; }
         public string VideoCodec { get; set; }
 
         public DlnaProfileType Type { get; set; }
 
-        public List<ProfileCondition> Conditions { get; set; }
-
-        public DirectPlayProfile()
+        public List<string> GetContainers()
         {
-            Conditions = new List<ProfileCondition>();
-
-            Containers = new string[] { };
+            return (Container ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
         }
 
         public List<string> GetAudioCodecs()

+ 7 - 0
MediaBrowser.Controller/Dlna/MediaProfile.cs

@@ -13,6 +13,13 @@ namespace MediaBrowser.Controller.Dlna
         public string OrgPn { get; set; }
         public string MimeType { get; set; }
 
+        public ProfileCondition[] Conditions { get; set; }
+
+        public MediaProfile()
+        {
+            Conditions = new ProfileCondition[] {};
+        }
+        
         public List<string> GetAudioCodecs()
         {
             return (AudioCodec ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();

+ 18 - 5
MediaBrowser.Controller/Dlna/TranscodingProfile.cs

@@ -1,5 +1,4 @@
-using System.Collections.Generic;
-
+
 namespace MediaBrowser.Controller.Dlna
 {
     public class TranscodingProfile
@@ -11,12 +10,18 @@ namespace MediaBrowser.Controller.Dlna
         public string VideoCodec { get; set; }
         public string AudioCodec { get; set; }
 
-        public List<TranscodingSetting> Settings { get; set; }
+        public bool EstimateContentLength { get; set; }
+
+        public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
+
+        public TranscodingSetting[] Settings { get; set; }
 
         public TranscodingProfile()
         {
-            Settings = new List<TranscodingSetting>();
+            Settings = new TranscodingSetting[] { };
         }
+
+        public bool EnableMpegtsM2TsMode { get; set; }
     }
 
     public class TranscodingSetting
@@ -27,6 +32,14 @@ namespace MediaBrowser.Controller.Dlna
 
     public enum TranscodingSettingType
     {
-        Profile
+        VideoLevel = 0,
+        VideoProfile = 1,
+        MaxAudioChannels = 2
+    }
+
+    public enum TranscodeSeekInfo
+    {
+        Auto = 0,
+        Bytes = 1
     }
 }

+ 1 - 0
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -79,6 +79,7 @@
     <Compile Include="Collections\CollectionCreationOptions.cs" />
     <Compile Include="Collections\ICollectionManager.cs" />
     <Compile Include="Dlna\CodecProfile.cs" />
+    <Compile Include="Dlna\ContainerProfile.cs" />
     <Compile Include="Dlna\DeviceIdentification.cs" />
     <Compile Include="Dlna\DirectPlayProfile.cs" />
     <Compile Include="Dlna\IDlnaManager.cs" />

+ 35 - 871
MediaBrowser.Dlna/DlnaManager.cs

@@ -1,6 +1,7 @@
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Dlna.Profiles;
 using MediaBrowser.Model.Serialization;
 using System.Collections.Generic;
 using System.Linq;
@@ -13,849 +14,42 @@ namespace MediaBrowser.Dlna
         private IApplicationPaths _appPaths;
         private readonly IXmlSerializer _xmlSerializer;
         private readonly IFileSystem _fileSystem;
+        private readonly IJsonSerializer _jsonSerializer;
 
-        public DlnaManager(IXmlSerializer xmlSerializer, IFileSystem fileSystem)
+        public DlnaManager(IXmlSerializer xmlSerializer, IFileSystem fileSystem, IJsonSerializer jsonSerializer)
         {
             _xmlSerializer = xmlSerializer;
             _fileSystem = fileSystem;
+            _jsonSerializer = jsonSerializer;
 
-            //GetProfiles();
+            GetProfiles();
         }
 
         public IEnumerable<DeviceProfile> GetProfiles()
         {
-            var list = new List<DeviceProfile>();
-
-            list.Add(new DeviceProfile
-            {
-                Name = "Samsung TV (B Series)",
-                ClientType = "DLNA",
-
-                Identification = new DeviceIdentification
-                {
-                    FriendlyName = "^TV$",
-                    ModelNumber = @"1\.0",
-                    ModelName = "Samsung DTV DMR"
-                },
-
-                TranscodingProfiles = new[]
-                {
-                    new TranscodingProfile
-                    {
-                        Container = "mp3", 
-                        Type = DlnaProfileType.Audio,                        
-                    },
-                     new TranscodingProfile
-                    {
-                        Container = "ts", 
-                        Type = DlnaProfileType.Video
-                    }
-                },
-
-                DirectPlayProfiles = new[]
-                {
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mp3"}, 
-                        Type = DlnaProfileType.Audio,
-                    },
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mkv"}, 
-                        Type = DlnaProfileType.Video
-                    },
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"avi"}, 
-                        Type = DlnaProfileType.Video
-                    },
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mp4"},                       
-                        Type = DlnaProfileType.Video
-                    }
-                },
-
-                MediaProfiles = new[]
-                {
-                    new MediaProfile
-                    {
-                        Container ="avi",
-                        MimeType = "video/x-msvideo",
-                        Type = DlnaProfileType.Video
-                    },
-
-                    new MediaProfile
-                    {
-                        Container ="mkv",
-                        MimeType = "video/x-mkv",
-                        Type = DlnaProfileType.Video
-                    }
-                }
-            });
-
-            list.Add(new DeviceProfile
-            {
-                Name = "Samsung TV (E/F-series)",
-                ClientType = "DLNA",
-
-                Identification = new DeviceIdentification
-                {
-                    FriendlyName = @"(^\[TV\][A-Z]{2}\d{2}(E|F)[A-Z]?\d{3,4}.*)|^\[TV\] Samsung|(^\[TV\]Samsung [A-Z]{2}\d{2}(E|F)[A-Z]?\d{3,4}.*)",
-                    ModelNumber = @"(1\.0)|(AllShare1\.0)"
-                },
-
-                TranscodingProfiles = new[]
-                {
-                    new TranscodingProfile
-                    {
-                        Container = "mp3", 
-                        Type = DlnaProfileType.Audio
-                    },
-                    new TranscodingProfile
-                    {
-                        Container = "ts", 
-                        Type = DlnaProfileType.Video
-                    }
-                },
-
-                DirectPlayProfiles = new[]
-                {
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mp3"}, 
-                        Type = DlnaProfileType.Audio
-                    },
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mkv"}, 
-                        Type = DlnaProfileType.Video
-                    },
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"avi"}, 
-                        Type = DlnaProfileType.Video
-                    },
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mp4"},                       
-                        Type = DlnaProfileType.Video
-                    }
-                },
-
-                MediaProfiles = new[]
-                {
-                    new MediaProfile
-                    {
-                        Container ="avi",
-                        MimeType = "video/x-msvideo",
-                        Type = DlnaProfileType.Video
-                    },
-
-                    new MediaProfile
-                    {
-                        Container ="mkv",
-                        MimeType = "video/x-mkv",
-                        Type = DlnaProfileType.Video
-                    }
-                }
-            });
-
-            list.Add(new DeviceProfile
-            {
-                Name = "Samsung TV (C/D-series)",
-                ClientType = "DLNA",
-
-                Identification = new DeviceIdentification
-                {
-                    FriendlyName = @"(^TV-\d{2}C\d{3}.*)|(^\[TV\][A-Z]{2}\d{2}(D)[A-Z]?\d{3,4}.*)|^\[TV\] Samsung",
-                    ModelNumber = @"(1\.0)|(AllShare1\.0)"
-                },
-
-                TranscodingProfiles = new[]
-                {
-                    new TranscodingProfile
-                    {
-                        Container = "mp3", 
-                        Type = DlnaProfileType.Audio
-                    },
-                     new TranscodingProfile
-                    {
-                        Container = "ts", 
-                        Type = DlnaProfileType.Video
-                    }
-                },
-
-                DirectPlayProfiles = new[]
-                {
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mp3"}, 
-                        Type = DlnaProfileType.Audio
-                    },                  
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mkv"}, 
-                        Type = DlnaProfileType.Video
-                    },
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"avi"}, 
-                        Type = DlnaProfileType.Video
-                    },
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mp4"},                       
-                        Type = DlnaProfileType.Video
-                    }
-                },
-
-                MediaProfiles = new[]
-                {
-                    new MediaProfile
-                    {
-                        Container ="avi",
-                        MimeType = "video/x-msvideo",
-                        Type = DlnaProfileType.Video
-                    },
-
-                    new MediaProfile
-                    {
-                        Container ="mkv",
-                        MimeType = "video/x-mkv",
-                        Type = DlnaProfileType.Video
-                    }
-                }
-            });
-
-            list.Add(new DeviceProfile
-            {
-                Name = "Xbox 360",
-                ClientType = "DLNA",
-
-                Identification = new DeviceIdentification
-                {
-                    ModelName = "Xbox 360"
-                },
-
-                TranscodingProfiles = new[]
-                {
-                    new TranscodingProfile
-                    {
-                        Container = "mp3", 
-                        Type = DlnaProfileType.Audio
-                    },
-                    new TranscodingProfile
-                    {
-                        Container = "ts", 
-                        Type = DlnaProfileType.Video
-                    }
-                },
-
-                DirectPlayProfiles = new[]
-                {
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mp3"}, 
-                        Type = DlnaProfileType.Audio
-                    },
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"avi"}, 
-                        Type = DlnaProfileType.Video
-                    },
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mp4"}, 
-                        Type = DlnaProfileType.Video
-                    }
-                },
-
-                MediaProfiles = new[]
-                {
-                    new MediaProfile
-                    {
-                        Container ="avi",
-                        MimeType = "video/avi",
-                        Type = DlnaProfileType.Video
-                    }
-                }
-            });
-
-            list.Add(new DeviceProfile
-            {
-                Name = "Xbox One",
-                ClientType = "DLNA",
-
-                Identification = new DeviceIdentification
-                {
-                    ModelName = "Xbox One",
-                    FriendlyName = "Xbox-SystemOS"
-                },
-
-                TranscodingProfiles = new[]
-                {
-                    new TranscodingProfile
-                    {
-                        Container = "mp3", 
-                        Type = DlnaProfileType.Audio
-                    },
-                    new TranscodingProfile
-                    {
-                        Container = "ts", 
-                        Type = DlnaProfileType.Video
-                    }
-                },
-
-                DirectPlayProfiles = new[]
-                {
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mp3"}, 
-                        Type = DlnaProfileType.Audio
-                    },
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"avi"}, 
-                        Type = DlnaProfileType.Video
-                    }
-                },
-
-                MediaProfiles = new[]
-                {
-                    new MediaProfile
-                    {
-                        Container ="avi",
-                        MimeType = "video/x-msvideo",
-                        Type = DlnaProfileType.Video
-                    }
-                }
-            });
-
-            list.Add(new DeviceProfile
-            {
-                Name = "Sony Bravia (2012)",
-                ClientType = "DLNA",
-
-                Identification = new DeviceIdentification
-                {
-                    FriendlyName = @"BRAVIA KDL-\d{2}[A-Z]X\d5(\d|G).*"
-                },
-
-                TranscodingProfiles = new[]
-                {
-                    new TranscodingProfile
-                    {
-                        Container = "mp3", 
-                        Type = DlnaProfileType.Audio
-                    },
-                    new TranscodingProfile
-                    {
-                        Container = "ts", 
-                        Type = DlnaProfileType.Video
-                    }
-                },
-
-                DirectPlayProfiles = new[]
-                {
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mp3"}, 
-                        Type = DlnaProfileType.Audio
-                    },
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"avi"}, 
-                        Type = DlnaProfileType.Video
-                    },
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"asf"}, 
-                        Type = DlnaProfileType.Audio
-                    }
-                },
-
-                MediaProfiles = new[]
-                {
-                    new MediaProfile
-                    {
-                        Container ="avi",
-                        MimeType = "video/avi",
-                        Type = DlnaProfileType.Video
-                    },
-
-                    new MediaProfile
-                    {
-                        Container ="asf",
-                        MimeType = "video/x-ms-wmv",
-                        Type = DlnaProfileType.Audio
-                    }
-                }
-            });
-
-            list.Add(new DeviceProfile
-            {
-                Name = "Sony Bravia (2013)",
-                ClientType = "DLNA",
-
-                Identification = new DeviceIdentification
-                {
-                    FriendlyName = @"BRAVIA (KDL-\d{2}W[689]\d{2}A.*)|(KD-\d{2}X9\d{3}A.*)"
-                },
-
-                TranscodingProfiles = new[]
-                {
-                    new TranscodingProfile
-                    {
-                        Container = "mp3", 
-                        Type = DlnaProfileType.Audio
-                    },
-                    new TranscodingProfile
-                    {
-                        Container = "ts", 
-                        Type = DlnaProfileType.Video
-                    }
-                },
-
-                DirectPlayProfiles = new[]
-                {
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mp3"}, 
-                        Type = DlnaProfileType.Audio
-                    },
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"wma"}, 
-                        Type = DlnaProfileType.Audio
-                    },                    
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"avi"}, 
-                        Type = DlnaProfileType.Video
-                    },
-                     new DirectPlayProfile
-                    {
-                        Containers = new[]{"mp4"}, 
-                        Type = DlnaProfileType.Video
-                    }
-                },
-
-                MediaProfiles = new[]
-                {
-                    new MediaProfile
-                    {
-                        Container ="avi",
-                        MimeType = "video/avi",
-                        Type = DlnaProfileType.Video
-                    },
-
-                    new MediaProfile
-                    {
-                        Container ="mp4",
-                        MimeType = "video/mp4",
-                        Type = DlnaProfileType.Video
-                    },
-
-                    new MediaProfile
-                    {
-                        Container ="ts",
-                        MimeType = "video/mpeg",
-                        Type = DlnaProfileType.Video
-                    },
-
-                    new MediaProfile
-                    {
-                        Container ="wma",
-                        MimeType = "video/x-ms-wma",
-                        Type = DlnaProfileType.Audio
-                    }
-                }
-            });
-
-            list.Add(new DeviceProfile
-            {
-                //Panasonic Viera (2011|2012) Without AVI Support
-                Name = "Panasonic Viera E/S/ST/VT (2011)",
-                ClientType = "DLNA",
-
-                Identification = new DeviceIdentification
-                {
-                    FriendlyName = @"(VIERA (E|S)T?(3|5)0?.*)|(VIERA VT30.*)",
-                    Manufacturer = "Panasonic"
-                },
-
-                TranscodingProfiles = new[]
-                {
-                    new TranscodingProfile
-                    {
-                        Container = "mp3", 
-                        Type = DlnaProfileType.Audio
-                    },
-                    new TranscodingProfile
-                    {
-                        Container = "ts", 
-                        Type = DlnaProfileType.Video
-                    }
-                },
-
-                DirectPlayProfiles = new[]
-                {
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mp3"}, 
-                        Type = DlnaProfileType.Audio
-                    },
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mkv"}, 
-                        Type = DlnaProfileType.Video                        
-                    }
-                }
-            });
-
-            list.Add(new DeviceProfile
-            {
-                //Panasonic Viera (2011|2012) With AVI Support
-                Name = "Panasonic Viera G/GT/DT/UT/VT (2011/2012)",
-                ClientType = "DLNA",
-
-                Identification = new DeviceIdentification
-                {
-                    FriendlyName = @"(VIERA (G|D|U)T?(3|5)0?.*)|(VIERA VT50.*)",
-                    Manufacturer = "Panasonic"
-                },
-
-                TranscodingProfiles = new[]
-                {
-                    new TranscodingProfile
-                    {
-                        Container = "mp3", 
-                        Type = DlnaProfileType.Audio
-                    },
-                    new TranscodingProfile
-                    {
-                        Container = "ts", 
-                        Type = DlnaProfileType.Video
-                    }
-                },
-
-                DirectPlayProfiles = new[]
-                {
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mp3"}, 
-                        Type = DlnaProfileType.Audio
-                    },
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mkv"}, 
-                        Type = DlnaProfileType.Video                        
-                    },
-                     new DirectPlayProfile
-                    {
-                        Containers = new[]{"avi"}, 
-                        Type = DlnaProfileType.Video
-                    }
-                },
-
-                MediaProfiles = new[]
-                {
-                    new MediaProfile
-                    {
-                        Container ="avi",
-                        MimeType = "video/divx",
-                        Type = DlnaProfileType.Video
-                    }
-                }
-            });
-
-            list.Add(new DeviceProfile
-            {
-                Name = "Philips (2010-)",
-                ClientType = "DLNA",
-
-                Identification = new DeviceIdentification
-                {
-                    FriendlyName = ".*PHILIPS.*",
-                    ModelName = "WD TV HD Live"
-                },
-
-                DirectPlayProfiles = new[]
-                {
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mp3", "wma"}, 
-                        Type = DlnaProfileType.Audio
-                    },
-
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"avi"}, 
-                        Type = DlnaProfileType.Video
-                    },
-
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mkv"}, 
-                        Type = DlnaProfileType.Video
-                    }
-                },
-
-                MediaProfiles = new[]
-                {
-                    new MediaProfile
-                    {
-                        Container ="avi",
-                        MimeType = "video/avi",
-                        Type = DlnaProfileType.Video
-                    },
-
-                    new MediaProfile
-                    {
-                        Container ="mkv",
-                        MimeType = "video/x-matroska",
-                        Type = DlnaProfileType.Video
-                    }
-                }
-            });
-
-            list.Add(new DeviceProfile
-            {
-                Name = "WDTV Live",
-                ClientType = "DLNA",
-
-                TimelineOffsetSeconds = 5,
-
-                Identification = new DeviceIdentification
-                {
-                    ModelName = "WD TV HD Live",
-
-                    Headers = new List<HttpHeaderInfo>
-                    {
-                         new HttpHeaderInfo{ Name="User-Agent", Value="alphanetworks", Match= HeaderMatchType.Substring},
-                         new HttpHeaderInfo{ Name="User-Agent", Value="ALPHA Networks", Match= HeaderMatchType.Substring}
-                    }
-                },
-
-                TranscodingProfiles = new[]
-                {
-                    new TranscodingProfile
-                    {
-                        Container = "mp3", 
-                        Type = DlnaProfileType.Audio,
-                        AudioCodec = "mp3"
-                    },
-                    new TranscodingProfile
-                    {
-                        Container = "ts", 
-                        Type = DlnaProfileType.Video,
-                        VideoCodec = "h264",
-                        AudioCodec = "aac"
-                    },
-                    new TranscodingProfile
-                    {
-                        Container = "jpeg", 
-                        Type = DlnaProfileType.Photo
-                    }
-                },
-
-                DirectPlayProfiles = new[]
-                {
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"avi"}, 
-                        Type = DlnaProfileType.Video,
-                        VideoCodec = "mpeg1video,mpeg2video,mpeg4,h264,vc1",
-                        AudioCodec = "ac3,dca,mp2,mp3,pcm"
-                    },
-
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mpeg"}, 
-                        Type = DlnaProfileType.Video,
-                        VideoCodec = "mpeg1video,mpeg2video",
-                        AudioCodec = "ac3,dca,mp2,mp3,pcm"
-                    },
-
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mkv"}, 
-                        Type = DlnaProfileType.Video,
-                        VideoCodec = "mpeg1video,mpeg2video,mpeg4,h264,vc1",
-                        AudioCodec = "ac3,dca,aac,mp2,mp3,pcm"
-                    },
-
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"ts"}, 
-                        Type = DlnaProfileType.Video,
-                        VideoCodec = "mpeg1video,mpeg2video,h264,vc1",
-                        AudioCodec = "ac3,dca,mp2,mp3"
-                    },
-
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mp4", "mov"}, 
-                        Type = DlnaProfileType.Video,
-                        VideoCodec = "h264,mpeg4",
-                        AudioCodec = "ac3,aac,mp2,mp3"
-                    },
-
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"asf"}, 
-                        Type = DlnaProfileType.Video,
-                        VideoCodec = "vc1",
-                        AudioCodec = "wmav2,wmapro"
-                    },
-
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"asf"}, 
-                        Type = DlnaProfileType.Video,
-                        VideoCodec = "mpeg2video",
-                        AudioCodec = "mp2,ac3"
-                    },
-
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mp3"}, 
-                        AudioCodec = "mp2,mp3",
-                        Type = DlnaProfileType.Audio
-                    },
-
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mp4"}, 
-                        AudioCodec = "mp4",
-                        Type = DlnaProfileType.Audio
-                    },
-
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"flac"}, 
-                        AudioCodec = "flac",
-                        Type = DlnaProfileType.Audio
-                    },
-
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"asf"}, 
-                        AudioCodec = "wmav2,wmapro,wmavoice",
-                        Type = DlnaProfileType.Audio
-                    },
-
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"ogg"}, 
-                        AudioCodec = "vorbis",
-                        Type = DlnaProfileType.Audio
-                    },
-
-                    new DirectPlayProfile
-                    {
-                        Type = DlnaProfileType.Photo,
-
-                        Containers = new[]{"jpeg", "png", "gif", "bmp", "tiff"},
-
-                        Conditions = new List<ProfileCondition>
-                        {
-                            new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.Width, Value = "1920"},
-                            new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.Height, Value = "1080"}
-                        }
-                    }
-                },
-
-                MediaProfiles = new[]
-                {
-                    new MediaProfile
-                    {
-                        Container ="ts",
-                        OrgPn = "MPEG_TS_SD_NA",
-                        Type = DlnaProfileType.Video
-                    }
-                },
-
-                CodecProfiles = new[]
-                {
-                    new CodecProfile
-                    {
-                         Type = CodecType.VideoCodec,
-                         Codec= "h264",
-
-                        Conditions = new List<ProfileCondition>
-                        {
-                            new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.Width, Value = "1920"},
-                            new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.Height, Value = "1080"},
-                            new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.VideoLevel, Value = "41"}
-                        }
-                    },
-
-                    new CodecProfile
-                    {
-                        Type = CodecType.VideoAudioCodec,
-                         Codec= "aac",
-
-                        Conditions = new List<ProfileCondition>
-                        {
-                            new ProfileCondition{ Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.AudioChannels, Value = "2"}
-                        }
-                    }
-                }
-            });
-
-            list.Add(new DeviceProfile
-            {
-                // Linksys DMA2100us does not need any transcoding of the formats we support statically
-                Name = "Linksys DMA2100",
-                ClientType = "DLNA",
-
-                Identification = new DeviceIdentification
-                {
-                    ModelName = "DMA2100us"
-                },
-
-                DirectPlayProfiles = new[]
-                {
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mp3", "flac", "m4a", "wma"}, 
-                        Type = DlnaProfileType.Audio
-                    },
-
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"avi", "mp4", "mkv", "ts"}, 
-                        Type = DlnaProfileType.Video
-                    }
-                }
-            });
-
-            list.Add(new DeviceProfile
-            {
-                Name = "Denon AVR",
-                ClientType = "DLNA",
-
-                Identification = new DeviceIdentification
-                {
-                    FriendlyName = @"Denon:\[AVR:.*",
-                    Manufacturer = "Denon"
-                },
-
-                DirectPlayProfiles = new[]
-                {
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mp3", "flac", "m4a", "wma"}, 
-                        Type = DlnaProfileType.Audio
-                    },                   
-                }
-            });
+            var list = new List<DeviceProfile>
+            {
+                new SamsungSmartTvProfile(),
+                new Xbox360Profile(),
+                new XboxOneProfile(),
+                new SonyPs3Profile(),
+                new SonyBravia2010Profile(),
+                new SonyBravia2011Profile(),
+                new SonyBravia2012Profile(),
+                new SonyBravia2013Profile(),
+                new SonyBlurayPlayer2013Profile(),
+                new SonyBlurayPlayerProfile(),
+                new PanasonicVieraProfile(),
+                new WdtvLiveProfile(),
+                new DenonAvrProfile(),
+                new LinksysDMA2100Profile(),
+                new LgTvProfile()
+            };
 
             foreach (var item in list)
             {
-                //_xmlSerializer.SerializeToFile(item, "d:\\" + _fileSystem.GetValidFilename(item.Name));
+                //_xmlSerializer.SerializeToFile(item, "d:\\" + _fileSystem.GetValidFilename(item.Name) + ".xml");
+                //_jsonSerializer.SerializeToFile(item, "d:\\" + _fileSystem.GetValidFilename(item.Name) + ".json");
             }
 
             return list;
@@ -863,37 +57,7 @@ namespace MediaBrowser.Dlna
 
         public DeviceProfile GetDefaultProfile()
         {
-            return new DeviceProfile
-            {
-                TranscodingProfiles = new[]
-                {
-                    new TranscodingProfile
-                    {
-                        Container = "mp3", 
-                        Type = DlnaProfileType.Audio
-                    },
-                    new TranscodingProfile
-                    {
-                        Container = "ts", 
-                        Type = DlnaProfileType.Video
-                    }
-                },
-
-                DirectPlayProfiles = new[]
-                {
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"mp3", "wma"}, 
-                        Type = DlnaProfileType.Audio
-                    },
-
-                    new DirectPlayProfile
-                    {
-                        Containers = new[]{"avi", "mp4"}, 
-                        Type = DlnaProfileType.Video
-                    }
-                }
-            };
+            return new DefaultProfile();
         }
 
         public DeviceProfile GetProfile(DeviceIdentification deviceInfo)
@@ -906,55 +70,55 @@ namespace MediaBrowser.Dlna
         {
             if (!string.IsNullOrWhiteSpace(profileInfo.DeviceDescription))
             {
-                if (!Regex.IsMatch(deviceInfo.DeviceDescription, profileInfo.DeviceDescription))
+                if (deviceInfo.DeviceDescription == null || !Regex.IsMatch(deviceInfo.DeviceDescription, profileInfo.DeviceDescription))
                     return false;
             }
 
             if (!string.IsNullOrWhiteSpace(profileInfo.FriendlyName))
             {
-                if (!Regex.IsMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName))
+                if (deviceInfo.FriendlyName == null || !Regex.IsMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName))
                     return false;
             }
 
             if (!string.IsNullOrWhiteSpace(profileInfo.Manufacturer))
             {
-                if (!Regex.IsMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer))
+                if (deviceInfo.Manufacturer == null || !Regex.IsMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer))
                     return false;
             }
 
             if (!string.IsNullOrWhiteSpace(profileInfo.ManufacturerUrl))
             {
-                if (!Regex.IsMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl))
+                if (deviceInfo.ManufacturerUrl == null || !Regex.IsMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl))
                     return false;
             }
 
             if (!string.IsNullOrWhiteSpace(profileInfo.ModelDescription))
             {
-                if (!Regex.IsMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription))
+                if (deviceInfo.ModelDescription == null || !Regex.IsMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription))
                     return false;
             }
 
             if (!string.IsNullOrWhiteSpace(profileInfo.ModelName))
             {
-                if (!Regex.IsMatch(deviceInfo.ModelName, profileInfo.ModelName))
+                if (deviceInfo.ModelName == null || !Regex.IsMatch(deviceInfo.ModelName, profileInfo.ModelName))
                     return false;
             }
 
             if (!string.IsNullOrWhiteSpace(profileInfo.ModelNumber))
             {
-                if (!Regex.IsMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber))
+                if (deviceInfo.ModelNumber == null || !Regex.IsMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber))
                     return false;
             }
 
             if (!string.IsNullOrWhiteSpace(profileInfo.ModelUrl))
             {
-                if (!Regex.IsMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl))
+                if (deviceInfo.ModelUrl == null || !Regex.IsMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl))
                     return false;
             }
 
             if (!string.IsNullOrWhiteSpace(profileInfo.SerialNumber))
             {
-                if (!Regex.IsMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber))
+                if (deviceInfo.SerialNumber == null || !Regex.IsMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber))
                     return false;
             }
 

+ 22 - 3
MediaBrowser.Dlna/MediaBrowser.Dlna.csproj

@@ -81,7 +81,28 @@
     <Compile Include="PlayTo\uIcon.cs" />
     <Compile Include="PlayTo\uParser.cs" />
     <Compile Include="PlayTo\uPnpNamespaces.cs" />
+    <Compile Include="Profiles\DefaultProfile.cs" />
+    <Compile Include="Profiles\DenonAvrProfile.cs" />
+    <Compile Include="Profiles\LgTvProfile.cs" />
+    <Compile Include="Profiles\LinksysDMA2100Profile.cs" />
+    <Compile Include="Profiles\PanasonicVieraProfile.cs" />
+    <Compile Include="Profiles\SamsungSmartTvProfile.cs" />
+    <Compile Include="Profiles\SonyBlurayPlayer2013Profile.cs" />
+    <Compile Include="Profiles\SonyBlurayPlayerProfile.cs" />
+    <Compile Include="Profiles\SonyBravia2010Profile.cs" />
+    <Compile Include="Profiles\SonyBravia2011Profile.cs" />
+    <Compile Include="Profiles\SonyBravia2012Profile.cs" />
+    <Compile Include="Profiles\SonyBravia2013Profile.cs" />
+    <Compile Include="Profiles\SonyPs3Profile.cs" />
+    <Compile Include="Profiles\WdtvLiveProfile.cs" />
+    <Compile Include="Profiles\Xbox360Profile.cs" />
+    <Compile Include="Profiles\XboxOneProfile.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Server\DlnaServerEntryPoint.cs" />
+    <Compile Include="Server\Headers.cs" />
+    <Compile Include="Server\RawHeaders.cs" />
+    <Compile Include="Server\SsdpHandler.cs" />
+    <Compile Include="Server\UpnpDevice.cs" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
@@ -97,9 +118,7 @@
       <Name>MediaBrowser.Model</Name>
     </ProjectReference>
   </ItemGroup>
-  <ItemGroup>
-    <Folder Include="Server\" />
-  </ItemGroup>
+  <ItemGroup />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.

+ 4 - 1
MediaBrowser.Dlna/PlayTo/Device.cs

@@ -681,7 +681,10 @@ namespace MediaBrowser.Dlna.PlayTo
             var presentationUrl = document.Descendants(uPnpNamespaces.ud.GetName("presentationURL")).FirstOrDefault();
             if (presentationUrl != null)
                 deviceProperties.PresentationUrl = presentationUrl.Value;
-
+            var modelUrl = document.Descendants(uPnpNamespaces.ud.GetName("modelURL")).FirstOrDefault();
+            if (modelUrl != null)
+                deviceProperties.ModelUrl = modelUrl.Value;
+            
 
             deviceProperties.BaseUrl = String.Format("http://{0}:{1}", url.Host, url.Port);
 

+ 4 - 1
MediaBrowser.Dlna/PlayTo/DeviceInfo.cs

@@ -34,6 +34,8 @@ namespace MediaBrowser.Dlna.PlayTo
 
         public string ModelNumber { get; set; }
 
+        public string ModelUrl { get; set; }
+
         public string Manufacturer { get; set; }
 
         public string ManufacturerUrl { get; set; }
@@ -72,7 +74,8 @@ namespace MediaBrowser.Dlna.PlayTo
                 ModelName = ModelName,
                 ModelNumber = ModelNumber,
                 FriendlyName = Name,
-                ManufacturerUrl = ManufacturerUrl
+                ManufacturerUrl = ManufacturerUrl,
+                ModelUrl = ModelUrl
             };
         }
     }

+ 87 - 13
MediaBrowser.Dlna/PlayTo/DlnaController.cs

@@ -270,7 +270,7 @@ namespace MediaBrowser.Dlna.PlayTo
                         playlistItem.StartPositionTicks = newItem.StartPositionTicks;
                         playlistItem.StreamUrl = newItem.StreamUrl;
                         playlistItem.Didl = newItem.Didl;
-                        return _device.SetAvTransport(playlistItem.StreamUrl, playlistItem.DlnaHeaders, playlistItem.Didl);
+                        return _device.SetAvTransport(playlistItem.StreamUrl, GetDlnaHeaders(playlistItem), playlistItem.Didl);
 
                     }
                     return _device.Seek(TimeSpan.FromTicks(command.SeekPositionTicks ?? 0));
@@ -391,16 +391,23 @@ namespace MediaBrowser.Dlna.PlayTo
 
         private PlaylistItem CreatePlaylistItem(BaseItem item, long startPostionTicks, string serverAddress)
         {
-            var streams = _itemRepository.GetMediaStreams(new MediaStreamQuery { ItemId = item.Id }).ToList();
+            var streams = _itemRepository.GetMediaStreams(new MediaStreamQuery
+            {
+                ItemId = item.Id
+
+            }).ToList();
 
             var deviceInfo = _device.Properties;
 
-            var playlistItem = GetPlaylistItem(item, _dlnaManager.GetProfile(deviceInfo.ToDeviceIdentification()));
+            var profile = _dlnaManager.GetProfile(deviceInfo.ToDeviceIdentification());
+
+            var playlistItem = GetPlaylistItem(item, streams, profile);
             playlistItem.StartPositionTicks = startPostionTicks;
+            playlistItem.DeviceProfileName = profile.Name;
 
             if (playlistItem.MediaType == DlnaProfileType.Audio)
             {
-                playlistItem.StreamUrl = StreamHelper.GetAudioUrl(playlistItem, serverAddress);
+                playlistItem.StreamUrl = StreamHelper.GetAudioUrl(deviceInfo, playlistItem, streams, serverAddress);
             }
             else
             {
@@ -410,32 +417,92 @@ namespace MediaBrowser.Dlna.PlayTo
             var didl = DidlBuilder.Build(item, _session.UserId.ToString(), serverAddress, playlistItem.StreamUrl, streams);
             playlistItem.Didl = didl;
 
-            var header = StreamHelper.GetDlnaHeaders(playlistItem);
-            playlistItem.DlnaHeaders = header;
             return playlistItem;
         }
 
-        private PlaylistItem GetPlaylistItem(BaseItem item, DeviceProfile profile)
+        private string GetDlnaHeaders(PlaylistItem item)
+        {
+            var orgOp = item.Transcode ? ";DLNA.ORG_OP=00" : ";DLNA.ORG_OP=01";
+
+            var orgCi = item.Transcode ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";
+
+            const string dlnaflags = ";DLNA.ORG_FLAGS=01500000000000000000000000000000";
+
+            string contentFeatures;
+
+            var container = item.Container.TrimStart('.');
+
+            if (string.Equals(container, "mp3", StringComparison.OrdinalIgnoreCase))
+            {
+                contentFeatures = "DLNA.ORG_PN=MP3";
+            }
+            else if (string.Equals(container, "wma", StringComparison.OrdinalIgnoreCase))
+            {
+                contentFeatures = "DLNA.ORG_PN=WMABASE";
+            }
+            else if (string.Equals(container, "wmw", StringComparison.OrdinalIgnoreCase))
+            {
+                contentFeatures = "DLNA.ORG_PN=WMVMED_BASE";
+            }
+            else if (string.Equals(container, "asf", StringComparison.OrdinalIgnoreCase))
+            {
+                contentFeatures = "DLNA.ORG_PN=WMVMED_BASE";
+            }
+            else if (string.Equals(container, "avi", StringComparison.OrdinalIgnoreCase))
+            {
+                contentFeatures = "DLNA.ORG_PN=AVI";
+            }
+            else if (string.Equals(container, "mkv", StringComparison.OrdinalIgnoreCase))
+            {
+                contentFeatures = "DLNA.ORG_PN=MATROSKA";
+            }
+            else if (string.Equals(container, "mp4", StringComparison.OrdinalIgnoreCase))
+            {
+                contentFeatures = "DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC";
+            }
+            else if (string.Equals(container, "mpeg", StringComparison.OrdinalIgnoreCase))
+            {
+                contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL";
+            }
+            else if (string.Equals(container, "ts", StringComparison.OrdinalIgnoreCase))
+            {
+                contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL";
+            }
+            else if (item.MediaType == DlnaProfileType.Video)
+            {
+                // Default to AVI for video
+                contentFeatures = "DLNA.ORG_PN=AVI";
+            }
+            else
+            {
+                // Default to MP3 for audio
+                contentFeatures = "DLNA.ORG_PN=MP3";
+            }
+
+            return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
+        }
+        
+        private PlaylistItem GetPlaylistItem(BaseItem item, List<MediaStream> mediaStreams, DeviceProfile profile)
         {
             var video = item as Video;
 
             if (video != null)
             {
-                return new PlaylistItemFactory(_itemRepository).Create(video, profile);
+                return new PlaylistItemFactory().Create(video, mediaStreams, profile);
             }
 
             var audio = item as Audio;
 
             if (audio != null)
             {
-                return new PlaylistItemFactory(_itemRepository).Create(audio, profile);
+                return new PlaylistItemFactory().Create(audio, mediaStreams, profile);
             }
 
             var photo = item as Photo;
 
             if (photo != null)
             {
-                return new PlaylistItemFactory(_itemRepository).Create(photo, profile);
+                return new PlaylistItemFactory().Create(photo, profile);
             }
 
             throw new ArgumentException("Unrecognized item type.");
@@ -482,11 +549,18 @@ namespace MediaBrowser.Dlna.PlayTo
                 await _device.SetStop();
                 return true;
             }
+
             nextTrack.PlayState = 1;
-            _logger.Debug("{0} - SetAvTransport Uri: {1} DlnaHeaders: {2}", _device.Properties.Name, nextTrack.StreamUrl, nextTrack.DlnaHeaders);
-            await _device.SetAvTransport(nextTrack.StreamUrl, nextTrack.DlnaHeaders, nextTrack.Didl);
+
+            var dlnaheaders = GetDlnaHeaders(nextTrack);
+
+            _logger.Debug("{0} - SetAvTransport Uri: {1} DlnaHeaders: {2}", _device.Properties.Name, nextTrack.StreamUrl, dlnaheaders);
+
+            await _device.SetAvTransport(nextTrack.StreamUrl, dlnaheaders, nextTrack.Didl);
+         
             if (nextTrack.StartPositionTicks > 0 && !nextTrack.Transcode)
                 await _device.Seek(TimeSpan.FromTicks(nextTrack.StartPositionTicks));
+           
             return true;
         }
 
@@ -508,7 +582,7 @@ namespace MediaBrowser.Dlna.PlayTo
                 return Task.FromResult(false);
 
             prevTrack.PlayState = 1;
-            return _device.SetAvTransport(prevTrack.StreamUrl, prevTrack.DlnaHeaders, prevTrack.Didl);
+            return _device.SetAvTransport(prevTrack.StreamUrl, GetDlnaHeaders(prevTrack), prevTrack.Didl);
         }
 
         #endregion

+ 9 - 11
MediaBrowser.Dlna/PlayTo/PlayToManager.cs

@@ -7,6 +7,7 @@ using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Controller.Session;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Session;
 using System;
 using System.Collections.Concurrent;
 using System.Linq;
@@ -16,7 +17,6 @@ using System.Net.Sockets;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
-using MediaBrowser.Model.Session;
 
 namespace MediaBrowser.Dlna.PlayTo
 {
@@ -54,10 +54,8 @@ namespace MediaBrowser.Dlna.PlayTo
             _config = config;
         }
 
-        public async void Start()
+        public void Start()
         {
-            _logger.Log(LogSeverity.Info, "PlayTo-Manager starting");
-
             _locations = new ConcurrentDictionary<string, DateTime>();
 
             foreach (var network in NetworkInterface.GetAllNetworkInterfaces())
@@ -73,7 +71,7 @@ namespace MediaBrowser.Dlna.PlayTo
 
                 IPAddress localIp = null;
 
-                foreach (UnicastIPAddressInformation ipInfo in network.GetIPProperties().UnicastAddresses)
+                foreach (var ipInfo in network.GetIPProperties().UnicastAddresses)
                 {
                     if (ipInfo.Address.AddressFamily == AddressFamily.InterNetwork)
                     {
@@ -95,8 +93,6 @@ namespace MediaBrowser.Dlna.PlayTo
                 {
                     _logger.ErrorException("Failed to Initilize Socket", e);
                 }
-
-                await Task.Delay(100).ConfigureAwait(false);
             }
         }
 
@@ -139,7 +135,7 @@ namespace MediaBrowser.Dlna.PlayTo
 
                     _logger.Info("SSDP listener - Task completed");
                 }
-                catch (OperationCanceledException c)
+                catch (OperationCanceledException)
                 {
                 }
                 catch (Exception e)
@@ -158,7 +154,7 @@ namespace MediaBrowser.Dlna.PlayTo
                 {
                     await CreateController(uri).ConfigureAwait(false);
                 }
-                catch (OperationCanceledException c)
+                catch (OperationCanceledException)
                 {
                 }
                 catch (Exception ex)
@@ -180,10 +176,12 @@ namespace MediaBrowser.Dlna.PlayTo
                     {
                         socket.SendTo(request, new IPEndPoint(IPAddress.Parse("239.255.255.250"), 1900));
 
-                        await Task.Delay(10000).ConfigureAwait(false);
+                        var delay = _config.Configuration.DlnaOptions.ClientDiscoveryIntervalSeconds * 1000;
+
+                        await Task.Delay(delay).ConfigureAwait(false);
                     }
                 }
-                catch (OperationCanceledException c)
+                catch (OperationCanceledException)
                 {
                 }
                 catch (Exception ex)

+ 18 - 4
MediaBrowser.Dlna/PlayTo/PlaylistItem.cs

@@ -1,4 +1,5 @@
 using MediaBrowser.Controller.Dlna;
+using System.Collections.Generic;
 
 namespace MediaBrowser.Dlna.PlayTo
 {
@@ -14,16 +15,29 @@ namespace MediaBrowser.Dlna.PlayTo
 
         public string Container { get; set; }
 
-        public string MimeType { get; set; }
-
         public int PlayState { get; set; }
 
         public string StreamUrl { get; set; }
 
-        public string DlnaHeaders { get; set; }
-
         public string Didl { get; set; }
 
         public long StartPositionTicks { get; set; }
+
+        public string VideoCodec { get; set; }
+
+        public string AudioCodec { get; set; }
+
+        public List<TranscodingSetting> TranscodingSettings { get; set; }
+
+        public int? AudioStreamIndex { get; set; }
+
+        public int? SubtitleStreamIndex { get; set; }
+
+        public string DeviceProfileName { get; set; }
+        
+        public PlaylistItem()
+        {
+            TranscodingSettings = new List<TranscodingSetting>();
+        }
     }
 }

+ 117 - 107
MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs

@@ -1,9 +1,9 @@
 using MediaBrowser.Controller.Dlna;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Model.Entities;
 using System;
+using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
 using System.Linq;
@@ -12,15 +12,9 @@ namespace MediaBrowser.Dlna.PlayTo
 {
     public class PlaylistItemFactory
     {
-        private readonly IItemRepository _itemRepo;
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
 
-        public PlaylistItemFactory(IItemRepository itemRepo)
-        {
-            _itemRepo = itemRepo;
-        }
-
-        public PlaylistItem Create(Audio item, DeviceProfile profile)
+        public PlaylistItem Create(Audio item, List<MediaStream> mediaStreams, DeviceProfile profile)
         {
             var playlistItem = new PlaylistItem
             {
@@ -28,23 +22,21 @@ namespace MediaBrowser.Dlna.PlayTo
                 MediaType = DlnaProfileType.Audio
             };
 
-            var mediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery
-            {
-                ItemId = item.Id,
-                Type = MediaStreamType.Audio
-            });
-
             var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
 
-            var directPlay = profile.DirectPlayProfiles
-                .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item, audioStream));
-
-            if (directPlay != null)
+            if (profile.CodecProfiles.Where(i => i.Type == CodecType.AudioCodec)
+                .All(i => IsCodecProfileSupported(i, item.Path, null, audioStream)))
             {
-                playlistItem.Transcode = false;
-                playlistItem.Container = Path.GetExtension(item.Path);
+                var directPlay = profile.DirectPlayProfiles
+                    .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item, audioStream));
 
-                return playlistItem;
+                if (directPlay != null)
+                {
+                    playlistItem.Transcode = false;
+                    playlistItem.Container = Path.GetExtension(item.Path);
+
+                    return playlistItem;
+                }
             }
 
             var transcodingProfile = profile.TranscodingProfiles
@@ -53,11 +45,10 @@ namespace MediaBrowser.Dlna.PlayTo
             if (transcodingProfile != null)
             {
                 playlistItem.Transcode = true;
-
+                playlistItem.TranscodingSettings = transcodingProfile.Settings.ToList();
                 playlistItem.Container = "." + transcodingProfile.Container.TrimStart('.');
+                playlistItem.AudioCodec = transcodingProfile.AudioCodec;
             }
-
-            AttachMediaProfile(playlistItem, profile);
             
             return playlistItem;
         }
@@ -87,16 +78,14 @@ namespace MediaBrowser.Dlna.PlayTo
             if (transcodingProfile != null)
             {
                 playlistItem.Transcode = true;
-
+                playlistItem.TranscodingSettings = transcodingProfile.Settings.ToList();
                 playlistItem.Container = "." + transcodingProfile.Container.TrimStart('.');
             }
-
-            AttachMediaProfile(playlistItem, profile);
             
             return playlistItem;
         }
 
-        public PlaylistItem Create(Video item, DeviceProfile profile)
+        public PlaylistItem Create(Video item, List<MediaStream> mediaStreams, DeviceProfile profile)
         {
             var playlistItem = new PlaylistItem
             {
@@ -104,24 +93,22 @@ namespace MediaBrowser.Dlna.PlayTo
                 MediaType = DlnaProfileType.Video
             };
 
-            var mediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery
-            {
-                ItemId = item.Id
-
-            }).ToList();
-
             var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
             var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
 
-            var directPlay = profile.DirectPlayProfiles
-                .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item, videoStream, audioStream));
-
-            if (directPlay != null)
+            if (profile.CodecProfiles.Where(i => i.Type == CodecType.VideoCodec || i.Type == CodecType.VideoAudioCodec)
+                .All(i => IsCodecProfileSupported(i, item.Path, videoStream, audioStream)))
             {
-                playlistItem.Transcode = false;
-                playlistItem.Container = Path.GetExtension(item.Path);
+                var directPlay = profile.DirectPlayProfiles
+                    .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item, videoStream, audioStream));
 
-                return playlistItem;
+                if (directPlay != null)
+                {
+                    playlistItem.Transcode = false;
+                    playlistItem.Container = Path.GetExtension(item.Path);
+
+                    return playlistItem;
+                }
             }
 
             var transcodingProfile = profile.TranscodingProfiles
@@ -130,63 +117,29 @@ namespace MediaBrowser.Dlna.PlayTo
             if (transcodingProfile != null)
             {
                 playlistItem.Transcode = true;
+                playlistItem.TranscodingSettings = transcodingProfile.Settings.ToList();
                 playlistItem.Container = "." + transcodingProfile.Container.TrimStart('.');
+                playlistItem.AudioCodec = transcodingProfile.AudioCodec.Split(',').FirstOrDefault();
+                playlistItem.VideoCodec = transcodingProfile.VideoCodec;
             }
 
-            AttachMediaProfile(playlistItem, profile);
-
             return playlistItem;
         }
 
-        private void AttachMediaProfile(PlaylistItem item, DeviceProfile profile)
-        {
-            var mediaProfile = GetMediaProfile(item, profile);
-
-            if (mediaProfile != null)
-            {
-                item.MimeType = (mediaProfile.MimeType ?? string.Empty).Split('/').LastOrDefault();
-
-                // TODO: Org_pn?
-            }
-        }
-
-        private MediaProfile GetMediaProfile(PlaylistItem item, DeviceProfile profile)
-        {
-            return profile.MediaProfiles.FirstOrDefault(i =>
-            {
-                if (i.Type == item.MediaType)
-                {
-                    if (string.Equals(item.Container.TrimStart('.'), i.Container.TrimStart('.'), StringComparison.OrdinalIgnoreCase))
-                    {
-                        // TODO: Enforce codecs
-                        return true;
-                    }
-                }
-
-                return false;
-            });
-        }
-
         private bool IsSupported(DirectPlayProfile profile, Photo item)
         {
             var mediaPath = item.Path;
 
-            if (profile.Containers.Length > 0)
+            if (profile.Container.Length > 0)
             {
                 // Check container type
                 var mediaContainer = Path.GetExtension(mediaPath);
-                if (!profile.Containers.Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase)))
+                if (!profile.GetContainers().Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase)))
                 {
                     return false;
                 }
             }
 
-            // Check additional conditions
-            if (!profile.Conditions.Any(i => IsConditionSatisfied(i, mediaPath, null, null)))
-            {
-                return false;
-            }
-
             return true;
         }
         
@@ -194,22 +147,16 @@ namespace MediaBrowser.Dlna.PlayTo
         {
             var mediaPath = item.Path;
 
-            if (profile.Containers.Length > 0)
+            if (profile.Container.Length > 0)
             {
                 // Check container type
                 var mediaContainer = Path.GetExtension(mediaPath);
-                if (!profile.Containers.Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase)))
+                if (!profile.GetContainers().Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase)))
                 {
                     return false;
                 }
             }
 
-            // Check additional conditions
-            if (!profile.Conditions.Any(i => IsConditionSatisfied(i, mediaPath, null, audioStream)))
-            {
-                return false;
-            }
-
             return true;
         }
 
@@ -222,11 +169,11 @@ namespace MediaBrowser.Dlna.PlayTo
 
             var mediaPath = item.Path;
 
-            if (profile.Containers.Length > 0)
+            if (profile.Container.Length > 0)
             {
                 // Check container type
                 var mediaContainer = Path.GetExtension(mediaPath);
-                if (!profile.Containers.Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase)))
+                if (!profile.GetContainers().Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase)))
                 {
                     return false;
                 }
@@ -254,12 +201,6 @@ namespace MediaBrowser.Dlna.PlayTo
                 }
             }
 
-            // Check additional conditions
-            if (!profile.Conditions.Any(i => IsConditionSatisfied(i, mediaPath, videoStream, audioStream)))
-            {
-                return false;
-            }
-
             return true;
         }
 
@@ -281,6 +222,24 @@ namespace MediaBrowser.Dlna.PlayTo
             return true;
         }
 
+        private bool IsCodecProfileSupported(CodecProfile profile, string mediaPath, MediaStream videoStream, MediaStream audioStream)
+        {
+            var codecs = profile.GetCodecs();
+            var stream = profile.Type == CodecType.VideoCodec ? videoStream : audioStream;
+            var existingCodec = (stream == null ? null : stream.Codec) ?? string.Empty;
+
+            if (codecs.Count == 0 || codecs.Contains(existingCodec, StringComparer.OrdinalIgnoreCase))
+            {
+                // Check additional conditions
+                if (!profile.Conditions.Any(i => IsConditionSatisfied(i, mediaPath, videoStream, audioStream)))
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
         /// <summary>
         /// Determines whether [is condition satisfied] [the specified condition].
         /// </summary>
@@ -292,30 +251,75 @@ namespace MediaBrowser.Dlna.PlayTo
         /// <exception cref="System.InvalidOperationException">Unexpected ProfileConditionType</exception>
         private bool IsConditionSatisfied(ProfileCondition condition, string mediaPath, MediaStream videoStream, MediaStream audioStream)
         {
-            var actualValue = GetConditionValue(condition, mediaPath, videoStream, audioStream);
+            if (condition.Property == ProfileConditionValue.Has64BitOffsets)
+            {
+                // TODO: Determine how to evaluate this
+            }
 
-            if (actualValue.HasValue)
+            if (condition.Property == ProfileConditionValue.VideoProfile)
             {
-                long expected;
-                if (long.TryParse(condition.Value, NumberStyles.Any, _usCulture, out expected))
+                var profile = videoStream == null ? null : videoStream.Profile;
+
+                if (!string.IsNullOrWhiteSpace(profile))
                 {
                     switch (condition.Condition)
                     {
                         case ProfileConditionType.Equals:
-                            return actualValue.Value == expected;
-                        case ProfileConditionType.GreaterThanEqual:
-                            return actualValue.Value >= expected;
-                        case ProfileConditionType.LessThanEqual:
-                            return actualValue.Value <= expected;
+                            return string.Equals(profile, condition.Value, StringComparison.OrdinalIgnoreCase);
                         case ProfileConditionType.NotEquals:
-                            return actualValue.Value != expected;
+                            return !string.Equals(profile, condition.Value, StringComparison.OrdinalIgnoreCase);
                         default:
                             throw new InvalidOperationException("Unexpected ProfileConditionType");
                     }
                 }
             }
 
-            return false;
+            else if (condition.Property == ProfileConditionValue.AudioProfile)
+            {
+                var profile = audioStream == null ? null : audioStream.Profile;
+
+                if (!string.IsNullOrWhiteSpace(profile))
+                {
+                    switch (condition.Condition)
+                    {
+                        case ProfileConditionType.Equals:
+                            return string.Equals(profile, condition.Value, StringComparison.OrdinalIgnoreCase);
+                        case ProfileConditionType.NotEquals:
+                            return !string.Equals(profile, condition.Value, StringComparison.OrdinalIgnoreCase);
+                        default:
+                            throw new InvalidOperationException("Unexpected ProfileConditionType");
+                    }
+                }
+            }
+
+            else
+            {
+                var actualValue = GetConditionValue(condition, mediaPath, videoStream, audioStream);
+
+                if (actualValue.HasValue)
+                {
+                    long expected;
+                    if (long.TryParse(condition.Value, NumberStyles.Any, _usCulture, out expected))
+                    {
+                        switch (condition.Condition)
+                        {
+                            case ProfileConditionType.Equals:
+                                return actualValue.Value == expected;
+                            case ProfileConditionType.GreaterThanEqual:
+                                return actualValue.Value >= expected;
+                            case ProfileConditionType.LessThanEqual:
+                                return actualValue.Value <= expected;
+                            case ProfileConditionType.NotEquals:
+                                return actualValue.Value != expected;
+                            default:
+                                throw new InvalidOperationException("Unexpected ProfileConditionType");
+                        }
+                    }
+                }
+            }
+
+            // Value doesn't exist in metadata. Fail it if required.
+            return !condition.IsRequired;
         }
 
         /// <summary>
@@ -347,6 +351,12 @@ namespace MediaBrowser.Dlna.PlayTo
                     return videoStream == null ? null : videoStream.Width;
                 case ProfileConditionValue.VideoLevel:
                     return videoStream == null ? null : ConvertToLong(videoStream.Level);
+                case ProfileConditionValue.VideoPacketLength:
+                    // TODO: Determine how to get this
+                    return null;
+                case ProfileConditionValue.VideoTimestamp:
+                    // TODO: Determine how to get this
+                    return null;
                 default:
                     throw new InvalidOperationException("Unexpected Property");
             }

+ 3 - 3
MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs

@@ -49,7 +49,7 @@ namespace MediaBrowser.Dlna.PlayTo
             {
                 Url = url.ToString(),
                 UserAgent = USERAGENT,
-                LogRequest = _config.Configuration.DlnaOptions.EnablePlayToDebugLogging
+                LogRequest = _config.Configuration.DlnaOptions.EnableDebugLogging
             };
 
             options.RequestHeaders["HOST"] = ip + ":" + port;
@@ -88,7 +88,7 @@ namespace MediaBrowser.Dlna.PlayTo
             {
                 Url = url.ToString(),
                 UserAgent = USERAGENT,
-                LogRequest = _config.Configuration.DlnaOptions.EnablePlayToDebugLogging
+                LogRequest = _config.Configuration.DlnaOptions.EnableDebugLogging
             };
 
             options.RequestHeaders["FriendlyName.DLNA.ORG"] = FriendlyName;
@@ -112,7 +112,7 @@ namespace MediaBrowser.Dlna.PlayTo
             {
                 Url = url.ToString(),
                 UserAgent = USERAGENT,
-                LogRequest = _config.Configuration.DlnaOptions.EnablePlayToDebugLogging
+                LogRequest = _config.Configuration.DlnaOptions.EnableDebugLogging
             };
 
             options.RequestHeaders["SOAPAction"] = soapAction;

+ 31 - 158
MediaBrowser.Dlna/PlayTo/StreamHelper.cs

@@ -1,6 +1,5 @@
-using MediaBrowser.Model.Dto;
+using MediaBrowser.Controller.Dlna;
 using MediaBrowser.Model.Entities;
-using System;
 using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
@@ -9,91 +8,21 @@ namespace MediaBrowser.Dlna.PlayTo
 {
     class StreamHelper
     {
-        /// <summary>
-        /// Gets the dlna headers.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <returns></returns>
-        internal static string GetDlnaHeaders(PlaylistItem item)
-        {
-            var orgOp = item.Transcode ? ";DLNA.ORG_OP=00" : ";DLNA.ORG_OP=01";
-
-            var orgCi = item.Transcode ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";
-
-            const string dlnaflags = ";DLNA.ORG_FLAGS=01500000000000000000000000000000";
-
-            var contentFeatures = string.Empty;
-
-            if (string.Equals(item.Container, "mp3", StringComparison.OrdinalIgnoreCase))
-            {
-                contentFeatures = "DLNA.ORG_PN=MP3";
-            }
-            else if (string.Equals(item.Container, "wma", StringComparison.OrdinalIgnoreCase))
-            {
-                contentFeatures = "DLNA.ORG_PN=WMABASE";
-            }
-            else if (string.Equals(item.Container, "wmw", StringComparison.OrdinalIgnoreCase))
-            {
-                contentFeatures = "DLNA.ORG_PN=WMVMED_BASE";
-            }
-            else if (string.Equals(item.Container, "asf", StringComparison.OrdinalIgnoreCase))
-            {
-                contentFeatures = "DLNA.ORG_PN=WMVMED_BASE";
-            }
-            else if (string.Equals(item.Container, "avi", StringComparison.OrdinalIgnoreCase))
-            {
-                contentFeatures = "DLNA.ORG_PN=AVI";
-            }
-            else if (string.Equals(item.Container, "mkv", StringComparison.OrdinalIgnoreCase))
-            {
-                contentFeatures = "DLNA.ORG_PN=MATROSKA";
-            }
-            else if (string.Equals(item.Container, "mp4", StringComparison.OrdinalIgnoreCase))
-            {
-                contentFeatures = "DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC";
-            }
-            else if (string.Equals(item.Container, "mpeg", StringComparison.OrdinalIgnoreCase))
-            {
-                contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL";
-            }
-            else if (string.Equals(item.Container, "ts", StringComparison.OrdinalIgnoreCase))
-            {
-                contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL";
-            }
-            else if (item.MediaType == Controller.Dlna.DlnaProfileType.Video)
-            {
-                //Default to AVI for video
-                contentFeatures = "DLNA.ORG_PN=AVI";
-            }
-            else
-            {
-                //Default to MP3 for audio
-                contentFeatures = "DLNA.ORG_PN=MP3";
-            }
-
-            return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';');
-        }
-
-        #region Audio
-
         /// <summary>
         /// Gets the audio URL.
         /// </summary>
+        /// <param name="deviceProperties">The device properties.</param>
         /// <param name="item">The item.</param>
+        /// <param name="streams">The streams.</param>
         /// <param name="serverAddress">The server address.</param>
         /// <returns>System.String.</returns>
-        internal static string GetAudioUrl(PlaylistItem item, string serverAddress)
+        internal static string GetAudioUrl(DeviceInfo deviceProperties, PlaylistItem item, List<MediaStream> streams, string serverAddress)
         {
-            if (!item.Transcode)
-                return string.Format("{0}/audio/{1}/stream{2}?Static=True", serverAddress, item.ItemId, item.Container);
+            var dlnaCommand = BuildDlnaUrl(item.DeviceProfileName, item.MediaSourceId, deviceProperties.UUID, !item.Transcode, null, item.AudioCodec, item.AudioStreamIndex, item.SubtitleStreamIndex, null, 128000, item.StartPositionTicks, item.TranscodingSettings);
 
-            return string.Format("{0}/audio/{1}/stream.mp3?AudioCodec=Mp3", serverAddress, item.ItemId);
+            return string.Format("{0}/audio/{1}/stream{2}?{3}", serverAddress, item.ItemId, "." + item.Container.TrimStart('.'), dlnaCommand);
         }
 
-        #endregion
-
-        #region Video
-
         /// <summary>
         /// Gets the video URL.
         /// </summary>
@@ -104,97 +33,41 @@ namespace MediaBrowser.Dlna.PlayTo
         /// <returns>The url to send to the device</returns>
         internal static string GetVideoUrl(DeviceInfo deviceProperties, PlaylistItem item, List<MediaStream> streams, string serverAddress)
         {
-            string dlnaCommand = string.Empty;
-            if (!item.Transcode)
-            {
-                dlnaCommand = BuildDlnaUrl(deviceProperties.UUID, !item.Transcode, null, null, null, null, null, null, null, null, null, null, item.MimeType);
-                return string.Format("{0}/Videos/{1}/stream{2}?{3}", serverAddress, item.ItemId, item.Container, dlnaCommand);
-            }
-            var videostream = streams.Where(m => m.Type == MediaStreamType.Video).OrderBy(m => m.IsDefault).FirstOrDefault();
-            var audiostream = streams.Where(m => m.Type == MediaStreamType.Audio).OrderBy(m => m.IsDefault).FirstOrDefault();
-
-            var videoCodec = GetVideoCodec(videostream);
-            var audioCodec = GetAudioCodec(audiostream);
-            int? videoBitrate = null;
-            int? audioBitrate = null;
-            int? audioChannels = null;
-
-            if (videoCodec != VideoCodecs.Copy)
-                videoBitrate = 2000000;
+            var dlnaCommand = BuildDlnaUrl(item.DeviceProfileName, item.MediaSourceId, deviceProperties.UUID, !item.Transcode, item.VideoCodec, item.AudioCodec, item.AudioStreamIndex, item.SubtitleStreamIndex, 1500000, 128000, item.StartPositionTicks, item.TranscodingSettings);
 
-            if (audioCodec != AudioCodecs.Copy)
-            {
-                audioBitrate = 128000;
-                audioChannels = 2;
-            }
-
-            dlnaCommand = BuildDlnaUrl(deviceProperties.UUID, !item.Transcode, videoCodec, audioCodec, null, null, videoBitrate, audioChannels, audioBitrate, item.StartPositionTicks, "baseline", "3", item.MimeType);
             return string.Format("{0}/Videos/{1}/stream{2}?{3}", serverAddress, item.ItemId, item.Container, dlnaCommand);
         }
 
-        /// <summary>
-        /// Gets the video codec.
-        /// </summary>
-        /// <param name="videoStream">The video stream.</param>
-        /// <returns></returns>
-        private static VideoCodecs GetVideoCodec(MediaStream videoStream)
-        {
-            switch (videoStream.Codec.ToLower())
-            {
-                case "h264":
-                case "mpeg4":
-                    return VideoCodecs.Copy;
-
-            }
-            return VideoCodecs.H264;
-        }
-
-        /// <summary>
-        /// Gets the audio codec.
-        /// </summary>
-        /// <param name="audioStream">The audio stream.</param>
-        /// <returns></returns>
-        private static AudioCodecs GetAudioCodec(MediaStream audioStream)
-        {
-            if (audioStream != null)
-            {
-                switch (audioStream.Codec.ToLower())
-                {
-                    case "aac":
-                    case "mp3":
-                    case "wma":
-                        return AudioCodecs.Copy;
-
-                }
-            }
-            return AudioCodecs.Aac;
-        }
-
         /// <summary>
         /// Builds the dlna URL.
         /// </summary>
-        private static string BuildDlnaUrl(string deviceID, bool isStatic, VideoCodecs? videoCodec, AudioCodecs? audioCodec, int? subtitleIndex, int? audiostreamIndex, int? videoBitrate, int? audiochannels, int? audioBitrate, long? startPositionTicks, string profile, string videoLevel, string mimeType)
+        private static string BuildDlnaUrl(string deviceProfileName, string mediaSourceId, string deviceID, bool isStatic, string videoCodec, string audioCodec, int? audiostreamIndex, int? subtitleIndex, int? videoBitrate, int? audioBitrate, long? startPositionTicks, List<TranscodingSetting> settings)
         {
-            var usCulture = new CultureInfo("en-US");
+            var profile = settings.Where(i => i.Name == TranscodingSettingType.VideoProfile).Select(i => i.Value).FirstOrDefault();
+            var videoLevel = settings.Where(i => i.Name == TranscodingSettingType.VideoLevel).Select(i => i.Value).FirstOrDefault();
+            var maxAudioChannels = settings.Where(i => i.Name == TranscodingSettingType.MaxAudioChannels).Select(i => i.Value).FirstOrDefault();
 
-            var dlnaparam = string.Format("Params={0};", deviceID);
-            dlnaparam += isStatic ? "true;" : "false;";
-            dlnaparam += videoCodec.HasValue ? videoCodec.Value + ";" : ";";
-            dlnaparam += audioCodec.HasValue ? audioCodec.Value + ";" : ";";
-            dlnaparam += audiostreamIndex.HasValue ? audiostreamIndex.Value.ToString(usCulture) + ";" : ";";
-            dlnaparam += subtitleIndex.HasValue ? subtitleIndex.Value.ToString(usCulture) + ";" : ";";
-            dlnaparam += videoBitrate.HasValue ? videoBitrate.Value.ToString(usCulture) + ";" : ";";
-            dlnaparam += audioBitrate.HasValue ? audioBitrate.Value.ToString(usCulture) + ";" : ";";
-            dlnaparam += audiochannels.HasValue ? audiochannels.Value.ToString(usCulture) + ";" : ";";
-            dlnaparam += startPositionTicks.HasValue ? startPositionTicks.Value.ToString(usCulture) + ";" : ";";
-            dlnaparam += profile + ";";
-            dlnaparam += videoLevel + ";";
-            dlnaparam += mimeType + ";";
+            var usCulture = new CultureInfo("en-US");
 
-            return dlnaparam;
+            var list = new List<string>
+            {
+                deviceProfileName ?? string.Empty,
+                deviceID ?? string.Empty,
+                mediaSourceId ?? string.Empty,
+                isStatic.ToString().ToLower(),
+                videoCodec ?? string.Empty,
+                audioCodec ?? string.Empty,
+                audiostreamIndex.HasValue ? audiostreamIndex.Value.ToString(usCulture) : string.Empty,
+                subtitleIndex.HasValue ? subtitleIndex.Value.ToString(usCulture) : string.Empty,
+                videoBitrate.HasValue ? videoBitrate.Value.ToString(usCulture) : string.Empty,
+                audioBitrate.HasValue ? audioBitrate.Value.ToString(usCulture) : string.Empty,
+                maxAudioChannels ?? string.Empty,
+                startPositionTicks.HasValue ? startPositionTicks.Value.ToString(usCulture) : string.Empty,
+                profile ?? string.Empty,
+                videoLevel ?? string.Empty
+            };
+
+            return string.Format("Params={0}", string.Join(";", list.ToArray()));
         }
-
-        #endregion
-
     }
 }

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

@@ -0,0 +1,59 @@
+using MediaBrowser.Controller.Dlna;
+
+namespace MediaBrowser.Dlna.Profiles
+{
+    public class DefaultProfile : DeviceProfile
+    {
+        public DefaultProfile()
+        {
+            ProtocolInfo = "DLNA";
+
+            ClientType = "DLNA";
+            Manufacturer = "Media Browser";
+            ModelDescription = "Media Browser";
+            ModelName = "Media Browser";
+            ModelNumber = "Media Browser";
+            ModelUrl = "http://mediabrowser3.com/";
+            ManufacturerUrl = "http://mediabrowser3.com/";
+
+            TranscodingProfiles = new[]
+            {
+                new TranscodingProfile
+                {
+                    Container = "mp3",
+                    AudioCodec = "mp3",
+                    Type = DlnaProfileType.Audio
+                },
+
+                new TranscodingProfile
+                {
+                    Container = "ts",
+                    Type = DlnaProfileType.Video,
+                    AudioCodec = "aac",
+                    VideoCodec = "h264",
+
+                    Settings = new []
+                    {
+                        new TranscodingSetting {Name = TranscodingSettingType.VideoLevel, Value = "3"},
+                        new TranscodingSetting {Name = TranscodingSettingType.VideoProfile, Value = "baseline"}
+                    }
+                }
+            };
+
+            DirectPlayProfiles = new[]
+            {
+                new DirectPlayProfile
+                {
+                    Container = "mp3,wma",
+                    Type = DlnaProfileType.Audio
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "avi,mp4",
+                    Type = DlnaProfileType.Video
+                }
+            };
+        }
+    }
+}

+ 27 - 0
MediaBrowser.Dlna/Profiles/DenonAvrProfile.cs

@@ -0,0 +1,27 @@
+using MediaBrowser.Controller.Dlna;
+
+namespace MediaBrowser.Dlna.Profiles
+{
+    public class DenonAvrProfile : DefaultProfile
+    {
+        public DenonAvrProfile()
+        {
+            Name = "Denon AVR";
+
+            Identification = new DeviceIdentification
+            {
+                FriendlyName = @"Denon:\[AVR:.*",
+                Manufacturer = "Denon"
+            };
+
+            DirectPlayProfiles = new[]
+            {
+                new DirectPlayProfile
+                {
+                    Container = "mp3,flac,m4a,wma",
+                    Type = DlnaProfileType.Audio
+                },
+            };
+        }
+    }
+}

+ 192 - 0
MediaBrowser.Dlna/Profiles/LgTvProfile.cs

@@ -0,0 +1,192 @@
+using MediaBrowser.Controller.Dlna;
+
+namespace MediaBrowser.Dlna.Profiles
+{
+    public class LgTvProfile : DefaultProfile
+    {
+        public LgTvProfile()
+        {
+            Name = "LG Smart TV";
+
+            TimelineOffsetSeconds = 10;
+
+            Identification = new DeviceIdentification
+            {
+                FriendlyName = @"LG.*",
+
+                Headers = new[]
+               {
+                   new HttpHeaderInfo
+                   {
+                       Name = "User-Agent",
+                       Value = "LG",
+                       Match = HeaderMatchType.Substring
+                   }
+               }
+            };
+
+            TranscodingProfiles = new[]
+           {
+               new TranscodingProfile
+               {
+                   Container = "mp3",
+                   AudioCodec = "mp3",
+                   Type = DlnaProfileType.Audio
+               },
+               new TranscodingProfile
+               {
+                   Container = "ts",
+                   AudioCodec = "ac3",
+                   VideoCodec = "h264",
+                   Type = DlnaProfileType.Video
+               },
+               new TranscodingProfile
+               {
+                   Container = "jpeg",
+                   Type = DlnaProfileType.Photo
+               }
+           };
+
+            DirectPlayProfiles = new[]
+            {
+                new DirectPlayProfile
+                {
+                    Container = "ts",
+                    VideoCodec = "h264",
+                    AudioCodec = "aac,ac3,mp3",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "mkv",
+                    VideoCodec = "h264",
+                    AudioCodec = "aac,ac3,mp3",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "mp4",
+                    VideoCodec = "h264,mpeg4",
+                    AudioCodec = "aac,ac3,mp3",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "mp3",
+                    AudioCodec = "mp3",
+                    Type = DlnaProfileType.Audio
+                },
+                new DirectPlayProfile
+                {
+                    Container = "jpeg",
+                    Type = DlnaProfileType.Photo
+                }
+            };
+
+            ContainerProfiles = new[]
+            {
+                new ContainerProfile
+                {
+                    Type = DlnaProfileType.Photo,
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Width,
+                            Value = "1920"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Height,
+                            Value = "1080"
+                        }
+                    }
+                }
+            };
+
+            CodecProfiles = new[]
+           {
+               new CodecProfile
+               {
+                   Type = CodecType.VideoCodec,
+                   Codec = "mpeg4",
+
+                   Conditions = new[]
+                   {
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.Width,
+                           Value = "1920"
+                       },
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.Height,
+                           Value = "1080"
+                       },
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.VideoFramerate,
+                           Value = "30"
+                       }
+                   }
+               },
+
+               new CodecProfile
+               {
+                   Type = CodecType.VideoCodec,
+                   Codec = "h264",
+
+                   Conditions = new[]
+                   {
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.Width,
+                           Value = "1920"
+                       },
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.Height,
+                           Value = "1080"
+                       },
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.VideoFramerate,
+                           Value = "30"
+                       },
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.VideoLevel,
+                           Value = "41"
+                       }
+                   }
+               },
+
+               new CodecProfile
+               {
+                   Type = CodecType.VideoAudioCodec,
+                   Codec = "ac3,aac,mp3",
+
+                   Conditions = new[]
+                   {
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.AudioChannels,
+                           Value = "6"
+                       }
+                   }
+               }
+           };
+        }
+    }
+}

+ 33 - 0
MediaBrowser.Dlna/Profiles/LinksysDMA2100Profile.cs

@@ -0,0 +1,33 @@
+using MediaBrowser.Controller.Dlna;
+
+namespace MediaBrowser.Dlna.Profiles
+{
+    public class LinksysDMA2100Profile : DefaultProfile
+    {
+        public LinksysDMA2100Profile()
+        {
+            // Linksys DMA2100us does not need any transcoding of the formats we support statically
+            Name = "Linksys DMA2100";
+
+            Identification = new DeviceIdentification
+            {
+                ModelName = "DMA2100us"
+            };
+
+            DirectPlayProfiles = new[]
+            {
+                new DirectPlayProfile
+                {
+                    Container = "mp3,flac,m4a,wma",
+                    Type = DlnaProfileType.Audio
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "avi,mp4,mkv,ts",
+                    Type = DlnaProfileType.Video
+                }
+            };
+        }
+    }
+}

+ 186 - 0
MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs

@@ -0,0 +1,186 @@
+using MediaBrowser.Controller.Dlna;
+
+namespace MediaBrowser.Dlna.Profiles
+{
+   public class PanasonicVieraProfile : DefaultProfile
+    {
+       public PanasonicVieraProfile()
+       {
+           Name = "Panasonic Viera";
+
+           Identification = new DeviceIdentification
+           {
+               FriendlyName = @"VIERA",
+               Manufacturer = "Panasonic",
+
+               Headers = new[]
+               {
+                   new HttpHeaderInfo
+                   {
+                       Name = "User-Agent",
+                       Value = "Panasonic MIL DLNA",
+                       Match = HeaderMatchType.Substring
+                   }
+               }
+           };
+
+           TimelineOffsetSeconds = 10;
+
+           TranscodingProfiles = new[]
+           {
+               new TranscodingProfile
+               {
+                   Container = "mp3",
+                   AudioCodec = "mp3",
+                   Type = DlnaProfileType.Audio
+               },
+               new TranscodingProfile
+               {
+                   Container = "ts",
+                   AudioCodec = "ac3",
+                   VideoCodec = "h264",
+                   Type = DlnaProfileType.Video
+               },
+               new TranscodingProfile
+               {
+                   Container = "jpeg",
+                   Type = DlnaProfileType.Photo
+               }
+           };
+
+           DirectPlayProfiles = new[]
+           {
+               new DirectPlayProfile
+               {
+                   Container = "mpeg",
+                   VideoCodec = "mpeg2video,mpeg4",
+                   AudioCodec = "ac3,mp3",
+                   Type = DlnaProfileType.Video
+               },
+
+               new DirectPlayProfile
+               {
+                   Container = "mkv",
+                   VideoCodec = "h264",
+                   AudioCodec = "aac,ac3,mp3,pcm",
+                   Type = DlnaProfileType.Video
+               },
+
+               new DirectPlayProfile
+               {
+                   Container = "ts",
+                   VideoCodec = "h264",
+                   AudioCodec = "aac,mp3",
+                   Type = DlnaProfileType.Video
+               },
+
+               new DirectPlayProfile
+               {
+                   Container = "mp4",
+                   VideoCodec = "h264",
+                   AudioCodec = "aac,ac3,mp3,pcm",
+                   Type = DlnaProfileType.Video
+               },
+
+               new DirectPlayProfile
+               {
+                   Container = "mov",
+                   VideoCodec = "h264",
+                   AudioCodec = "aac,pcm",
+                   Type = DlnaProfileType.Video
+               },
+
+               new DirectPlayProfile
+               {
+                   Container = "avi",
+                   VideoCodec = "mpeg4",
+                   AudioCodec = "pcm",
+                   Type = DlnaProfileType.Video
+               },
+
+               new DirectPlayProfile
+               {
+                   Container = "flv",
+                   VideoCodec = "h264",
+                   AudioCodec = "aac",
+                   Type = DlnaProfileType.Video
+               },
+
+               new DirectPlayProfile
+               {
+                   Container = "mp3",
+                   AudioCodec = "mp3",
+                   Type = DlnaProfileType.Audio
+               },
+
+               new DirectPlayProfile
+               {
+                   Container = "mp4",
+                   AudioCodec = "aac",
+                   Type = DlnaProfileType.Audio
+               },
+
+               new DirectPlayProfile
+               {
+                   Container = "jpeg",
+                   Type = DlnaProfileType.Photo
+               }
+           };
+
+           ContainerProfiles = new[]
+            {
+                new ContainerProfile
+                {
+                    Type = DlnaProfileType.Photo,
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Width,
+                            Value = "1920"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Height,
+                            Value = "1080"
+                        }
+                    }
+                }
+            };
+
+           CodecProfiles = new[]
+           {
+               new CodecProfile
+               {
+                   Type = CodecType.VideoCodec,
+
+                   Conditions = new[]
+                   {
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.Width,
+                           Value = "1920"
+                       },
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.Height,
+                           Value = "1080"
+                       },
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.VideoBitDepth,
+                           Value = "8",
+                           IsRequired = false
+                       }
+                   }
+               }
+           };
+       }
+    }
+}

+ 315 - 0
MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs

@@ -0,0 +1,315 @@
+using MediaBrowser.Controller.Dlna;
+
+namespace MediaBrowser.Dlna.Profiles
+{
+    public class SamsungSmartTvProfile : DefaultProfile
+    {
+        public SamsungSmartTvProfile()
+        {
+            Name = "Samsung Smart TV";
+
+            SupportsAlbumArtInDidl = true;
+
+            Identification = new DeviceIdentification
+            {
+                ModelUrl = "samsung.com"
+            };
+
+            TranscodingProfiles = new[]
+           {
+               new TranscodingProfile
+               {
+                   Container = "mp3",
+                   AudioCodec = "mp3",
+                   Type = DlnaProfileType.Audio
+               },
+               new TranscodingProfile
+               {
+                   Container = "ts",
+                   AudioCodec = "ac3",
+                   VideoCodec = "h264",
+                   Type = DlnaProfileType.Video
+               },
+               new TranscodingProfile
+               {
+                   Container = "jpeg",
+                   Type = DlnaProfileType.Photo
+               }
+           };
+
+            DirectPlayProfiles = new[]
+            {
+                new DirectPlayProfile
+                {
+                    Container = "asf",
+                    VideoCodec = "h264,mpeg4,mjpeg",
+                    AudioCodec = "mp3,ac3,wmav2,wmapro,wmavoice",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "avi",
+                    VideoCodec = "h264,mpeg4,mjpeg",
+                    AudioCodec = "mp3,ac3,dca",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "mkv",
+                    VideoCodec = "h264,mpeg4,mjpeg4",
+                    AudioCodec = "mp3,ac3,dca,aac",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "mp4",
+                    VideoCodec = "h264,mpeg4",
+                    AudioCodec = "mp3,aac",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "3gpp",
+                    VideoCodec = "h264,mpeg4",
+                    AudioCodec = "aac,he-aac",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "mpg,mpeg",
+                    VideoCodec = "mpeg1video,mpeg2video,h264",
+                    AudioCodec = "ac3,mp2,mp3,aac",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "vro,vob",
+                    VideoCodec = "mpeg1video,mpeg2video",
+                    AudioCodec = "ac3,mp2,mp3",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "ts",
+                    VideoCodec = "mpeg2video,h264,vc1",
+                    AudioCodec = "ac3,aac,mp3,eac3",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "asf",
+                    VideoCodec = "wmv2,wmv3",
+                    AudioCodec = "wmav2,wmavoice",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "mp3",
+                    AudioCodec = "mp3",
+                    Type = DlnaProfileType.Audio
+                },
+                new DirectPlayProfile
+                {
+                    Container = "jpeg",
+                    Type = DlnaProfileType.Photo
+                }
+            };
+
+            ContainerProfiles = new[]
+            {
+                new ContainerProfile
+                {
+                    Type = DlnaProfileType.Photo,
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Width,
+                            Value = "1920"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Height,
+                            Value = "1080"
+                        }
+                    }
+                }
+            };
+
+            CodecProfiles = new[]
+           {
+               new CodecProfile
+               {
+                   Type = CodecType.VideoCodec,
+                   Codec = "mpeg2video",
+
+                   Conditions = new[]
+                   {
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.Width,
+                           Value = "1920"
+                       },
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.Height,
+                           Value = "1080"
+                       },
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.VideoFramerate,
+                           Value = "30"
+                       },
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.VideoBitrate,
+                           Value = "30720000"
+                       }
+                   }
+               },
+
+               new CodecProfile
+               {
+                   Type = CodecType.VideoCodec,
+                   Codec = "mpeg4",
+
+                   Conditions = new[]
+                   {
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.Width,
+                           Value = "1920"
+                       },
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.Height,
+                           Value = "1080"
+                       },
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.VideoFramerate,
+                           Value = "30"
+                       },
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.VideoBitrate,
+                           Value = "8192000"
+                       }
+                   }
+               },
+
+               new CodecProfile
+               {
+                   Type = CodecType.VideoCodec,
+                   Codec = "h264",
+
+                   Conditions = new[]
+                   {
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.Width,
+                           Value = "1920"
+                       },
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.Height,
+                           Value = "1080"
+                       },
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.VideoFramerate,
+                           Value = "30"
+                       },
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.VideoBitrate,
+                           Value = "37500000"
+                       },
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.VideoLevel,
+                           Value = "41"
+                       }
+                   }
+               },
+
+               new CodecProfile
+               {
+                   Type = CodecType.VideoCodec,
+                   Codec = "wmv2,wmv3,vc1",
+
+                   Conditions = new[]
+                   {
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.Width,
+                           Value = "1920"
+                       },
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.Height,
+                           Value = "1080"
+                       },
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.VideoFramerate,
+                           Value = "30"
+                       },
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.VideoBitrate,
+                           Value = "25600000"
+                       }
+                   }
+               },
+
+               new CodecProfile
+               {
+                   Type = CodecType.VideoAudioCodec,
+                   Codec = "ac3,wmav2,dca,aac,mp3",
+
+                   Conditions = new[]
+                   {
+                       new ProfileCondition
+                       {
+                           Condition = ProfileConditionType.LessThanEqual,
+                           Property = ProfileConditionValue.AudioChannels,
+                           Value = "6"
+                       }
+                   }
+               }
+           };
+
+            MediaProfiles = new[]
+            {
+                new MediaProfile
+                {
+                    Container = "mkv",
+                    MimeType = "video/x-mkv",
+                    Type = DlnaProfileType.Video
+                }
+            };
+
+        }
+    }
+}

File diff suppressed because it is too large
+ 19 - 0
MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs


File diff suppressed because it is too large
+ 35 - 0
MediaBrowser.Dlna/Profiles/SonyBlurayPlayerProfile.cs


+ 286 - 0
MediaBrowser.Dlna/Profiles/SonyBravia2010Profile.cs

@@ -0,0 +1,286 @@
+using MediaBrowser.Controller.Dlna;
+
+namespace MediaBrowser.Dlna.Profiles
+{
+    public class SonyBravia2010Profile : DefaultProfile
+    {
+        public SonyBravia2010Profile()
+        {
+            Name = "Sony Bravia (2010)";
+
+            Identification = new DeviceIdentification
+            {
+                FriendlyName = @"KDL-\d{2}[EHLNPB]X\d[01]\d.*",
+                Manufacturer = "Sony",
+
+                Headers = new[]
+                {
+                    new HttpHeaderInfo
+                    {
+                        Name = "X-AV-Client-Info",
+                        Value = @".*KDL-\d{2}[EHLNPB]X\d[01]\d.*",
+                        Match = HeaderMatchType.Regex
+                    }
+                }
+            };
+
+            ModelName = "Windows Media Player Sharing";
+            ModelNumber = "3.0";
+            ModelUrl = "http://www.microsoft.com/";
+            Manufacturer = "Microsoft Corporation";
+            ManufacturerUrl = "http://www.microsoft.com/";
+            SonyAggregationFlags = "10";
+            ProtocolInfo =
+                "http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000";
+
+            TranscodingProfiles = new[]
+            {
+                new TranscodingProfile
+                {
+                    Container = "mp3",
+                    AudioCodec = "mp3",
+                    Type = DlnaProfileType.Audio
+                },
+                new TranscodingProfile
+                {
+                    Container = "ts",
+                    VideoCodec = "h264",
+                    AudioCodec = "ac3,aac",
+                    Type = DlnaProfileType.Video,
+                    EnableMpegtsM2TsMode = true
+                },
+                new TranscodingProfile
+                {
+                    Container = "jpeg",
+                    Type = DlnaProfileType.Photo
+                }
+            };
+
+            DirectPlayProfiles = new[]
+            {
+                new DirectPlayProfile
+                {
+                    Container = "ts",
+                    VideoCodec = "h264",
+                    AudioCodec = "ac3,aac,mp3",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "ts",
+                    VideoCodec = "mpeg1video,mpeg2video",
+                    AudioCodec = "mp3,mp2",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "mpeg",
+                    VideoCodec = "mpeg2video,mpeg1video",
+                    AudioCodec = "mp3,mp2",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "mp3",
+                    AudioCodec = "mp3",
+                    Type = DlnaProfileType.Audio
+                }
+            };
+
+            MediaProfiles = new[]
+            {
+                new MediaProfile
+                {
+                    Container = "ts",
+                    VideoCodec="h264",
+                    AudioCodec="ac3,aac,mp3",
+                    MimeType = "video/vnd.dlna.mpeg-tts",
+                    OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
+                    Type = DlnaProfileType.Video,
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="192"},
+                        new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoTimestamp, Value="1"}
+                    }
+                },
+
+                new MediaProfile
+                {
+                    Container = "ts",
+                    VideoCodec="h264",
+                    AudioCodec="ac3,aac,mp3",
+                    MimeType = "video/mpeg",
+                    OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
+                    Type = DlnaProfileType.Video,
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="188"}
+                    }
+                },
+
+                new MediaProfile
+                {
+                    Container = "ts",
+                    VideoCodec="h264",
+                    AudioCodec="ac3,aac,mp3",
+                    MimeType = "video/vnd.dlna.mpeg-tts",
+                    OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
+                    Type = DlnaProfileType.Video
+                },
+
+                new MediaProfile
+                {
+                    Container = "ts",
+                    VideoCodec="mpeg2video",
+                    MimeType = "video/vnd.dlna.mpeg-tts",
+                    OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
+                    Type = DlnaProfileType.Video
+                },
+
+                new MediaProfile
+                {
+                    Container = "mpeg",
+                    VideoCodec="mpeg1video,mpeg2video",
+                    MimeType = "video/mpeg",
+                    OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL",
+                    Type = DlnaProfileType.Video
+                }
+            };
+
+            ContainerProfiles = new[]
+            {
+                new ContainerProfile
+                {
+                    Type = DlnaProfileType.Photo,
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Width,
+                            Value = "1920"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Height,
+                            Value = "1080"
+                        }
+                    }
+                }
+            };
+
+            CodecProfiles = new[]
+            {
+                new CodecProfile
+                {
+                    Type = CodecType.VideoCodec,
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Width,
+                            Value = "1920"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Height,
+                            Value = "1080"
+                        }
+                    }
+                },
+
+                new CodecProfile
+                {
+                    Type = CodecType.VideoCodec,
+                    Codec = "h264",
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.VideoFramerate,
+                            Value = "30"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.VideoBitrate,
+                            Value = "20000000"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.VideoLevel,
+                            Value = "41"
+                        }
+                    }
+                },
+
+                new CodecProfile
+                {
+                    Type = CodecType.VideoCodec,
+                    Codec = "mpeg2video",
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.VideoFramerate,
+                            Value = "30"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.VideoBitrate,
+                            Value = "20000000"
+                        }
+                    }
+                },
+
+                new CodecProfile
+                {
+                    Type = CodecType.VideoAudioCodec,
+                    Codec = "ac3",
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.AudioChannels,
+                            Value = "6"
+                        }
+                    }
+                },
+
+                new CodecProfile
+                {
+                    Type = CodecType.VideoAudioCodec,
+                    Codec = "aac",
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.AudioChannels,
+                            Value = "2"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.NotEquals,
+                            Property = ProfileConditionValue.AudioProfile,
+                            Value = "he-aac"
+                        }
+                    }
+                }
+            };
+        }
+    }
+}

+ 304 - 0
MediaBrowser.Dlna/Profiles/SonyBravia2011Profile.cs

@@ -0,0 +1,304 @@
+using MediaBrowser.Controller.Dlna;
+
+namespace MediaBrowser.Dlna.Profiles
+{
+    public class SonyBravia2011Profile : DefaultProfile
+    {
+        public SonyBravia2011Profile()
+        {
+            Name = "Sony Bravia (2011)";
+
+            Identification = new DeviceIdentification
+            {
+                FriendlyName = @"KDL-\d{2}([A-Z]X\d2\d|CX400).*",
+                Manufacturer = "Sony",
+
+                Headers = new[]
+                {
+                    new HttpHeaderInfo
+                    {
+                        Name = "X-AV-Client-Info",
+                        Value = @".*KDL-\d{2}([A-Z]X\d2\d|CX400).*",
+                        Match = HeaderMatchType.Regex
+                    }
+                }
+            };
+
+            ModelName = "Windows Media Player Sharing";
+            ModelNumber = "3.0";
+            ModelUrl = "http://www.microsoft.com/";
+            Manufacturer = "Microsoft Corporation";
+            ManufacturerUrl = "http://www.microsoft.com/";
+            SonyAggregationFlags = "10";
+
+            TranscodingProfiles = new[]
+            {
+                new TranscodingProfile
+                {
+                    Container = "mp3",
+                    AudioCodec = "mp3",
+                    Type = DlnaProfileType.Audio
+                },
+                new TranscodingProfile
+                {
+                    Container = "ts",
+                    VideoCodec = "h264",
+                    AudioCodec = "ac3,aac",
+                    Type = DlnaProfileType.Video,
+                    EnableMpegtsM2TsMode = true
+                },
+                new TranscodingProfile
+                {
+                    Container = "jpeg",
+                    Type = DlnaProfileType.Photo
+                }
+            };
+
+            DirectPlayProfiles = new[]
+            {
+                new DirectPlayProfile
+                {
+                    Container = "ts",
+                    VideoCodec = "h264",
+                    AudioCodec = "ac3,aac,mp3",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "ts",
+                    VideoCodec = "mpeg2video",
+                    AudioCodec = "mp3,mp2",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "mp4",
+                    VideoCodec = "h264,mpeg4",
+                    AudioCodec = "ac3,aac,mp3,mp2",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "mpeg",
+                    VideoCodec = "mpeg2video,mpeg1video",
+                    AudioCodec = "mp3,mp2",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "asf",
+                    VideoCodec = "wmv2,wmv3,vc1",
+                    AudioCodec = "wmav2,wmapro,wmavoice",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "mp3",
+                    AudioCodec = "mp3",
+                    Type = DlnaProfileType.Audio
+                },
+                new DirectPlayProfile
+                {
+                    Container = "asf",
+                    AudioCodec = "wmav2,wmapro,wmavoice",
+                    Type = DlnaProfileType.Audio
+                }
+            };
+
+            ContainerProfiles = new[]
+            {
+                new ContainerProfile
+                {
+                    Type = DlnaProfileType.Photo,
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Width,
+                            Value = "1920"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Height,
+                            Value = "1080"
+                        }
+                    }
+                }
+            };
+
+            MediaProfiles = new[]
+            {
+                new MediaProfile
+                {
+                    Container = "ts",
+                    VideoCodec="h264",
+                    AudioCodec="ac3,aac,mp3",
+                    MimeType = "video/vnd.dlna.mpeg-tts",
+                    OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
+                    Type = DlnaProfileType.Video,
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="192"},
+                        new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoTimestamp, Value="1"}
+                    }
+                },
+
+                new MediaProfile
+                {
+                    Container = "ts",
+                    VideoCodec="h264",
+                    AudioCodec="ac3,aac,mp3",
+                    MimeType = "video/mpeg",
+                    OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
+                    Type = DlnaProfileType.Video,
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="188"}
+                    }
+                },
+
+                new MediaProfile
+                {
+                    Container = "ts",
+                    VideoCodec="h264",
+                    AudioCodec="ac3,aac,mp3",
+                    MimeType = "video/vnd.dlna.mpeg-tts",
+                    OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
+                    Type = DlnaProfileType.Video
+                },
+
+                new MediaProfile
+                {
+                    Container = "ts",
+                    VideoCodec="mpeg2video",
+                    MimeType = "video/vnd.dlna.mpeg-tts",
+                    OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
+                    Type = DlnaProfileType.Video
+                },
+
+                new MediaProfile
+                {
+                    Container = "mpeg",
+                    VideoCodec="mpeg1video,mpeg2video",
+                    MimeType = "video/mpeg",
+                    OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL",
+                    Type = DlnaProfileType.Video
+                }
+            };
+
+            CodecProfiles = new[]
+            {
+                new CodecProfile
+                {
+                    Type = CodecType.VideoCodec,
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Width,
+                            Value = "1920"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Height,
+                            Value = "1080"
+                        }
+                    }
+                },
+
+                new CodecProfile
+                {
+                    Type = CodecType.VideoCodec,
+                    Codec = "h264",
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.VideoFramerate,
+                            Value = "30"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.VideoBitrate,
+                            Value = "20000000"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.VideoLevel,
+                            Value = "41"
+                        }
+                    }
+                },
+
+                new CodecProfile
+                {
+                    Type = CodecType.VideoCodec,
+                    Codec = "mpeg2video",
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.VideoFramerate,
+                            Value = "30"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.VideoBitrate,
+                            Value = "20000000"
+                        }
+                    }
+                },
+
+                new CodecProfile
+                {
+                    Type = CodecType.VideoAudioCodec,
+                    Codec = "ac3",
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.AudioChannels,
+                            Value = "6"
+                        }
+                    }
+                },
+
+                new CodecProfile
+                {
+                    Type = CodecType.VideoAudioCodec,
+                    Codec = "aac",
+
+                    Conditions = new[]
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.AudioChannels,
+                            Value = "2"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.NotEquals,
+                            Property = ProfileConditionValue.AudioProfile,
+                            Value = "he-aac"
+                        }
+                    }
+                }
+            };
+        }
+    }
+}

+ 246 - 0
MediaBrowser.Dlna/Profiles/SonyBravia2012Profile.cs

@@ -0,0 +1,246 @@
+using MediaBrowser.Controller.Dlna;
+
+namespace MediaBrowser.Dlna.Profiles
+{
+    public class SonyBravia2012Profile : DefaultProfile
+    {
+        public SonyBravia2012Profile()
+        {
+            Name = "Sony Bravia (2012)";
+
+            Identification = new DeviceIdentification
+            {
+                FriendlyName = @"KDL-\d{2}[A-Z]X\d5(\d|G).*",
+                Manufacturer = "Sony",
+
+                Headers = new[]
+                {
+                    new HttpHeaderInfo
+                    {
+                        Name = "X-AV-Client-Info",
+                        Value = @".*KDL-\d{2}[A-Z]X\d5(\d|G).*",
+                        Match = HeaderMatchType.Regex
+                    }
+                }
+            };
+
+            ModelName = "Windows Media Player Sharing";
+            ModelNumber = "3.0";
+            ModelUrl = "http://www.microsoft.com/";
+            Manufacturer = "Microsoft Corporation";
+            ManufacturerUrl = "http://www.microsoft.com/";
+            SonyAggregationFlags = "10";
+
+            TranscodingProfiles = new[]
+            {
+                new TranscodingProfile
+                {
+                    Container = "mp3",
+                    AudioCodec = "mp3",
+                    Type = DlnaProfileType.Audio
+                },
+                new TranscodingProfile
+                {
+                    Container = "ts",
+                    VideoCodec = "h264",
+                    AudioCodec = "ac3,aac",
+                    Type = DlnaProfileType.Video,
+                    EnableMpegtsM2TsMode = true
+                },
+                new TranscodingProfile
+                {
+                    Container = "jpeg",
+                    Type = DlnaProfileType.Photo
+                }
+            };
+
+            DirectPlayProfiles = new[]
+            {
+                new DirectPlayProfile
+                {
+                    Container = "ts",
+                    VideoCodec = "h264",
+                    AudioCodec = "ac3,aac,mp3",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "ts",
+                    VideoCodec = "mpeg2video",
+                    AudioCodec = "mp3,mp2",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "mp4",
+                    VideoCodec = "h264,mpeg4",
+                    AudioCodec = "ac3,aac,mp3,mp2",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "avi",
+                    VideoCodec = "mpeg4",
+                    AudioCodec = "ac3,mp3",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "mpeg",
+                    VideoCodec = "mpeg2video,mpeg1video",
+                    AudioCodec = "mp3,mp2",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "asf",
+                    VideoCodec = "wmv2,wmv3,vc1",
+                    AudioCodec = "wmav2,wmapro,wmavoice",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "mp3",
+                    AudioCodec = "mp3",
+                    Type = DlnaProfileType.Audio
+                },
+                new DirectPlayProfile
+                {
+                    Container = "asf",
+                    AudioCodec = "wmav2,wmapro,wmavoice",
+                    Type = DlnaProfileType.Audio
+                },
+                new DirectPlayProfile
+                {
+                    Container = "jpeg",
+                    Type = DlnaProfileType.Photo
+                }
+            };
+
+            MediaProfiles = new[]
+            {
+                new MediaProfile
+                {
+                    Container = "ts",
+                    VideoCodec="h264",
+                    AudioCodec="ac3,aac,mp3",
+                    MimeType = "video/vnd.dlna.mpeg-tts",
+                    OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
+                    Type = DlnaProfileType.Video,
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="192"},
+                        new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoTimestamp, Value="1"}
+                    }
+                },
+
+                new MediaProfile
+                {
+                    Container = "ts",
+                    VideoCodec="h264",
+                    AudioCodec="ac3,aac,mp3",
+                    MimeType = "video/mpeg",
+                    OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
+                    Type = DlnaProfileType.Video,
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="188"}
+                    }
+                },
+
+                new MediaProfile
+                {
+                    Container = "ts",
+                    VideoCodec="h264",
+                    AudioCodec="ac3,aac,mp3",
+                    MimeType = "video/vnd.dlna.mpeg-tts",
+                    OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
+                    Type = DlnaProfileType.Video
+                },
+
+                new MediaProfile
+                {
+                    Container = "ts",
+                    VideoCodec="mpeg2video",
+                    MimeType = "video/vnd.dlna.mpeg-tts",
+                    OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
+                    Type = DlnaProfileType.Video
+                },
+
+                new MediaProfile
+                {
+                    Container = "mpeg",
+                    VideoCodec="mpeg1video,mpeg2video",
+                    MimeType = "video/mpeg",
+                    OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL",
+                    Type = DlnaProfileType.Video
+                }
+            };
+
+            ContainerProfiles = new[]
+            {
+                new ContainerProfile
+                {
+                    Type = DlnaProfileType.Photo,
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Width,
+                            Value = "1920"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Height,
+                            Value = "1080"
+                        }
+                    }
+                }
+            };
+
+            CodecProfiles = new[]
+            {
+                new CodecProfile
+                {
+                    Type = CodecType.VideoCodec,
+                    Conditions = new[]
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Width,
+                            Value = "1920"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Height,
+                            Value = "1080"
+                        }
+                    }
+                },
+
+                new CodecProfile
+                {
+                    Type = CodecType.VideoAudioCodec,
+                    Codec = "ac3",
+
+                    Conditions = new[]
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.AudioChannels,
+                            Value = "6"
+                        }
+                    }
+                }
+            };
+        }
+    }
+}

+ 264 - 0
MediaBrowser.Dlna/Profiles/SonyBravia2013Profile.cs

@@ -0,0 +1,264 @@
+using MediaBrowser.Controller.Dlna;
+
+namespace MediaBrowser.Dlna.Profiles
+{
+    public class SonyBravia2013Profile : DefaultProfile
+    {
+        public SonyBravia2013Profile()
+        {
+            Name = "Sony Bravia (2013)";
+
+            Identification = new DeviceIdentification
+            {
+                FriendlyName = @"KDL-\d{2}[WR][5689]\d{2}A.*",
+                Manufacturer = "Sony",
+
+                Headers = new[]
+                {
+                    new HttpHeaderInfo
+                    {
+                        Name = "X-AV-Client-Info",
+                        Value = @".*KDL-\d{2}[WR][5689]\d{2}A.*",
+                        Match = HeaderMatchType.Regex
+                    }
+                }
+            };
+
+            ModelName = "Windows Media Player Sharing";
+            ModelNumber = "3.0";
+            ModelUrl = "http://www.microsoft.com/";
+            Manufacturer = "Microsoft Corporation";
+            ManufacturerUrl = "http://www.microsoft.com/";
+            SonyAggregationFlags = "10";
+
+            TranscodingProfiles = new[]
+            {
+                new TranscodingProfile
+                {
+                    Container = "mp3",
+                    Type = DlnaProfileType.Audio
+                },
+                new TranscodingProfile
+                {
+                    Container = "ts",
+                    VideoCodec = "h264",
+                    AudioCodec = "ac3,aac",
+                    Type = DlnaProfileType.Video,
+                    EnableMpegtsM2TsMode = true
+                },
+                new TranscodingProfile
+                {
+                    Container = "jpeg",
+                    Type = DlnaProfileType.Photo
+                }
+            };
+
+            DirectPlayProfiles = new[]
+            {
+                new DirectPlayProfile
+                {
+                    Container = "ts",
+                    VideoCodec = "h264",
+                    AudioCodec = "ac3,eac3,aac,mp3",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "ts",
+                    VideoCodec = "mpeg2video",
+                    AudioCodec = "mp3,mp2",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "mp4",
+                    VideoCodec = "h264,mpeg4",
+                    AudioCodec = "ac3,eac3,aac,mp3,mp2",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "mov",
+                    VideoCodec = "h264,mpeg4,mjpeg",
+                    AudioCodec = "ac3,eac3,aac,mp3,mp2",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "mkv",
+                    VideoCodec = "h264,mpeg4,vp8",
+                    AudioCodec = "ac3,eac3,aac,mp3,mp2,pcm,vorbis",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "avi",
+                    VideoCodec = "mpeg4",
+                    AudioCodec = "ac3,eac3,mp3",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "avi",
+                    VideoCodec = "mjpeg",
+                    AudioCodec = "pcm",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "mpeg",
+                    VideoCodec = "mpeg2video,mpeg1video",
+                    AudioCodec = "mp3,mp2",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "asf",
+                    VideoCodec = "wmv2,wmv3,vc1",
+                    AudioCodec = "wmav2,wmapro,wmavoice",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "mp3",
+                    AudioCodec = "mp3",
+                    Type = DlnaProfileType.Audio
+                },
+                new DirectPlayProfile
+                {
+                    Container = "mp4",
+                    AudioCodec = "aac",
+                    Type = DlnaProfileType.Audio
+                },
+                new DirectPlayProfile
+                {
+                    Container = "wav",
+                    AudioCodec = "pcm",
+                    Type = DlnaProfileType.Audio
+                },
+                new DirectPlayProfile
+                {
+                    Container = "asf",
+                    AudioCodec = "wmav2,wmapro,wmavoice",
+                    Type = DlnaProfileType.Audio
+                },
+                new DirectPlayProfile
+                {
+                    Container = "jpeg",
+                    Type = DlnaProfileType.Photo
+                }
+            };
+
+            ContainerProfiles = new[]
+            {
+                new ContainerProfile
+                {
+                    Type = DlnaProfileType.Photo,
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Width,
+                            Value = "1920"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Height,
+                            Value = "1080"
+                        }
+                    }
+                }
+            };
+
+            MediaProfiles = new[]
+            {
+                new MediaProfile
+                {
+                    Container = "ts",
+                    VideoCodec="h264",
+                    AudioCodec="ac3,aac,mp3",
+                    MimeType = "video/vnd.dlna.mpeg-tts",
+                    OrgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
+                    Type = DlnaProfileType.Video,
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="192"},
+                        new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoTimestamp, Value="1"}
+                    }
+                },
+
+                new MediaProfile
+                {
+                    Container = "ts",
+                    VideoCodec="h264",
+                    AudioCodec="ac3,aac,mp3",
+                    MimeType = "video/mpeg",
+                    OrgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
+                    Type = DlnaProfileType.Video,
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition{ Condition= ProfileConditionType.Equals, Property= ProfileConditionValue.VideoPacketLength, Value="188"}
+                    }
+                },
+
+                new MediaProfile
+                {
+                    Container = "ts",
+                    VideoCodec="h264",
+                    AudioCodec="ac3,aac,mp3",
+                    MimeType = "video/vnd.dlna.mpeg-tts",
+                    OrgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
+                    Type = DlnaProfileType.Video
+                },
+
+                new MediaProfile
+                {
+                    Container = "ts",
+                    VideoCodec="mpeg2video",
+                    MimeType = "video/vnd.dlna.mpeg-tts",
+                    OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
+                    Type = DlnaProfileType.Video
+                },
+
+                new MediaProfile
+                {
+                    Container = "mpeg",
+                    VideoCodec="mpeg1video,mpeg2video",
+                    MimeType = "video/mpeg",
+                    OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL",
+                    Type = DlnaProfileType.Video
+                }
+            };
+
+
+            CodecProfiles = new[]
+            {
+                new CodecProfile
+                {
+                    Type = CodecType.VideoCodec,
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Width,
+                            Value = "1920"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Height,
+                            Value = "1080"
+                        }
+                    }
+                }
+            };
+        }
+    }
+}

+ 233 - 0
MediaBrowser.Dlna/Profiles/SonyPs3Profile.cs

@@ -0,0 +1,233 @@
+using MediaBrowser.Controller.Dlna;
+
+namespace MediaBrowser.Dlna.Profiles
+{
+    public class SonyPs3Profile : DefaultProfile
+    {
+        public SonyPs3Profile()
+        {
+            Name = "Sony Bravia (2010)";
+
+            Identification = new DeviceIdentification
+            {
+                Headers = new[]
+                {
+                    new HttpHeaderInfo
+                    {
+                        Name = "User-Agent",
+                        Value = @"PLAYSTATION 3",
+                        Match = HeaderMatchType.Substring
+                    },
+
+                    new HttpHeaderInfo
+                    {
+                        Name = "X-AV-Client-Info",
+                        Value = @"PLAYSTATION 3",
+                        Match = HeaderMatchType.Substring
+                    }
+                }
+            };
+
+            SonyAggregationFlags = "10";
+            XDlnaDoc = "DMS-1.50";
+
+            TranscodingProfiles = new[]
+            {
+                new TranscodingProfile
+                {
+                    Container = "mp3",
+                    AudioCodec = "mp3",
+                    Type = DlnaProfileType.Audio
+                },
+                new TranscodingProfile
+                {
+                    Container = "ts",
+                    VideoCodec = "h264",
+                    AudioCodec = "mp3",
+                    Type = DlnaProfileType.Video
+                },
+                new TranscodingProfile
+                {
+                    Container = "jpeg",
+                    Type = DlnaProfileType.Photo
+                }
+            };
+
+            ContainerProfiles = new[]
+            {
+                new ContainerProfile
+                {
+                    Type = DlnaProfileType.Photo,
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Width,
+                            Value = "1920"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Height,
+                            Value = "1080"
+                        }
+                    }
+                }
+            };
+
+            CodecProfiles = new[]
+            {
+                new CodecProfile
+                {
+                    Type = CodecType.VideoCodec,
+                    Codec = "h264",
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Width,
+                            Value = "1920"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Height,
+                            Value = "1080"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.VideoFramerate,
+                            Value = "30",
+                            IsRequired = false
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.VideoBitrate,
+                            Value = "15360000",
+                            IsRequired = false
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.VideoLevel,
+                            Value = "41",
+                            IsRequired = false
+                        }
+                    }
+                },
+
+                new CodecProfile
+                {
+                    Type = CodecType.VideoAudioCodec,
+                    Codec = "ac3",
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.AudioChannels,
+                            Value = "6",
+                            IsRequired = false
+                        },
+
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.AudioBitrate,
+                            Value = "640000",
+                            IsRequired = false
+                        }
+                    }
+                },
+
+                new CodecProfile
+                {
+                    Type = CodecType.VideoAudioCodec,
+                    Codec = "wmapro",
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.AudioChannels,
+                            Value = "2"
+                        }
+                    }
+                },
+
+                new CodecProfile
+                {
+                    Type = CodecType.VideoAudioCodec,
+                    Codec = "aac",
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.NotEquals,
+                            Property = ProfileConditionValue.AudioProfile,
+                            Value = "he-aac",
+                            IsRequired = false
+                        }
+                    }
+                },
+
+                new CodecProfile
+                {
+                    Type = CodecType.VideoAudioCodec,
+                    Codec = "aac",
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.AudioChannels,
+                            Value = "2"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.NotEquals,
+                            Property = ProfileConditionValue.AudioProfile,
+                            Value = "he-aac"
+                        }
+                    }
+                }
+            };
+
+            MediaProfiles = new[]
+            {
+                new MediaProfile
+                {
+                    Container = "mp4,mov",
+                    AudioCodec="aac",
+                    MimeType = "video/mp4",
+                    Type = DlnaProfileType.Video
+                },
+
+                new MediaProfile
+                {
+                    Container = "avi",
+                    MimeType = "video/divx",
+                    OrgPn="AVI",
+                    Type = DlnaProfileType.Video
+                },
+
+                new MediaProfile
+                {
+                    Container = "wav",
+                    MimeType = "audio/wav",
+                    Type = DlnaProfileType.Audio
+                }
+            };
+        }
+    }
+}

+ 241 - 0
MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs

@@ -0,0 +1,241 @@
+using MediaBrowser.Controller.Dlna;
+
+namespace MediaBrowser.Dlna.Profiles
+{
+    public class WdtvLiveProfile : DefaultProfile
+    {
+        public WdtvLiveProfile()
+        {
+            Name = "WDTV Live";
+
+            TimelineOffsetSeconds = 5;
+            IgnoreTranscodeByteRangeRequests = true;
+
+            Identification = new DeviceIdentification
+            {
+                ModelName = "WD TV HD Live",
+
+                Headers = new []
+                {
+                    new HttpHeaderInfo {Name = "User-Agent", Value = "alphanetworks", Match = HeaderMatchType.Substring},
+                    new HttpHeaderInfo
+                    {
+                        Name = "User-Agent",
+                        Value = "ALPHA Networks",
+                        Match = HeaderMatchType.Substring
+                    }
+                }
+            };
+
+            TranscodingProfiles = new[]
+            {
+                new TranscodingProfile
+                {
+                    Container = "mp3",
+                    Type = DlnaProfileType.Audio,
+                    AudioCodec = "mp3"
+                },
+                new TranscodingProfile
+                {
+                    Container = "ts",
+                    Type = DlnaProfileType.Video,
+                    VideoCodec = "h264",
+                    AudioCodec = "aac",
+
+                    Settings = new []
+                    {
+                        new TranscodingSetting {Name = TranscodingSettingType.VideoLevel, Value = "3"},
+                        new TranscodingSetting {Name = TranscodingSettingType.VideoProfile, Value = "baseline"}
+                    }
+                },
+                new TranscodingProfile
+                {
+                    Container = "jpeg",
+                    Type = DlnaProfileType.Photo
+                }
+            };
+
+            DirectPlayProfiles = new[]
+            {
+                new DirectPlayProfile
+                {
+                    Container = "avi",
+                    Type = DlnaProfileType.Video,
+                    VideoCodec = "mpeg1video,mpeg2video,mpeg4,h264,vc1",
+                    AudioCodec = "ac3,dca,mp2,mp3,pcm"
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "mpeg",
+                    Type = DlnaProfileType.Video,
+                    VideoCodec = "mpeg1video,mpeg2video",
+                    AudioCodec = "ac3,dca,mp2,mp3,pcm"
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "mkv",
+                    Type = DlnaProfileType.Video,
+                    VideoCodec = "mpeg1video,mpeg2video,mpeg4,h264,vc1",
+                    AudioCodec = "ac3,dca,aac,mp2,mp3,pcm"
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "ts",
+                    Type = DlnaProfileType.Video,
+                    VideoCodec = "mpeg1video,mpeg2video,h264,vc1",
+                    AudioCodec = "ac3,dca,mp2,mp3"
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "mp4,mov",
+                    Type = DlnaProfileType.Video,
+                    VideoCodec = "h264,mpeg4",
+                    AudioCodec = "ac3,aac,mp2,mp3"
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "asf",
+                    Type = DlnaProfileType.Video,
+                    VideoCodec = "vc1",
+                    AudioCodec = "wmav2,wmapro"
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "asf",
+                    Type = DlnaProfileType.Video,
+                    VideoCodec = "mpeg2video",
+                    AudioCodec = "mp2,ac3"
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "mp3",
+                    AudioCodec = "mp2,mp3",
+                    Type = DlnaProfileType.Audio
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "mp4",
+                    AudioCodec = "mp4",
+                    Type = DlnaProfileType.Audio
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "flac",
+                    AudioCodec = "flac",
+                    Type = DlnaProfileType.Audio
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "asf",
+                    AudioCodec = "wmav2,wmapro,wmavoice",
+                    Type = DlnaProfileType.Audio
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "ogg",
+                    AudioCodec = "vorbis",
+                    Type = DlnaProfileType.Audio
+                },
+
+                new DirectPlayProfile
+                {
+                    Type = DlnaProfileType.Photo,
+
+                    Container = "jpeg,png,gif,bmp,tiff"
+                }
+            };
+
+            MediaProfiles = new[]
+            {
+                new MediaProfile
+                {
+                    Container = "ts",
+                    OrgPn = "MPEG_TS_SD_NA",
+                    Type = DlnaProfileType.Video
+                }
+            };
+
+            ContainerProfiles = new[]
+            {
+                new ContainerProfile
+                {
+                    Type = DlnaProfileType.Photo,
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Width,
+                            Value = "1920"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Height,
+                            Value = "1080"
+                        }
+                    }
+                }
+            };
+
+            CodecProfiles = new[]
+            {
+                new CodecProfile
+                {
+                    Type = CodecType.VideoCodec,
+                    Codec = "h264",
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Width,
+                            Value = "1920"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Height,
+                            Value = "1080"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.VideoLevel,
+                            Value = "41"
+                        }
+                    }
+                },
+
+                new CodecProfile
+                {
+                    Type = CodecType.VideoAudioCodec,
+                    Codec = "aac",
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.AudioChannels,
+                            Value = "2"
+                        }
+                    }
+                }
+            };
+        }
+    }
+}

+ 312 - 0
MediaBrowser.Dlna/Profiles/Xbox360Profile.cs

@@ -0,0 +1,312 @@
+using MediaBrowser.Controller.Dlna;
+
+namespace MediaBrowser.Dlna.Profiles
+{
+    public class Xbox360Profile : DefaultProfile
+    {
+        public Xbox360Profile()
+        {
+            Name = "Xbox 360";
+
+            ModelName = "Windows Media Player Sharing";
+            ModelNumber = "12.0";
+            ModelUrl = "http://www.microsoft.com/";
+            Manufacturer = "Microsoft Corporation";
+            ManufacturerUrl = "http://www.microsoft.com/";
+            XDlnaDoc = "DMS-1.50";
+
+            TimelineOffsetSeconds = 40;
+            RequiresPlainFolders = true;
+            RequiresPlainVideoItems = true;
+
+            Identification = new DeviceIdentification
+            {
+                ModelName = "Xbox 360",
+
+                Headers = new []
+                {
+                    new HttpHeaderInfo {Name = "User-Agent", Value = "Xbox", Match = HeaderMatchType.Substring},
+                    new HttpHeaderInfo {Name = "User-Agent", Value = "Xenon", Match = HeaderMatchType.Substring}
+                }
+            };
+
+            TranscodingProfiles = new[]
+            {
+                new TranscodingProfile
+                {
+                    Container = "mp3",
+                    AudioCodec = "mp3",
+                    Type = DlnaProfileType.Audio
+                },
+                new TranscodingProfile
+                {
+                    Container = "asf",
+                    VideoCodec = "wmv2",
+                    AudioCodec = "wmav2",
+                    Type = DlnaProfileType.Video,
+                    TranscodeSeekInfo = TranscodeSeekInfo.Bytes,
+                    EstimateContentLength = true,
+
+                    Settings = new []
+                    {
+                        new TranscodingSetting {Name = TranscodingSettingType.MaxAudioChannels, Value = "6"},
+                        new TranscodingSetting {Name = TranscodingSettingType.VideoLevel, Value = "3"},
+                        new TranscodingSetting {Name = TranscodingSettingType.VideoProfile, Value = "baseline"}
+                    }
+                },
+                new TranscodingProfile
+                {
+                    Container = "jpeg",
+                    Type = DlnaProfileType.Photo
+                }
+            };
+
+            DirectPlayProfiles = new[]
+            {
+                new DirectPlayProfile
+                {
+                    Container = "avi",
+                    VideoCodec = "mpeg4",
+                    AudioCodec = "ac3,mp3",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "avi",
+                    VideoCodec = "h264",
+                    AudioCodec = "aac",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "mp4,mov",
+                    VideoCodec = "h264,mpeg4",
+                    AudioCodec = "aac,ac3",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "asf",
+                    VideoCodec = "wmv2,wmv3,vc1",
+                    AudioCodec = "wmav2,wmapro",
+                    Type = DlnaProfileType.Video
+                },
+                new DirectPlayProfile
+                {
+                    Container = "asf",
+                    AudioCodec = "wmav2,wmapro,wmavoice",
+                    Type = DlnaProfileType.Audio
+                },
+                new DirectPlayProfile
+                {
+                    Container = "mp3",
+                    AudioCodec = "mp3",
+                    Type = DlnaProfileType.Audio
+                },
+                new DirectPlayProfile
+                {
+                    Container = "jpeg",
+                    Type = DlnaProfileType.Photo
+                }
+            };
+
+            MediaProfiles = new[]
+            {
+                new MediaProfile
+                {
+                    Container = "avi",
+                    MimeType = "video/avi",
+                    Type = DlnaProfileType.Video
+                }
+            };
+
+            ContainerProfiles = new[]
+            {
+                new ContainerProfile
+                {
+                    Type = DlnaProfileType.Video,
+                    Container = "mp4,mov",
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Has64BitOffsets,
+                            Value = "false",
+                            IsRequired = false
+                        }
+                    }
+                },
+
+                new ContainerProfile
+                {
+                    Type = DlnaProfileType.Photo,
+
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Width,
+                            Value = "1920"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Height,
+                            Value = "1080"
+                        }
+                    }
+                }
+            };
+
+            CodecProfiles = new[]
+            {
+                new CodecProfile
+                {
+                    Type = CodecType.VideoCodec,
+                    Codec = "mpeg4",
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Width,
+                            Value = "1280"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Height,
+                            Value = "720"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.VideoFramerate,
+                            Value = "30",
+                            IsRequired = false
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.VideoBitrate,
+                            Value = "5120000",
+                            IsRequired = false
+                        }
+                    }
+                },
+
+                new CodecProfile
+                {
+                    Type = CodecType.VideoCodec,
+                    Codec = "h264",
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Width,
+                            Value = "1920"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Height,
+                            Value = "1080"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.VideoLevel,
+                            Value = "41",
+                            IsRequired = false
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.VideoBitrate,
+                            Value = "10240000",
+                            IsRequired = false
+                        }
+                    }
+                },
+
+                new CodecProfile
+                {
+                    Type = CodecType.VideoCodec,
+                    Codec = "wmv2,wmv3,vc1",
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Width,
+                            Value = "1920"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.Height,
+                            Value = "1080"
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.VideoFramerate,
+                            Value = "30",
+                            IsRequired = false
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.VideoBitrate,
+                            Value = "15360000",
+                            IsRequired = false
+                        }
+                    }
+                },
+
+                new CodecProfile
+                {
+                    Type = CodecType.VideoAudioCodec,
+                    Codec = "ac3,wmav2,wmapro",
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.AudioChannels,
+                            Value = "6",
+                            IsRequired = false
+                        }
+                    }
+                },
+
+                new CodecProfile
+                {
+                    Type = CodecType.VideoAudioCodec,
+                    Codec = "aac",
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.AudioChannels,
+                            Value = "6",
+                            IsRequired = false
+                        },
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.Equals,
+                            Property = ProfileConditionValue.AudioProfile,
+                            Value = "lc",
+                            IsRequired = false
+                        }
+                    }
+                }
+            };
+        }
+    }
+}

+ 54 - 0
MediaBrowser.Dlna/Profiles/XboxOneProfile.cs

@@ -0,0 +1,54 @@
+using MediaBrowser.Controller.Dlna;
+
+namespace MediaBrowser.Dlna.Profiles
+{
+    public class XboxOneProfile : DefaultProfile
+    {
+        public XboxOneProfile()
+        {
+            Name = "Xbox One";
+
+            Identification = new DeviceIdentification
+            {
+                ModelName = "Xbox One",
+                FriendlyName = "Xbox-SystemOS"
+            };
+
+            TranscodingProfiles = new[]
+            {
+                new TranscodingProfile
+                {
+                    Container = "mp3",
+                    AudioCodec = "mp3",
+                    Type = DlnaProfileType.Audio
+                },
+                new TranscodingProfile
+                {
+                    Container = "ts",
+                    VideoCodec = "h264",
+                    AudioCodec = "aac",
+                    Type = DlnaProfileType.Video
+                }
+            };
+
+            DirectPlayProfiles = new[]
+            {
+                new DirectPlayProfile
+                {
+                    Container = "mp3,wma",
+                    Type = DlnaProfileType.Audio
+                }
+            };
+
+            MediaProfiles = new[]
+            {
+                new MediaProfile
+                {
+                    Container = "avi",
+                    MimeType = "video/x-msvideo",
+                    Type = DlnaProfileType.Video
+                }
+            };
+        }
+    }
+}

+ 115 - 0
MediaBrowser.Dlna/Server/DlnaServerEntryPoint.cs

@@ -0,0 +1,115 @@
+using MediaBrowser.Common;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Plugins;
+using MediaBrowser.Model.Logging;
+using System;
+
+namespace MediaBrowser.Dlna.Server
+{
+    public class DlnaServerEntryPoint : IServerEntryPoint
+    {
+        private readonly IServerConfigurationManager _config;
+        private readonly ILogger _logger;
+
+        private SsdpHandler _ssdpHandler;
+        private readonly IApplicationHost _appHost;
+
+        public DlnaServerEntryPoint(IServerConfigurationManager config, ILogManager logManager, IApplicationHost appHost)
+        {
+            _config = config;
+            _appHost = appHost;
+            _logger = logManager.GetLogger("DlnaServer");
+        }
+
+        public void Run()
+        {
+            _config.ConfigurationUpdated += ConfigurationUpdated;
+
+            //ReloadServer();
+        }
+
+        void ConfigurationUpdated(object sender, EventArgs e)
+        {
+            //ReloadServer();
+        }
+
+        private void ReloadServer()
+        {
+            var isStarted = _ssdpHandler != null;
+
+            if (_config.Configuration.DlnaOptions.EnableServer && !isStarted)
+            {
+                StartServer();
+            }
+            else if (!_config.Configuration.DlnaOptions.EnableServer && isStarted)
+            {
+                DisposeServer();
+            }
+        }
+
+        private readonly object _syncLock = new object();
+        private void StartServer()
+        {
+            var signature = GenerateServerSignature();
+
+            lock (_syncLock)
+            {
+                try
+                {
+                    _ssdpHandler = new SsdpHandler(_logger, _config, signature);
+                }
+                catch (Exception ex)
+                {
+                    _logger.ErrorException("Error starting Dlna server", ex);
+                }
+            }
+        }
+
+        private void DisposeServer()
+        {
+            lock (_syncLock)
+            {
+                if (_ssdpHandler != null)
+                {
+                    try
+                    {
+                        _ssdpHandler.Dispose();
+                    }
+                    catch (Exception ex)
+                    {
+                        _logger.ErrorException("Error disposing Dlna server", ex);
+                    }
+                    _ssdpHandler = null;
+                }
+            }
+        }
+
+        private string GenerateServerSignature()
+        {
+            var os = Environment.OSVersion;
+            var pstring = os.Platform.ToString();
+            switch (os.Platform)
+            {
+                case PlatformID.Win32NT:
+                case PlatformID.Win32S:
+                case PlatformID.Win32Windows:
+                    pstring = "WIN";
+                    break;
+            }
+
+            return String.Format(
+              "{0}{1}/{2}.{3} UPnP/1.0 DLNADOC/1.5 MediaBrowser/{4}",
+              pstring,
+              IntPtr.Size * 8,
+              os.Version.Major,
+              os.Version.Minor,
+              _appHost.ApplicationVersion
+              );
+        }
+
+        public void Dispose()
+        {
+            DisposeServer();
+        }
+    }
+}

+ 164 - 0
MediaBrowser.Dlna/Server/Headers.cs

@@ -0,0 +1,164 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace MediaBrowser.Dlna.Server
+{
+    public class Headers : IDictionary<string, string>
+    {
+        private readonly bool _asIs = false;
+        private readonly Dictionary<string, string> _dict = new Dictionary<string, string>();
+        private readonly static Regex Validator = new Regex(@"^[a-z\d][a-z\d_.-]+$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+
+        protected Headers(bool asIs)
+        {
+            _asIs = asIs;
+        }
+
+        public Headers()
+            : this(asIs: false)
+        {
+        }
+
+        public int Count
+        {
+            get
+            {
+                return _dict.Count;
+            }
+        }
+        public string HeaderBlock
+        {
+            get
+            {
+                var hb = new StringBuilder();
+                foreach (var h in this)
+                {
+                    hb.AppendFormat("{0}: {1}\r\n", h.Key, h.Value);
+                }
+                return hb.ToString();
+            }
+        }
+        public Stream HeaderStream
+        {
+            get
+            {
+                return new MemoryStream(Encoding.ASCII.GetBytes(HeaderBlock));
+            }
+        }
+        public bool IsReadOnly
+        {
+            get
+            {
+                return false;
+            }
+        }
+        public ICollection<string> Keys
+        {
+            get
+            {
+                return _dict.Keys;
+            }
+        }
+        public ICollection<string> Values
+        {
+            get
+            {
+                return _dict.Values;
+            }
+        }
+
+
+        public string this[string key]
+        {
+            get
+            {
+                return _dict[Normalize(key)];
+            }
+            set
+            {
+                _dict[Normalize(key)] = value;
+            }
+        }
+
+
+        private string Normalize(string header)
+        {
+            if (!_asIs)
+            {
+                header = header.ToLower();
+            }
+            header = header.Trim();
+            if (!Validator.IsMatch(header))
+            {
+                throw new ArgumentException("Invalid header: " + header);
+            }
+            return header;
+        }
+
+        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+        {
+            return _dict.GetEnumerator();
+        }
+
+        public void Add(KeyValuePair<string, string> item)
+        {
+            Add(item.Key, item.Value);
+        }
+
+        public void Add(string key, string value)
+        {
+            _dict.Add(Normalize(key), value);
+        }
+
+        public void Clear()
+        {
+            _dict.Clear();
+        }
+
+        public bool Contains(KeyValuePair<string, string> item)
+        {
+            var p = new KeyValuePair<string, string>(Normalize(item.Key), item.Value);
+            return _dict.Contains(p);
+        }
+
+        public bool ContainsKey(string key)
+        {
+            return _dict.ContainsKey(Normalize(key));
+        }
+
+        public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex)
+        {
+            throw new NotImplementedException();
+        }
+
+        public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
+        {
+            return _dict.GetEnumerator();
+        }
+
+        public bool Remove(string key)
+        {
+            return _dict.Remove(Normalize(key));
+        }
+
+        public bool Remove(KeyValuePair<string, string> item)
+        {
+            return Remove(item.Key);
+        }
+
+        public override string ToString()
+        {
+            return string.Format("({0})", string.Join(", ", (from x in _dict
+                                                             select string.Format("{0}={1}", x.Key, x.Value))));
+        }
+
+        public bool TryGetValue(string key, out string value)
+        {
+            return _dict.TryGetValue(Normalize(key), out value);
+        }
+    }
+}

+ 16 - 0
MediaBrowser.Dlna/Server/RawHeaders.cs

@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Dlna.Server
+{
+    public class RawHeaders : Headers
+    {
+        public RawHeaders()
+            : base(true)
+        {
+        }
+    }
+}

+ 260 - 0
MediaBrowser.Dlna/Server/SsdpHandler.cs

@@ -0,0 +1,260 @@
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Model.Logging;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+
+namespace MediaBrowser.Dlna.Server
+{
+    public class SsdpHandler : IDisposable
+    {
+        private readonly ILogger _logger;
+        private readonly IServerConfigurationManager _config;
+        private readonly string _serverSignature;
+        private bool _isDisposed = false;
+
+        const string SSDPAddr = "239.255.255.250";
+        const int SSDPPort = 1900;
+
+        private readonly IPEndPoint _ssdpEndp = new IPEndPoint(IPAddress.Parse(SSDPAddr), SSDPPort);
+        private readonly IPAddress _ssdpIp = IPAddress.Parse(SSDPAddr);
+
+        private UdpClient _udpClient;
+
+        private readonly Dictionary<Guid, List<UpnpDevice>> _devices = new Dictionary<Guid, List<UpnpDevice>>();
+
+        public SsdpHandler(ILogger logger, IServerConfigurationManager config, string serverSignature)
+        {
+            _logger = logger;
+            _config = config;
+            _serverSignature = serverSignature;
+
+            Start();
+        }
+
+        private IEnumerable<UpnpDevice> Devices
+        {
+            get
+            {
+                UpnpDevice[] devs;
+                lock (_devices)
+                {
+                    devs = _devices.Values.SelectMany(i => i).ToArray();
+                }
+                return devs;
+            }
+        }
+
+        private void Start()
+        {
+            _udpClient = new UdpClient();
+            _udpClient.Client.UseOnlyOverlappedIO = true;
+            _udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
+            _udpClient.ExclusiveAddressUse = false;
+            _udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, SSDPPort));
+            _udpClient.JoinMulticastGroup(_ssdpIp, 2);
+            _logger.Info("SSDP service started");
+            Receive();
+        }
+
+        private void Receive()
+        {
+            try
+            {
+                _udpClient.BeginReceive(ReceiveCallback, null);
+            }
+            catch (ObjectDisposedException)
+            {
+            }
+        }
+
+        private void ReceiveCallback(IAsyncResult result)
+        {
+            try
+            {
+                var endpoint = new IPEndPoint(IPAddress.None, SSDPPort);
+                var received = _udpClient.EndReceive(result, ref endpoint);
+
+                if (_config.Configuration.DlnaOptions.EnableDebugLogging)
+                {
+                    _logger.Debug("{0} - SSDP Received a datagram", endpoint);
+                }
+
+                using (var reader = new StreamReader(new MemoryStream(received), Encoding.ASCII))
+                {
+                    var proto = (reader.ReadLine() ?? string.Empty).Trim();
+                    var method = proto.Split(new[] { ' ' }, 2)[0];
+                    var headers = new Headers();
+                    for (var line = reader.ReadLine(); line != null; line = reader.ReadLine())
+                    {
+                        line = line.Trim();
+                        if (string.IsNullOrEmpty(line))
+                        {
+                            break;
+                        }
+                        var parts = line.Split(new char[] { ':' }, 2);
+                        headers[parts[0]] = parts[1].Trim();
+                    }
+
+                    if (_config.Configuration.DlnaOptions.EnableDebugLogging)
+                    {
+                        _logger.Debug("{0} - Datagram method: {1}", endpoint, method);
+                        //_logger.Debug(headers);
+                    }
+
+                    if (string.Equals(method, "M-SEARCH", StringComparison.OrdinalIgnoreCase))
+                    {
+                        RespondToSearch(endpoint, headers["st"]);
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("Failed to read SSDP message", ex);
+            }
+
+            if (!_isDisposed)
+            {
+                Receive();
+            }
+        }
+
+        private void RespondToSearch(IPEndPoint endpoint, string req)
+        {
+            if (req == "ssdp:all")
+            {
+                req = null;
+            }
+
+            if (_config.Configuration.DlnaOptions.EnableDebugLogging)
+            {
+                _logger.Debug("RespondToSearch");
+            }
+
+            foreach (var d in Devices)
+            {
+                if (!string.IsNullOrEmpty(req) && req != d.Type)
+                {
+                    continue;
+                }
+
+                SendSearchResponse(endpoint, d);
+            }
+        }
+
+        private void SendSearchResponse(IPEndPoint endpoint, UpnpDevice dev)
+        {
+            var headers = new RawHeaders();
+            headers.Add("CACHE-CONTROL", "max-age = 600");
+            headers.Add("DATE", DateTime.Now.ToString("R"));
+            headers.Add("EXT", "");
+            headers.Add("LOCATION", dev.Descriptor.ToString());
+            headers.Add("SERVER", _serverSignature);
+            headers.Add("ST", dev.Type);
+            headers.Add("USN", dev.USN);
+
+            SendDatagram(endpoint, String.Format("HTTP/1.1 200 OK\r\n{0}\r\n", headers.HeaderBlock), false);
+            _logger.Info("{1} - Responded to a {0} request", dev.Type, endpoint);
+        }
+
+        private void SendDatagram(IPEndPoint endpoint, string msg, bool sticky)
+        {
+            if (_isDisposed)
+            {
+                return;
+            }
+            //var dgram = new Datagram(endpoint, msg, sticky);
+            //if (messageQueue.Count == 0)
+            //{
+            //    dgram.Send();
+            //}
+            //messageQueue.Enqueue(dgram);
+            //queueTimer.Enabled = true;
+        }
+
+        private void NotifyAll()
+        {
+            _logger.Debug("NotifyAll");
+            foreach (var d in Devices)
+            {
+                NotifyDevice(d, "alive", false);
+            }
+        }
+
+        private void NotifyDevice(UpnpDevice dev, string type, bool sticky)
+        {
+            _logger.Debug("NotifyDevice");
+            var headers = new RawHeaders();
+            headers.Add("HOST", "239.255.255.250:1900");
+            headers.Add("CACHE-CONTROL", "max-age = 600");
+            headers.Add("LOCATION", dev.Descriptor.ToString());
+            headers.Add("SERVER", _serverSignature);
+            headers.Add("NTS", "ssdp:" + type);
+            headers.Add("NT", dev.Type);
+            headers.Add("USN", dev.USN);
+
+            SendDatagram(_ssdpEndp, String.Format("NOTIFY * HTTP/1.1\r\n{0}\r\n", headers.HeaderBlock), sticky);
+            _logger.Debug("{0} said {1}", dev.USN, type);
+        }
+
+        private void RegisterNotification(Guid UUID, Uri Descriptor)
+        {
+            List<UpnpDevice> list;
+            lock (_devices)
+            {
+                if (!_devices.TryGetValue(UUID, out list))
+                {
+                    _devices.Add(UUID, list = new List<UpnpDevice>());
+                }
+            }
+
+            foreach (var t in new[] { "upnp:rootdevice", "urn:schemas-upnp-org:device:MediaServer:1", "urn:schemas-upnp-org:service:ContentDirectory:1", "uuid:" + UUID })
+            {
+                list.Add(new UpnpDevice(UUID, t, Descriptor));
+            }
+
+            NotifyAll();
+            _logger.Debug("Registered mount {0}", UUID);
+        }
+
+        internal void UnregisterNotification(Guid UUID)
+        {
+            List<UpnpDevice> dl;
+            lock (_devices)
+            {
+                if (!_devices.TryGetValue(UUID, out dl))
+                {
+                    return;
+                }
+                _devices.Remove(UUID);
+            }
+            foreach (var d in dl)
+            {
+                NotifyDevice(d, "byebye", true);
+            }
+            _logger.Debug("Unregistered mount {0}", UUID);
+        }
+
+        public void Dispose()
+        {
+            _isDisposed = true;
+            //while (messageQueue.Count != 0)
+            //{
+            //    datagramPosted.WaitOne();
+            //}
+
+            _udpClient.DropMulticastGroup(_ssdpIp);
+            _udpClient.Close();
+
+            //notificationTimer.Enabled = false;
+            //queueTimer.Enabled = false;
+            //notificationTimer.Dispose();
+            //queueTimer.Dispose();
+            //datagramPosted.Dispose();
+        }
+    }
+}

+ 28 - 0
MediaBrowser.Dlna/Server/UpnpDevice.cs

@@ -0,0 +1,28 @@
+using System;
+
+namespace MediaBrowser.Dlna.Server
+{
+    public sealed class UpnpDevice
+    {
+        public readonly Uri Descriptor;
+        public readonly string Type;
+        public readonly string USN;
+        public readonly Guid Uuid;
+
+        public UpnpDevice(Guid aUuid, string aType, Uri aDescriptor)
+        {
+            Uuid = aUuid;
+            Type = aType;
+            Descriptor = aDescriptor;
+
+            if (Type.StartsWith("uuid:"))
+            {
+                USN = Type;
+            }
+            else
+            {
+                USN = String.Format("uuid:{0}::{1}", Uuid.ToString(), Type);
+            }
+        }
+    }
+}

+ 1 - 1
MediaBrowser.Model/ApiClient/IApiClient.cs

@@ -1030,7 +1030,7 @@ namespace MediaBrowser.Model.ApiClient
         /// <param name="timer">The timer.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        Task CreateLiveTvTimerAsync(TimerInfoDto timer, CancellationToken cancellationToken);
+        Task CreateLiveTvTimerAsync(BaseTimerInfoDto timer, CancellationToken cancellationToken);
 
         /// <summary>
         /// Updates the live tv timer asynchronous.

+ 5 - 1
MediaBrowser.Model/Configuration/DlnaOptions.cs

@@ -4,11 +4,15 @@ namespace MediaBrowser.Model.Configuration
     public class DlnaOptions
     {
         public bool EnablePlayTo { get; set; }
-        public bool EnablePlayToDebugLogging { get; set; }
+        public bool EnableServer { get; set; }
+        public bool EnableDebugLogging { get; set; }
+        public int ClientDiscoveryIntervalSeconds { get; set; }
 
         public DlnaOptions()
         {
             EnablePlayTo = true;
+            EnableServer = true;
+            ClientDiscoveryIntervalSeconds = 60;
         }
     }
 }

+ 2 - 66
MediaBrowser.Model/Dto/StreamOptions.cs

@@ -10,7 +10,7 @@
         /// Omit to copy
         /// </summary>
         /// <value>The video codec.</value>
-        public VideoCodecs? VideoCodec { get; set; }
+        public string VideoCodec { get; set; }
 
         /// <summary>
         /// Gets or sets the video bit rate.
@@ -113,7 +113,7 @@
         /// Omit to copy the original stream
         /// </summary>
         /// <value>The audio encoding format.</value>
-        public AudioCodecs? AudioCodec { get; set; }
+        public string AudioCodec { get; set; }
 
         /// <summary>
         /// Gets or sets the item id.
@@ -158,68 +158,4 @@
         /// <value>The device id.</value>
         public string DeviceId { get; set; }
     }
-
-    /// <summary>
-    /// These are the codecs the api is capable of encoding to
-    /// </summary>
-    public enum AudioCodecs
-    {
-        /// <summary>
-        /// The aac
-        /// </summary>
-        Aac,
-        /// <summary>
-        /// The MP3
-        /// </summary>
-        Mp3,
-        /// <summary>
-        /// The vorbis
-        /// </summary>
-        Vorbis,
-        /// <summary>
-        /// The wma
-        /// </summary>
-        Wma,
-        /// <summary>
-        /// The copy
-        /// </summary>
-        Copy
-    }
-
-    /// <summary>
-    /// Enum VideoCodecs
-    /// </summary>
-    public enum VideoCodecs
-    {
-        H263,
-
-        /// <summary>
-        /// The H264
-        /// </summary>
-        H264,
-
-        /// <summary>
-        /// The mpeg4
-        /// </summary>
-        Mpeg4,
-
-        /// <summary>
-        /// The theora
-        /// </summary>
-        Theora,
-
-        /// <summary>
-        /// The VPX
-        /// </summary>
-        Vpx,
-
-        /// <summary>
-        /// The WMV
-        /// </summary>
-        Wmv,
-        /// <summary>
-        /// The copy
-        /// </summary>
-        Copy
-    }
 }

+ 3 - 101
MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs

@@ -1,80 +1,14 @@
-using System;
+using MediaBrowser.Model.Entities;
+using System;
 using System.Collections.Generic;
-using System.ComponentModel;
 using System.Diagnostics;
 using System.Runtime.Serialization;
-using MediaBrowser.Model.Entities;
 
 namespace MediaBrowser.Model.LiveTv
 {
     [DebuggerDisplay("Name = {Name}")]
-    public class SeriesTimerInfoDto : INotifyPropertyChanged
+    public class SeriesTimerInfoDto : BaseTimerInfoDto
     {
-        /// <summary>
-        /// Id of the recording.
-        /// </summary>
-        public string Id { get; set; }
-
-        /// <summary>
-        /// Gets or sets the external identifier.
-        /// </summary>
-        /// <value>The external identifier.</value>
-        public string ExternalId { get; set; }
-        
-        /// <summary>
-        /// ChannelId of the recording.
-        /// </summary>
-        public string ChannelId { get; set; }
-
-        /// <summary>
-        /// Gets or sets the name of the service.
-        /// </summary>
-        /// <value>The name of the service.</value>
-        public string ServiceName { get; set; }
-
-        /// <summary>
-        /// Gets or sets the external channel identifier.
-        /// </summary>
-        /// <value>The external channel identifier.</value>
-        public string ExternalChannelId { get; set; }
-        
-        /// <summary>
-        /// ChannelName of the recording.
-        /// </summary>
-        public string ChannelName { get; set; }
-
-        /// <summary>
-        /// Gets or sets the program identifier.
-        /// </summary>
-        /// <value>The program identifier.</value>
-        public string ProgramId { get; set; }
-
-        /// <summary>
-        /// Gets or sets the external program identifier.
-        /// </summary>
-        /// <value>The external program identifier.</value>
-        public string ExternalProgramId { get; set; }
-        
-        /// <summary>
-        /// Name of the recording.
-        /// </summary>
-        public string Name { get; set; }
-
-        /// <summary>
-        /// Description of the recording.
-        /// </summary>
-        public string Overview { get; set; }
-
-        /// <summary>
-        /// The start date of the recording, in UTC.
-        /// </summary>
-        public DateTime StartDate { get; set; }
-
-        /// <summary>
-        /// The end date of the recording, in UTC.
-        /// </summary>
-        public DateTime EndDate { get; set; }
-
         /// <summary>
         /// Gets or sets a value indicating whether [record any time].
         /// </summary>
@@ -105,36 +39,6 @@ namespace MediaBrowser.Model.LiveTv
         /// <value>The day pattern.</value>
         public DayPattern? DayPattern { get; set; }
 
-        /// <summary>
-        /// Gets or sets the priority.
-        /// </summary>
-        /// <value>The priority.</value>
-        public int Priority { get; set; }
-
-        /// <summary>
-        /// Gets or sets the pre padding seconds.
-        /// </summary>
-        /// <value>The pre padding seconds.</value>
-        public int PrePaddingSeconds { get; set; }
-
-        /// <summary>
-        /// Gets or sets the post padding seconds.
-        /// </summary>
-        /// <value>The post padding seconds.</value>
-        public int PostPaddingSeconds { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is pre padding required.
-        /// </summary>
-        /// <value><c>true</c> if this instance is pre padding required; otherwise, <c>false</c>.</value>
-        public bool IsPrePaddingRequired { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is post padding required.
-        /// </summary>
-        /// <value><c>true</c> if this instance is post padding required; otherwise, <c>false</c>.</value>
-        public bool IsPostPaddingRequired { get; set; }
-
         /// <summary>
         /// Gets or sets the image tags.
         /// </summary>
@@ -156,7 +60,5 @@ namespace MediaBrowser.Model.LiveTv
             ImageTags = new Dictionary<ImageType, Guid>();
             Days = new List<DayOfWeek>();
         }
-
-        public event PropertyChangedEventHandler PropertyChanged;
     }
 }

+ 48 - 44
MediaBrowser.Model/LiveTv/TimerInfoDto.cs

@@ -3,8 +3,47 @@ using System.ComponentModel;
 
 namespace MediaBrowser.Model.LiveTv
 {
-    public class TimerInfoDto : INotifyPropertyChanged
+    public class TimerInfoDto : BaseTimerInfoDto
     {
+        /// <summary>
+        /// Gets or sets the status.
+        /// </summary>
+        /// <value>The status.</value>
+        public RecordingStatus Status { get; set; }
+
+        /// <summary>
+        /// Gets or sets the series timer identifier.
+        /// </summary>
+        /// <value>The series timer identifier.</value>
+        public string SeriesTimerId { get; set; }
+
+        /// <summary>
+        /// Gets or sets the external series timer identifier.
+        /// </summary>
+        /// <value>The external series timer identifier.</value>
+        public string ExternalSeriesTimerId { get; set; }
+
+        /// <summary>
+        /// Gets or sets the run time ticks.
+        /// </summary>
+        /// <value>The run time ticks.</value>
+        public long? RunTimeTicks { get; set; }
+
+        /// <summary>
+        /// Gets or sets the program information.
+        /// </summary>
+        /// <value>The program information.</value>
+        public ProgramInfoDto ProgramInfo { get; set; }
+
+    }
+
+    public class BaseTimerInfoDto : INotifyPropertyChanged
+    {
+        /// <summary>
+        /// Occurs when a property value changes.
+        /// </summary>
+        public event PropertyChangedEventHandler PropertyChanged;
+
         /// <summary>
         /// Id of the recording.
         /// </summary>
@@ -26,18 +65,12 @@ namespace MediaBrowser.Model.LiveTv
         /// </summary>
         /// <value>The external channel identifier.</value>
         public string ExternalChannelId { get; set; }
-        
+
         /// <summary>
         /// ChannelName of the recording.
         /// </summary>
         public string ChannelName { get; set; }
 
-        /// <summary>
-        /// Gets or sets the name of the service.
-        /// </summary>
-        /// <value>The name of the service.</value>
-        public string ServiceName { get; set; }
-
         /// <summary>
         /// Gets or sets the program identifier.
         /// </summary>
@@ -49,7 +82,7 @@ namespace MediaBrowser.Model.LiveTv
         /// </summary>
         /// <value>The external program identifier.</value>
         public string ExternalProgramId { get; set; }
-        
+
         /// <summary>
         /// Name of the recording.
         /// </summary>
@@ -71,22 +104,16 @@ namespace MediaBrowser.Model.LiveTv
         public DateTime EndDate { get; set; }
 
         /// <summary>
-        /// Gets or sets the status.
-        /// </summary>
-        /// <value>The status.</value>
-        public RecordingStatus Status { get; set; }
-
-        /// <summary>
-        /// Gets or sets the series timer identifier.
+        /// Gets or sets the name of the service.
         /// </summary>
-        /// <value>The series timer identifier.</value>
-        public string SeriesTimerId { get; set; }
+        /// <value>The name of the service.</value>
+        public string ServiceName { get; set; }
 
         /// <summary>
-        /// Gets or sets the external series timer identifier.
+        /// Gets or sets the priority.
         /// </summary>
-        /// <value>The external series timer identifier.</value>
-        public string ExternalSeriesTimerId { get; set; }
+        /// <value>The priority.</value>
+        public int Priority { get; set; }
 
         /// <summary>
         /// Gets or sets the pre padding seconds.
@@ -111,28 +138,5 @@ namespace MediaBrowser.Model.LiveTv
         /// </summary>
         /// <value><c>true</c> if this instance is post padding required; otherwise, <c>false</c>.</value>
         public bool IsPostPaddingRequired { get; set; }
-
-        /// <summary>
-        /// Gets or sets the run time ticks.
-        /// </summary>
-        /// <value>The run time ticks.</value>
-        public long? RunTimeTicks { get; set; }
-
-        /// <summary>
-        /// Gets or sets the priority.
-        /// </summary>
-        /// <value>The priority.</value>
-        public int Priority { get; set; }
-
-        /// <summary>
-        /// Gets or sets the program information.
-        /// </summary>
-        /// <value>The program information.</value>
-        public ProgramInfoDto ProgramInfo { get; set; }
-
-        /// <summary>
-        /// Occurs when a property value changes.
-        /// </summary>
-        public event PropertyChangedEventHandler PropertyChanged;
     }
 }

+ 1 - 1
MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs

@@ -88,7 +88,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
 
                 CreateRules(device);
             }
-            catch (Exception ex)
+            catch (Exception)
             {
                 //_logger.ErrorException("Error creating port forwarding rules", ex);
             }

+ 2 - 2
MediaBrowser.ServerApplication/ApplicationHost.cs

@@ -266,7 +266,7 @@ namespace MediaBrowser.ServerApplication
             {
                 MigrateUserFolders();
             }
-            catch (IOException ex)
+            catch (IOException)
             {
             }
 
@@ -498,7 +498,7 @@ namespace MediaBrowser.ServerApplication
             var appThemeManager = new AppThemeManager(ApplicationPaths, FileSystemManager, JsonSerializer, Logger);
             RegisterSingleInstance<IAppThemeManager>(appThemeManager);
 
-            var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager);
+            var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager, JsonSerializer);
             RegisterSingleInstance<IDlnaManager>(dlnaManager);
 
             var collectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor);

+ 3 - 3
MediaBrowser.WebDashboard/ApiClient.js

@@ -3809,7 +3809,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
                 var deferred = $.Deferred();
 
                 var msg = [itemId, canSeek, queueableMediaTypes];
-                
+
                 if (mediaSourceId) {
                     msg.push(mediaSourceId);
                 }
@@ -4029,7 +4029,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
 
         self.createPackageReview = function (review) {
 
-            var url = self.getUrl("PackageReviews/" + review.id, review);
+            var url = self.getUrl("Packages/Reviews/" + review.id, review);
 
             return self.ajax({
                 type: "POST",
@@ -4058,7 +4058,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
                 options.ForceTitle = true;
             }
 
-            var url = self.getUrl("PackageReviews/" + packageId, options);
+            var url = self.getUrl("Packages/" + packageId + "Reviews", options);
 
             return self.ajax({
                 type: "GET",

+ 1 - 1
MediaBrowser.WebDashboard/packages.config

@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="MediaBrowser.ApiClient.Javascript" version="3.0.247" targetFramework="net45" />
+  <package id="MediaBrowser.ApiClient.Javascript" version="3.0.248" targetFramework="net45" />
 </packages>

+ 2 - 2
Nuget/MediaBrowser.Common.Internal.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common.Internal</id>
-        <version>3.0.343</version>
+        <version>3.0.345</version>
         <title>MediaBrowser.Common.Internal</title>
         <authors>Luke</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
         <copyright>Copyright © Media Browser 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.343" />
+            <dependency id="MediaBrowser.Common" version="3.0.345" />
             <dependency id="NLog" version="2.1.0" />
             <dependency id="SimpleInjector" version="2.4.1" />
             <dependency id="sharpcompress" version="0.10.2" />

+ 1 - 1
Nuget/MediaBrowser.Common.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common</id>
-        <version>3.0.343</version>
+        <version>3.0.345</version>
         <title>MediaBrowser.Common</title>
         <authors>Media Browser Team</authors>
         <owners>ebr,Luke,scottisafool</owners>

+ 2 - 2
Nuget/MediaBrowser.Server.Core.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Server.Core</id>
-        <version>3.0.343</version>
+        <version>3.0.345</version>
         <title>Media Browser.Server.Core</title>
         <authors>Media Browser Team</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains core components required to build plugins for Media Browser Server.</description>
         <copyright>Copyright © Media Browser 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.343" />
+            <dependency id="MediaBrowser.Common" version="3.0.345" />
         </dependencies>
     </metadata>
     <files>

Some files were not shown because too many files changed in this diff