2
0
Luke Pulverenti 11 жил өмнө
parent
commit
21308be83f
71 өөрчлөгдсөн 1133 нэмэгдсэн , 873 устгасан
  1. 95 16
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  2. 1 1
      MediaBrowser.Api/Playback/StreamState.cs
  3. 0 7
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  4. 1 0
      MediaBrowser.Controller/MediaEncoding/EncodingOptions.cs
  5. 2 1
      MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
  6. 36 8
      MediaBrowser.Dlna/PlayTo/Device.cs
  7. 21 0
      MediaBrowser.Dlna/PlayTo/DeviceIcon.cs
  8. 2 1
      MediaBrowser.Dlna/PlayTo/DeviceInfo.cs
  9. 1 0
      MediaBrowser.Dlna/PlayTo/DlnaController.cs
  10. 1 1
      MediaBrowser.Dlna/PlayTo/PlaylistItem.cs
  11. 1 3
      MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs
  12. 0 48
      MediaBrowser.Dlna/PlayTo/uIcon.cs
  13. 1 0
      MediaBrowser.Dlna/Profiles/DefaultProfile.cs
  14. 1 0
      MediaBrowser.Dlna/Profiles/DenonAvrProfile.cs
  15. 1 0
      MediaBrowser.Dlna/Profiles/Foobar2000Profile.cs
  16. 1 0
      MediaBrowser.Dlna/Profiles/LgTvProfile.cs
  17. 1 0
      MediaBrowser.Dlna/Profiles/LinksysDMA2100Profile.cs
  18. 1 0
      MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs
  19. 1 0
      MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs
  20. 1 0
      MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs
  21. 1 0
      MediaBrowser.Dlna/Profiles/SonyBlurayPlayerProfile.cs
  22. 1 0
      MediaBrowser.Dlna/Profiles/SonyBravia2010Profile.cs
  23. 1 0
      MediaBrowser.Dlna/Profiles/SonyBravia2011Profile.cs
  24. 1 0
      MediaBrowser.Dlna/Profiles/SonyBravia2012Profile.cs
  25. 1 0
      MediaBrowser.Dlna/Profiles/SonyBravia2013Profile.cs
  26. 1 0
      MediaBrowser.Dlna/Profiles/SonyPs3Profile.cs
  27. 1 0
      MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs
  28. 141 0
      MediaBrowser.Dlna/Profiles/Windows81Profile.cs
  29. 2 2
      MediaBrowser.Dlna/Profiles/Xbox360Profile.cs
  30. 1 0
      MediaBrowser.Dlna/Profiles/XboxOneProfile.cs
  31. 27 3
      MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
  32. 27 3
      MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
  33. 2 3
      MediaBrowser.Model/Dlna/CodecProfile.cs
  34. 2 2
      MediaBrowser.Model/Dlna/ContainerProfile.cs
  35. 2 3
      MediaBrowser.Model/Dlna/DeviceIdentification.cs
  36. 3 4
      MediaBrowser.Model/Dlna/DeviceProfile.cs
  37. 4 4
      MediaBrowser.Model/Dlna/DirectPlayProfile.cs
  38. 4 4
      MediaBrowser.Model/Dlna/ResponseProfile.cs
  39. 533 0
      MediaBrowser.Model/Dlna/StreamBuilder.cs
  40. 124 0
      MediaBrowser.Model/Dlna/StreamInfo.cs
  41. 2 2
      MediaBrowser.Model/Dlna/TranscodingProfile.cs
  42. 2 0
      MediaBrowser.Model/Dto/MediaVersionInfo.cs
  43. 0 20
      MediaBrowser.Model/Extensions/ModelExtensions.cs
  44. 10 1
      MediaBrowser.Model/MediaBrowser.Model.csproj
  45. 13 3
      MediaBrowser.Model/Updates/PackageVersionInfo.cs
  46. 2 1
      MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
  47. 1 31
      MediaBrowser.Server.Implementations/Localization/JavaScript/de.json
  48. 1 31
      MediaBrowser.Server.Implementations/Localization/JavaScript/en_US.json
  49. 1 31
      MediaBrowser.Server.Implementations/Localization/JavaScript/es.json
  50. 1 31
      MediaBrowser.Server.Implementations/Localization/JavaScript/fr.json
  51. 1 0
      MediaBrowser.Server.Implementations/Localization/JavaScript/it.json
  52. 0 1
      MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json
  53. 1 31
      MediaBrowser.Server.Implementations/Localization/JavaScript/nl.json
  54. 1 31
      MediaBrowser.Server.Implementations/Localization/JavaScript/pt_BR.json
  55. 1 31
      MediaBrowser.Server.Implementations/Localization/JavaScript/pt_PT.json
  56. 0 31
      MediaBrowser.Server.Implementations/Localization/JavaScript/ru.json
  57. 1 31
      MediaBrowser.Server.Implementations/Localization/JavaScript/zh_TW.json
  58. 2 0
      MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs
  59. 0 50
      MediaBrowser.Server.Implementations/Localization/Server/de.json
  60. 0 50
      MediaBrowser.Server.Implementations/Localization/Server/en_US.json
  61. 0 50
      MediaBrowser.Server.Implementations/Localization/Server/es.json
  62. 0 50
      MediaBrowser.Server.Implementations/Localization/Server/fr.json
  63. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/he.json
  64. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/it.json
  65. 0 50
      MediaBrowser.Server.Implementations/Localization/Server/nl.json
  66. 0 50
      MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json
  67. 0 50
      MediaBrowser.Server.Implementations/Localization/Server/pt_PT.json
  68. 0 50
      MediaBrowser.Server.Implementations/Localization/Server/ru.json
  69. 41 2
      MediaBrowser.Server.Implementations/Localization/Server/server.json
  70. 0 50
      MediaBrowser.Server.Implementations/Localization/Server/zh_TW.json
  71. 4 0
      MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

+ 95 - 16
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -9,6 +9,7 @@ using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Drawing;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.IO;
@@ -247,6 +248,11 @@ namespace MediaBrowser.Api.Playback
                 }
             }
 
+            if (type == MediaStreamType.Video)
+            {
+                streams = streams.Where(i => !string.Equals(i.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase)).ToList();
+            }
+
             if (returnFirstIfNoIndex && type == MediaStreamType.Audio)
             {
                 return streams.FirstOrDefault(i => i.Channels.HasValue && i.Channels.Value > 0) ??
@@ -1018,7 +1024,7 @@ namespace MediaBrowser.Api.Playback
 
                     return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
                 }
-                
+
                 return string.Format(" -maxrate {0} -bufsize {1}",
                     bitrate.Value.ToString(UsCulture),
                     (bitrate.Value * 2).ToString(UsCulture));
@@ -1256,6 +1262,69 @@ namespace MediaBrowser.Api.Playback
             }
         }
 
+        /// <summary>
+        /// Parses the dlna headers.
+        /// </summary>
+        /// <param name="request">The request.</param>
+        private void ParseDlnaHeaders(StreamRequest request)
+        {
+            if (!request.StartTimeTicks.HasValue)
+            {
+                var timeSeek = GetHeader("TimeSeekRange.dlna.org");
+
+                request.StartTimeTicks = ParseTimeSeekHeader(timeSeek);
+            }
+        }
+
+        /// <summary>
+        /// Parses the time seek header.
+        /// </summary>
+        private long? ParseTimeSeekHeader(string value)
+        {
+            if (string.IsNullOrWhiteSpace(value))
+            {
+                return null;
+            }
+
+            if (value.IndexOf("npt=", StringComparison.OrdinalIgnoreCase) != 0)
+            {
+                throw new ArgumentException("Invalid timeseek header");
+            }
+            value = value.Substring(4).Split(new[] { '-' }, 2)[0];
+
+            if (value.IndexOf(':') == -1)
+            {
+                // Parses npt times in the format of '417.33'
+                double seconds;
+                if (double.TryParse(value, NumberStyles.Any, UsCulture, out seconds))
+                {
+                    return TimeSpan.FromSeconds(seconds).Ticks;
+                }
+
+                throw new ArgumentException("Invalid timeseek header");
+            }
+
+            // Parses npt times in the format of '10:19:25.7'
+            var tokens = value.Split(new[] { ':' }, 3);
+            double secondsSum = 0;
+            var timeFactor = 3600;
+
+            foreach (var time in tokens)
+            {
+                double digit;
+                if (double.TryParse(time, NumberStyles.Any, UsCulture, out digit))
+                {
+                    secondsSum += (digit * timeFactor);
+                }
+                else
+                {
+                    throw new ArgumentException("Invalid timeseek header");
+                }
+                timeFactor /= 60;
+            }
+            return TimeSpan.FromSeconds(secondsSum).Ticks;
+        }
+
         /// <summary>
         /// Gets the state.
         /// </summary>
@@ -1264,6 +1333,8 @@ namespace MediaBrowser.Api.Playback
         /// <returns>StreamState.</returns>
         protected async Task<StreamState> GetState(StreamRequest request, CancellationToken cancellationToken)
         {
+            ParseDlnaHeaders(request);
+
             if (!string.IsNullOrWhiteSpace(request.Params))
             {
                 ParseParams(request);
@@ -1509,8 +1580,6 @@ namespace MediaBrowser.Api.Playback
         /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
         protected void AddDlnaHeaders(StreamState state, IDictionary<string, string> responseHeaders, bool isStaticallyStreamed)
         {
-            var timeSeek = GetHeader("TimeSeekRange.dlna.org");
-
             var transferMode = GetHeader("transferMode.dlna.org");
             responseHeaders["transferMode.dlna.org"] = string.IsNullOrEmpty(transferMode) ? "Streaming" : transferMode;
             responseHeaders["realTimeInfo.dlna.org"] = "DLNA.ORG_TLAG=*";
@@ -1521,11 +1590,21 @@ namespace MediaBrowser.Api.Playback
             // first bit means Time based seek supported, second byte range seek supported (not sure about the order now), so 01 = only byte seek, 10 = time based, 11 = both, 00 = none
             var orgOp = ";DLNA.ORG_OP=";
 
-            // Time-based seeking currently only possible when transcoding
-            orgOp += isStaticallyStreamed ? "0" : "1";
+            if (state.RunTimeTicks.HasValue)
+            {
+                // Time-based seeking currently only possible when transcoding
+                orgOp += isStaticallyStreamed ? "0" : "1";
+
+                // Byte-based seeking only possible when not transcoding
+                orgOp += isStaticallyStreamed || state.TranscodeSeekInfo == TranscodeSeekInfo.Bytes ? "1" : "0";
 
-            // Byte-based seeking only possible when not transcoding
-            orgOp += isStaticallyStreamed || state.TranscodeSeekInfo == TranscodeSeekInfo.Bytes ? "1" : "0";
+                AddTimeSeekResponseHeaders(state, responseHeaders);
+            }
+            else
+            {
+                // No seeking is available if we don't know the content runtime
+                orgOp += "00";
+            }
 
             // 0 = native, 1 = transcoded
             var orgCi = isStaticallyStreamed ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";
@@ -1568,15 +1647,6 @@ namespace MediaBrowser.Api.Playback
             {
                 contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL";
             }
-            //else if (string.Equals(extension, ".wmv", StringComparison.OrdinalIgnoreCase))
-            //{
-            //    contentFeatures = "DLNA.ORG_PN=WMVHIGH_BASE";
-            //}
-            //else if (string.Equals(extension, ".asf", StringComparison.OrdinalIgnoreCase))
-            //{
-            //    // ??
-            //    contentFeatures = "DLNA.ORG_PN=WMVHIGH_BASE";
-            //}
 
             if (!string.IsNullOrEmpty(contentFeatures))
             {
@@ -1589,6 +1659,15 @@ namespace MediaBrowser.Api.Playback
             }
         }
 
+        private void AddTimeSeekResponseHeaders(StreamState state, IDictionary<string, string> responseHeaders)
+        {
+            var runtimeSeconds = TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalSeconds.ToString(UsCulture);
+            var startSeconds = TimeSpan.FromTicks(state.Request.StartTimeTicks ?? 0).TotalSeconds.ToString(UsCulture);
+
+            responseHeaders["TimeSeekRange.dlna.org"] = string.Format("npt={0}-{1}/{1}", startSeconds, runtimeSeconds);
+            responseHeaders["X-AvailableSeekRange"] = string.Format("1 npt={0}-{1}", startSeconds, runtimeSeconds);
+        }
+
         /// <summary>
         /// Enforces the resolution limit.
         /// </summary>

+ 1 - 1
MediaBrowser.Api/Playback/StreamState.cs

@@ -1,5 +1,5 @@
 using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.IO;
 using System.Collections.Generic;

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

@@ -78,14 +78,7 @@
     <Compile Include="Channels\Channel.cs" />
     <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" />
-    <Compile Include="Dlna\DeviceProfile.cs" />
-    <Compile Include="Dlna\ResponseProfile.cs" />
-    <Compile Include="Dlna\TranscodingProfile.cs" />
     <Compile Include="Drawing\IImageProcessor.cs" />
     <Compile Include="Drawing\ImageFormat.cs" />
     <Compile Include="Drawing\ImageProcessingOptions.cs" />

+ 1 - 0
MediaBrowser.Controller/MediaEncoding/EncodingOptions.cs

@@ -1,4 +1,5 @@
 using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Model.Dlna;
 
 namespace MediaBrowser.Controller.MediaEncoding
 {

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

@@ -70,6 +70,7 @@
     <Compile Include="PlayTo\PlayToServerEntryPoint.cs" />
     <Compile Include="PlayTo\ServiceAction.cs" />
     <Compile Include="Profiles\Foobar2000Profile.cs" />
+    <Compile Include="Profiles\Windows81Profile.cs" />
     <Compile Include="Ssdp\SsdpHelper.cs" />
     <Compile Include="PlayTo\SsdpHttpClient.cs" />
     <Compile Include="PlayTo\StateVariable.cs" />
@@ -78,7 +79,7 @@
     <Compile Include="PlayTo\TransportStateEventArgs.cs" />
     <Compile Include="PlayTo\uBaseObject.cs" />
     <Compile Include="PlayTo\uContainer.cs" />
-    <Compile Include="PlayTo\uIcon.cs" />
+    <Compile Include="PlayTo\DeviceIcon.cs" />
     <Compile Include="PlayTo\uParser.cs" />
     <Compile Include="PlayTo\uPnpNamespaces.cs" />
     <Compile Include="Profiles\DefaultProfile.cs" />

+ 36 - 8
MediaBrowser.Dlna/PlayTo/Device.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Common.Net;
+using System.Globalization;
+using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Model.Logging;
 using System;
@@ -453,10 +454,10 @@ namespace MediaBrowser.Dlna.PlayTo
             var volume = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetVolumeResponse").Select(i => i.Element("CurrentVolume")).FirstOrDefault(i => i != null);
             var volumeValue = volume == null ? null : volume.Value;
 
-            if (volumeValue == null)
+            if (string.IsNullOrWhiteSpace(volumeValue))
                 return;
 
-            Volume = Int32.Parse(volumeValue);
+            Volume = int.Parse(volumeValue, UsCulture);
 
             //Reset the Mute value if Volume is bigger than zero
             if (Volume > 0 && _muteVol > 0)
@@ -555,17 +556,17 @@ namespace MediaBrowser.Dlna.PlayTo
             var durationElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackDuration")).FirstOrDefault(i => i != null);
             var duration = durationElem == null ? null : durationElem.Value;
 
-            if (duration != null)
+            if (!string.IsNullOrWhiteSpace(duration))
             {
-                Duration = TimeSpan.Parse(duration);
+                Duration = TimeSpan.Parse(duration, UsCulture);
             }
 
             var positionElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("RelTime")).FirstOrDefault(i => i != null);
             var position = positionElem == null ? null : positionElem.Value;
 
-            if (position != null)
+            if (!string.IsNullOrWhiteSpace(position))
             {
-                Position = TimeSpan.Parse(position);
+                Position = TimeSpan.Parse(position, UsCulture);
             }
 
             var track = result.Document.Descendants("TrackMetaData").Select(i => i.Value)
@@ -701,7 +702,7 @@ namespace MediaBrowser.Dlna.PlayTo
 
             if (icon != null)
             {
-                deviceProperties.Icon = uIcon.Create(icon);
+                deviceProperties.Icon = CreateIcon(icon);
             }
 
             var isRenderer = false;
@@ -746,6 +747,33 @@ namespace MediaBrowser.Dlna.PlayTo
 
         #endregion
 
+        private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
+        private static DeviceIcon CreateIcon(XElement element)
+        {
+            if (element == null)
+            {
+                throw new ArgumentNullException("element");
+            }
+
+            var mimeType = element.GetDescendantValue(uPnpNamespaces.ud.GetName("mimetype"));
+            var width = element.GetDescendantValue(uPnpNamespaces.ud.GetName("width"));
+            var height = element.GetDescendantValue(uPnpNamespaces.ud.GetName("height"));
+            var depth = element.GetDescendantValue(uPnpNamespaces.ud.GetName("depth"));
+            var url = element.GetDescendantValue(uPnpNamespaces.ud.GetName("url"));
+
+            var widthValue = int.Parse(width, NumberStyles.Any, UsCulture);
+            var heightValue = int.Parse(height, NumberStyles.Any, UsCulture);
+
+            return new DeviceIcon
+            {
+                Depth = depth,
+                Height = heightValue,
+                MimeType = mimeType,
+                Url = url,
+                Width = widthValue
+            };
+        }
+
         private static DeviceService Create(XElement element)
         {
             var type = element.GetDescendantValue(uPnpNamespaces.ud.GetName("serviceType"));

+ 21 - 0
MediaBrowser.Dlna/PlayTo/DeviceIcon.cs

@@ -0,0 +1,21 @@
+
+namespace MediaBrowser.Dlna.PlayTo
+{
+    public class DeviceIcon
+    {
+        public string Url { get; set; }
+
+        public string MimeType { get; set; }
+
+        public int Width { get; set; }
+
+        public int Height { get; set; }
+
+        public string Depth { get; set; }
+
+        public override string ToString()
+        {
+            return string.Format("{0}x{1}", Height, Width);
+        }
+    }
+}

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

@@ -1,5 +1,6 @@
 using MediaBrowser.Controller.Dlna;
 using System.Collections.Generic;
+using MediaBrowser.Model.Dlna;
 
 namespace MediaBrowser.Dlna.PlayTo
 {
@@ -46,7 +47,7 @@ namespace MediaBrowser.Dlna.PlayTo
             }
         }
 
-        public uIcon Icon { get; set; }
+        public DeviceIcon Icon { get; set; }
 
         private readonly List<DeviceService> _services = new List<DeviceService>();
         public List<DeviceService> Services

+ 1 - 0
MediaBrowser.Dlna/PlayTo/DlnaController.cs

@@ -6,6 +6,7 @@ using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Session;

+ 1 - 1
MediaBrowser.Dlna/PlayTo/PlaylistItem.cs

@@ -1,4 +1,4 @@
-using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Model.Dlna;
 
 namespace MediaBrowser.Dlna.PlayTo
 {

+ 1 - 3
MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs

@@ -1,6 +1,7 @@
 using MediaBrowser.Controller.Dlna;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Entities;
 using System;
 using System.Collections.Generic;
@@ -184,7 +185,6 @@ namespace MediaBrowser.Dlna.PlayTo
                         }
                         break;
                     }
-                    case ProfileConditionValue.Filesize:
                     case ProfileConditionValue.AudioProfile:
                     case ProfileConditionValue.Has64BitOffsets:
                     case ProfileConditionValue.VideoBitDepth:
@@ -444,8 +444,6 @@ namespace MediaBrowser.Dlna.PlayTo
                     return audioStream == null ? null : audioStream.BitRate;
                 case ProfileConditionValue.AudioChannels:
                     return audioStream == null ? null : audioStream.Channels;
-                case ProfileConditionValue.Filesize:
-                    return new FileInfo(mediaPath).Length;
                 case ProfileConditionValue.VideoBitrate:
                     return videoStream == null ? null : videoStream.BitRate;
                 case ProfileConditionValue.VideoFramerate:

+ 0 - 48
MediaBrowser.Dlna/PlayTo/uIcon.cs

@@ -1,48 +0,0 @@
-using System;
-using System.Xml.Linq;
-
-namespace MediaBrowser.Dlna.PlayTo
-{
-    public class uIcon
-    {
-        public string Url { get; private set; }
-
-        public string MimeType { get; private set; }
-
-        public int Width { get; private set; }
-
-        public int Height { get; private set; }
-
-        public string Depth { get; private set; }
-
-        public uIcon(string mimeType, string width, string height, string depth, string url)
-        {
-            MimeType = mimeType;
-            Width = (!string.IsNullOrEmpty(width)) ? int.Parse(width) : 0;
-            Height = (!string.IsNullOrEmpty(height)) ? int.Parse(height) : 0;
-            Depth = depth;
-            Url = url;
-        }
-
-        public static uIcon Create(XElement element)
-        {
-            if (element == null)
-            {
-                throw new ArgumentNullException("element");
-            }
-
-            var mimeType = element.GetDescendantValue(uPnpNamespaces.ud.GetName("mimetype"));
-            var width = element.GetDescendantValue(uPnpNamespaces.ud.GetName("width"));
-            var height = element.GetDescendantValue(uPnpNamespaces.ud.GetName("height"));
-            var depth = element.GetDescendantValue(uPnpNamespaces.ud.GetName("depth"));
-            var url = element.GetDescendantValue(uPnpNamespaces.ud.GetName("url"));
-
-            return new uIcon(mimeType, width, height, depth, url);
-        }
-
-        public override string ToString()
-        {
-            return string.Format("{0}x{1}", Height, Width);
-        }
-    }
-}

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

@@ -1,5 +1,6 @@
 using MediaBrowser.Controller.Dlna;
 using System.Xml.Serialization;
+using MediaBrowser.Model.Dlna;
 
 namespace MediaBrowser.Dlna.Profiles
 {

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

@@ -1,5 +1,6 @@
 using System.Xml.Serialization;
 using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Model.Dlna;
 
 namespace MediaBrowser.Dlna.Profiles
 {

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

@@ -1,5 +1,6 @@
 using MediaBrowser.Controller.Dlna;
 using System.Xml.Serialization;
+using MediaBrowser.Model.Dlna;
 
 namespace MediaBrowser.Dlna.Profiles
 {

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

@@ -1,5 +1,6 @@
 using System.Xml.Serialization;
 using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Model.Dlna;
 
 namespace MediaBrowser.Dlna.Profiles
 {

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

@@ -1,5 +1,6 @@
 using System.Xml.Serialization;
 using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Model.Dlna;
 
 namespace MediaBrowser.Dlna.Profiles
 {

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

@@ -1,5 +1,6 @@
 using System.Xml.Serialization;
 using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Model.Dlna;
 
 namespace MediaBrowser.Dlna.Profiles
 {

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

@@ -1,5 +1,6 @@
 using System.Xml.Serialization;
 using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Model.Dlna;
 
 namespace MediaBrowser.Dlna.Profiles
 {

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

@@ -1,5 +1,6 @@
 using System.Xml.Serialization;
 using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Model.Dlna;
 
 namespace MediaBrowser.Dlna.Profiles
 {

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

@@ -1,5 +1,6 @@
 using System.Xml.Serialization;
 using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Model.Dlna;
 
 namespace MediaBrowser.Dlna.Profiles
 {

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

@@ -1,5 +1,6 @@
 using System.Xml.Serialization;
 using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Model.Dlna;
 
 namespace MediaBrowser.Dlna.Profiles
 {

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

@@ -1,5 +1,6 @@
 using System.Xml.Serialization;
 using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Model.Dlna;
 
 namespace MediaBrowser.Dlna.Profiles
 {

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

@@ -1,5 +1,6 @@
 using System.Xml.Serialization;
 using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Model.Dlna;
 
 namespace MediaBrowser.Dlna.Profiles
 {

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

@@ -1,5 +1,6 @@
 using System.Xml.Serialization;
 using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Model.Dlna;
 
 namespace MediaBrowser.Dlna.Profiles
 {

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

@@ -1,5 +1,6 @@
 using System.Xml.Serialization;
 using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Model.Dlna;
 
 namespace MediaBrowser.Dlna.Profiles
 {

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

@@ -1,5 +1,6 @@
 using MediaBrowser.Controller.Dlna;
 using System.Xml.Serialization;
+using MediaBrowser.Model.Dlna;
 
 namespace MediaBrowser.Dlna.Profiles
 {

+ 141 - 0
MediaBrowser.Dlna/Profiles/Windows81Profile.cs

@@ -0,0 +1,141 @@
+using System.Xml.Serialization;
+using MediaBrowser.Model.Dlna;
+
+namespace MediaBrowser.Dlna.Profiles
+{
+    [XmlRoot("Profile")]
+    public class Windows81Profile : DefaultProfile
+    {
+        public Windows81Profile()
+        {
+            Name = "Windows 8/RT";
+
+            Identification = new DeviceIdentification
+            {
+                Manufacturer = "Microsoft SDK Customer"
+            };
+
+            TranscodingProfiles = new[]
+            {
+                new TranscodingProfile
+                {
+                    Container = "mp3",
+                    AudioCodec = "mp3",
+                    Type = DlnaProfileType.Audio
+                },
+                new TranscodingProfile
+                {
+                    Container = "ts",
+                    VideoCodec = "h264",
+                    AudioCodec = "aac",
+                    Type = DlnaProfileType.Video,
+                    VideoProfile = "Baseline"
+                }
+            };
+
+            DirectPlayProfiles = new[]
+            {
+                new DirectPlayProfile
+                {
+                    Container = "mp4,mov",
+                    VideoCodec = "h264,mpeg4",
+                    AudioCodec = "aac,ac3,eac3,mp3,pcm",
+                    Type = DlnaProfileType.Video
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "ts",
+                    VideoCodec = "h264",
+                    AudioCodec = "aac,ac3,eac3,mp3,mp2,pcm",
+                    Type = DlnaProfileType.Video
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "asf",
+                    VideoCodec = "wmv2,wmv3,vc1",
+                    AudioCodec = "wmav2,wmapro,wmavoice",
+                    Type = DlnaProfileType.Video
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "avi",
+                    VideoCodec = "mpeg4,msmpeg4,mjpeg",
+                    AudioCodec = "mp3,ac3,eac3,mp2,pcm",
+                    Type = DlnaProfileType.Video
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "mp4",
+                    AudioCodec = "aac",
+                    Type = DlnaProfileType.Audio
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "mp3",
+                    AudioCodec = "mp3",
+                    Type = DlnaProfileType.Audio
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "jpeg",
+                    Type = DlnaProfileType.Photo
+                }
+            };
+
+            CodecProfiles = new[]
+            {
+                new CodecProfile
+                {
+                    Type = CodecType.Video,
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.VideoBitDepth,
+                            Value = "8",
+                            IsRequired = false
+                        }
+                    }
+                },
+
+                new CodecProfile
+                {
+                    Type = CodecType.VideoAudio,
+                    Codec = "aac,eac3",
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.AudioChannels,
+                            Value = "8"
+                        }
+                    }
+                },
+
+                new CodecProfile
+                {
+                    Type = CodecType.VideoAudio,
+                    Codec = "ac3",
+                    Conditions = new []
+                    {
+                        new ProfileCondition
+                        {
+                            Condition = ProfileConditionType.LessThanEqual,
+                            Property = ProfileConditionValue.AudioChannels,
+                            Value = "6"
+                        }
+                    }
+                }
+            };
+
+        }
+    }
+}

+ 2 - 2
MediaBrowser.Dlna/Profiles/Xbox360Profile.cs

@@ -1,5 +1,5 @@
-using System.Xml.Serialization;
-using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Model.Dlna;
+using System.Xml.Serialization;
 
 namespace MediaBrowser.Dlna.Profiles
 {

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

@@ -1,5 +1,6 @@
 using System.Xml.Serialization;
 using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Model.Dlna;
 
 namespace MediaBrowser.Dlna.Profiles
 {

+ 27 - 3
MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj

@@ -101,9 +101,36 @@
     <Compile Include="..\MediaBrowser.Model\Configuration\UserConfiguration.cs">
       <Link>Configuration\UserConfiguration.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dlna\CodecProfile.cs">
+      <Link>Dlna\CodecProfile.cs</Link>
+    </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dlna\ContainerProfile.cs">
+      <Link>Dlna\ContainerProfile.cs</Link>
+    </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dlna\DeviceIdentification.cs">
+      <Link>Dlna\DeviceIdentification.cs</Link>
+    </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dlna\DeviceProfile.cs">
+      <Link>Dlna\DeviceProfile.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Dlna\DeviceProfileInfo.cs">
       <Link>Dlna\DeviceProfileInfo.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dlna\DirectPlayProfile.cs">
+      <Link>Dlna\DirectPlayProfile.cs</Link>
+    </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dlna\ResponseProfile.cs">
+      <Link>Dlna\ResponseProfile.cs</Link>
+    </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dlna\StreamBuilder.cs">
+      <Link>Dlna\StreamBuilder.cs</Link>
+    </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dlna\StreamInfo.cs">
+      <Link>Dlna\StreamInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dlna\TranscodingProfile.cs">
+      <Link>Dlna\TranscodingProfile.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Drawing\DrawingUtils.cs">
       <Link>Drawing\DrawingUtils.cs</Link>
     </Compile>
@@ -224,9 +251,6 @@
     <Compile Include="..\MediaBrowser.Model\Entities\VirtualFolderInfo.cs">
       <Link>Entities\VirtualFolderInfo.cs</Link>
     </Compile>
-    <Compile Include="..\MediaBrowser.Model\Extensions\ModelExtensions.cs">
-      <Link>Extensions\ModelExtensions.cs</Link>
-    </Compile>
     <Compile Include="..\MediaBrowser.Model\FileOrganization\FileOrganizationQuery.cs">
       <Link>FileOrganization\FileOrganizationQuery.cs</Link>
     </Compile>

+ 27 - 3
MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj

@@ -88,9 +88,36 @@
     <Compile Include="..\MediaBrowser.Model\Configuration\UserConfiguration.cs">
       <Link>Configuration\UserConfiguration.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dlna\CodecProfile.cs">
+      <Link>Dlna\CodecProfile.cs</Link>
+    </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dlna\ContainerProfile.cs">
+      <Link>Dlna\ContainerProfile.cs</Link>
+    </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dlna\DeviceIdentification.cs">
+      <Link>Dlna\DeviceIdentification.cs</Link>
+    </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dlna\DeviceProfile.cs">
+      <Link>Dlna\DeviceProfile.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Dlna\DeviceProfileInfo.cs">
       <Link>Dlna\DeviceProfileInfo.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dlna\DirectPlayProfile.cs">
+      <Link>Dlna\DirectPlayProfile.cs</Link>
+    </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dlna\ResponseProfile.cs">
+      <Link>Dlna\ResponseProfile.cs</Link>
+    </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dlna\StreamBuilder.cs">
+      <Link>Dlna\StreamBuilder.cs</Link>
+    </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dlna\StreamInfo.cs">
+      <Link>Dlna\StreamInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dlna\TranscodingProfile.cs">
+      <Link>Dlna\TranscodingProfile.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Drawing\DrawingUtils.cs">
       <Link>Drawing\DrawingUtils.cs</Link>
     </Compile>
@@ -211,9 +238,6 @@
     <Compile Include="..\MediaBrowser.Model\Entities\VirtualFolderInfo.cs">
       <Link>Entities\VirtualFolderInfo.cs</Link>
     </Compile>
-    <Compile Include="..\MediaBrowser.Model\Extensions\ModelExtensions.cs">
-      <Link>Extensions\ModelExtensions.cs</Link>
-    </Compile>
     <Compile Include="..\MediaBrowser.Model\FileOrganization\FileOrganizationQuery.cs">
       <Link>FileOrganization\FileOrganizationQuery.cs</Link>
     </Compile>

+ 2 - 3
MediaBrowser.Controller/Dlna/CodecProfile.cs → MediaBrowser.Model/Dlna/CodecProfile.cs

@@ -3,7 +3,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Xml.Serialization;
 
-namespace MediaBrowser.Controller.Dlna
+namespace MediaBrowser.Model.Dlna
 {
     public class CodecProfile
     {
@@ -22,7 +22,7 @@ namespace MediaBrowser.Controller.Dlna
 
         public List<string> GetCodecs()
         {
-            return (Codec ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+            return (Codec ?? string.Empty).Split(',').Where(i => !string.IsNullOrEmpty(i)).ToList();
         }
 
         public bool ContainsCodec(string codec)
@@ -73,7 +73,6 @@ namespace MediaBrowser.Controller.Dlna
         AudioChannels,
         AudioBitrate,
         AudioProfile,
-        Filesize,
         Width,
         Height,
         Has64BitOffsets,

+ 2 - 2
MediaBrowser.Controller/Dlna/ContainerProfile.cs → MediaBrowser.Model/Dlna/ContainerProfile.cs

@@ -2,7 +2,7 @@
 using System.Linq;
 using System.Xml.Serialization;
 
-namespace MediaBrowser.Controller.Dlna
+namespace MediaBrowser.Model.Dlna
 {
     public class ContainerProfile
     {
@@ -20,7 +20,7 @@ namespace MediaBrowser.Controller.Dlna
 
         public List<string> GetContainers()
         {
-            return (Container ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+            return (Container ?? string.Empty).Split(',').Where(i => !string.IsNullOrEmpty(i)).ToList();
         }
     }
 }

+ 2 - 3
MediaBrowser.Controller/Dlna/DeviceIdentification.cs → MediaBrowser.Model/Dlna/DeviceIdentification.cs

@@ -1,7 +1,6 @@
-
-using System.Xml.Serialization;
+using System.Xml.Serialization;
 
-namespace MediaBrowser.Controller.Dlna
+namespace MediaBrowser.Model.Dlna
 {
     public class DeviceIdentification
     {

+ 3 - 4
MediaBrowser.Controller/Dlna/DeviceProfile.cs → MediaBrowser.Model/Dlna/DeviceProfile.cs

@@ -1,11 +1,10 @@
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Entities;
 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Xml.Serialization;
 
-namespace MediaBrowser.Controller.Dlna
+namespace MediaBrowser.Model.Dlna
 {
     [XmlRoot("Profile")]
     public class DeviceProfile
@@ -89,7 +88,7 @@ namespace MediaBrowser.Controller.Dlna
 
         public List<string> GetSupportedMediaTypes()
         {
-            return (SupportedMediaTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+            return (SupportedMediaTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrEmpty(i)).ToList();
         }
 
         public TranscodingProfile GetAudioTranscodingProfile(string container, string audioCodec)

+ 4 - 4
MediaBrowser.Controller/Dlna/DirectPlayProfile.cs → MediaBrowser.Model/Dlna/DirectPlayProfile.cs

@@ -2,7 +2,7 @@
 using System.Linq;
 using System.Xml.Serialization;
 
-namespace MediaBrowser.Controller.Dlna
+namespace MediaBrowser.Model.Dlna
 {
     public class DirectPlayProfile
     {
@@ -20,17 +20,17 @@ namespace MediaBrowser.Controller.Dlna
 
         public List<string> GetContainers()
         {
-            return (Container ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+            return (Container ?? string.Empty).Split(',').Where(i => !string.IsNullOrEmpty(i)).ToList();
         }
 
         public List<string> GetAudioCodecs()
         {
-            return (AudioCodec ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+            return (AudioCodec ?? string.Empty).Split(',').Where(i => !string.IsNullOrEmpty(i)).ToList();
         }
 
         public List<string> GetVideoCodecs()
         {
-            return (VideoCodec ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+            return (VideoCodec ?? string.Empty).Split(',').Where(i => !string.IsNullOrEmpty(i)).ToList();
         }
     }
 

+ 4 - 4
MediaBrowser.Controller/Dlna/ResponseProfile.cs → MediaBrowser.Model/Dlna/ResponseProfile.cs

@@ -2,7 +2,7 @@
 using System.Linq;
 using System.Xml.Serialization;
 
-namespace MediaBrowser.Controller.Dlna
+namespace MediaBrowser.Model.Dlna
 {
     public class ResponseProfile
     {
@@ -33,17 +33,17 @@ namespace MediaBrowser.Controller.Dlna
 
         public List<string> GetContainers()
         {
-            return (Container ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+            return (Container ?? string.Empty).Split(',').Where(i => !string.IsNullOrEmpty(i)).ToList();
         }
         
         public List<string> GetAudioCodecs()
         {
-            return (AudioCodec ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+            return (AudioCodec ?? string.Empty).Split(',').Where(i => !string.IsNullOrEmpty(i)).ToList();
         }
 
         public List<string> GetVideoCodecs()
         {
-            return (VideoCodec ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+            return (VideoCodec ?? string.Empty).Split(',').Where(i => !string.IsNullOrEmpty(i)).ToList();
         }
     }
 }

+ 533 - 0
MediaBrowser.Model/Dlna/StreamBuilder.cs

@@ -0,0 +1,533 @@
+using System;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using MediaBrowser.Model.Dto;
+using System.Collections.Generic;
+using MediaBrowser.Model.Entities;
+
+namespace MediaBrowser.Model.Dlna
+{
+    public class StreamBuilder
+    {
+        private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+
+        public StreamInfo BuildAudioItem(AudioOptions options)
+        {
+            ValidateAudioInput(options);
+            
+            var mediaSources = options.MediaSources;
+
+            // If the client wants a specific media soure, filter now
+            if (!string.IsNullOrEmpty(options.MediaSourceId))
+            {
+                // Avoid implicitly captured closure
+                var mediaSourceId = options.MediaSourceId;
+
+                mediaSources = mediaSources
+                    .Where(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase))
+                    .ToList();
+            }
+
+            var streams = mediaSources.Select(i => BuildAudioItem(options.ItemId, i, options.Profile)).ToList();
+
+            foreach (var stream in streams)
+            {
+                stream.DeviceId = options.DeviceId;
+                stream.DeviceProfileId = options.Profile.Id;
+            }
+
+            return GetOptimalStream(streams);
+        }
+
+        public StreamInfo BuildVideoItem(VideoOptions options)
+        {
+            ValidateInput(options);
+
+            var mediaSources = options.MediaSources;
+
+            // If the client wants a specific media soure, filter now
+            if (!string.IsNullOrEmpty(options.MediaSourceId))
+            {
+                // Avoid implicitly captured closure
+                var mediaSourceId = options.MediaSourceId;
+
+                mediaSources = mediaSources
+                    .Where(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase))
+                    .ToList();
+            }
+
+            var streams = mediaSources.Select(i => BuildVideoItem(i, options)).ToList();
+
+            foreach (var stream in streams)
+            {
+                stream.DeviceId = options.DeviceId;
+                stream.DeviceProfileId = options.Profile.Id;
+            }
+
+            return GetOptimalStream(streams);
+        }
+
+        private StreamInfo GetOptimalStream(List<StreamInfo> streams)
+        {
+            // Grab the first one that can be direct streamed
+            // If that doesn't produce anything, just take the first
+            return streams.FirstOrDefault(i => i.IsDirectStream) ??
+                streams.FirstOrDefault();
+        }
+
+        private StreamInfo BuildAudioItem(string itemId, MediaSourceInfo item, DeviceProfile profile)
+        {
+            var playlistItem = new StreamInfo
+            {
+                ItemId = itemId,
+                MediaType = DlnaProfileType.Audio,
+                MediaSourceId = item.Id
+            };
+
+            var audioStream = item.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
+
+            var directPlay = profile.DirectPlayProfiles
+                .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsAudioProfileSupported(i, item, audioStream));
+
+            if (directPlay != null)
+            {
+                var audioCodec = audioStream == null ? null : audioStream.Codec;
+
+                // Make sure audio codec profiles are satisfied
+                if (!string.IsNullOrEmpty(audioCodec) && profile.CodecProfiles.Where(i => i.Type == CodecType.Audio && i.ContainsCodec(audioCodec))
+                    .All(i => AreConditionsSatisfied(i.Conditions, item.Path, null, audioStream)))
+                {
+                    playlistItem.IsDirectStream = true;
+                    playlistItem.Container = item.Container;
+
+                    return playlistItem;
+                }
+            }
+
+            var transcodingProfile = profile.TranscodingProfiles
+                .FirstOrDefault(i => i.Type == playlistItem.MediaType);
+
+            if (transcodingProfile != null)
+            {
+                playlistItem.IsDirectStream = false;
+                playlistItem.Container = transcodingProfile.Container;
+                playlistItem.AudioCodec = transcodingProfile.AudioCodec;
+
+                var audioTranscodingConditions = profile.CodecProfiles
+                    .Where(i => i.Type == CodecType.Audio && i.ContainsCodec(transcodingProfile.AudioCodec))
+                    .Take(1)
+                    .SelectMany(i => i.Conditions);
+
+                ApplyTranscodingConditions(playlistItem, audioTranscodingConditions);
+            }
+
+            return playlistItem;
+        }
+
+        private StreamInfo BuildVideoItem(MediaSourceInfo item, VideoOptions options)
+        {
+            var playlistItem = new StreamInfo
+            {
+                ItemId = options.ItemId,
+                MediaType = DlnaProfileType.Video,
+                MediaSourceId = item.Id
+            };
+
+            var audioStream = item.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
+            var videoStream = item.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
+
+            if (IsEligibleForDirectPlay(item, options))
+            {
+                // See if it can be direct played
+                var directPlay = options.Profile.DirectPlayProfiles
+                    .FirstOrDefault(i => i.Type == playlistItem.MediaType && IsVideoProfileSupported(i, item, videoStream, audioStream));
+
+                if (directPlay != null)
+                {
+                    var videoCodec = videoStream == null ? null : videoStream.Codec;
+
+                    // Make sure video codec profiles are satisfied
+                    if (!string.IsNullOrEmpty(videoCodec) && options.Profile.CodecProfiles.Where(i => i.Type == CodecType.Video && i.ContainsCodec(videoCodec))
+                        .All(i => AreConditionsSatisfied(i.Conditions, item.Path, videoStream, audioStream)))
+                    {
+                        var audioCodec = audioStream == null ? null : audioStream.Codec;
+
+                        // Make sure audio codec profiles are satisfied
+                        if (string.IsNullOrEmpty(audioCodec) || options.Profile.CodecProfiles.Where(i => i.Type == CodecType.VideoAudio && i.ContainsCodec(audioCodec))
+                            .All(i => AreConditionsSatisfied(i.Conditions, item.Path, videoStream, audioStream)))
+                        {
+                            playlistItem.IsDirectStream = true;
+                            playlistItem.Container = item.Container;
+
+                            return playlistItem;
+                        }
+                    }
+                }
+            }
+
+            // Can't direct play, find the transcoding profile
+            var transcodingProfile = options.Profile.TranscodingProfiles
+                .FirstOrDefault(i => i.Type == playlistItem.MediaType);
+
+            if (transcodingProfile != null)
+            {
+                playlistItem.IsDirectStream = false;
+                playlistItem.Container = transcodingProfile.Container;
+                playlistItem.AudioCodec = transcodingProfile.AudioCodec.Split(',').FirstOrDefault();
+                playlistItem.VideoCodec = transcodingProfile.VideoCodec;
+
+                var videoTranscodingConditions = options.Profile.CodecProfiles
+                    .Where(i => i.Type == CodecType.Video && i.ContainsCodec(transcodingProfile.VideoCodec))
+                    .Take(1)
+                    .SelectMany(i => i.Conditions);
+
+                ApplyTranscodingConditions(playlistItem, videoTranscodingConditions);
+
+                var audioTranscodingConditions = options.Profile.CodecProfiles
+                    .Where(i => i.Type == CodecType.VideoAudio && i.ContainsCodec(transcodingProfile.AudioCodec))
+                    .Take(1)
+                    .SelectMany(i => i.Conditions);
+
+                ApplyTranscodingConditions(playlistItem, audioTranscodingConditions);
+            }
+
+            return playlistItem;
+        }
+
+        private bool IsEligibleForDirectPlay(MediaSourceInfo item, VideoOptions options)
+        {
+            if (options.SubtitleStreamIndex.HasValue)
+            {
+                return false;
+            }
+
+            if (options.AudioStreamIndex.HasValue && 
+                item.MediaStreams.Count(i => i.Type == MediaStreamType.Audio) > 1)
+            {
+                return false;
+            }
+
+            return true;
+        }
+
+        private void ValidateInput(VideoOptions options)
+        {
+            ValidateAudioInput(options);
+
+            if (options.AudioStreamIndex.HasValue && string.IsNullOrEmpty(options.MediaSourceId))
+            {
+                throw new ArgumentException("MediaSourceId is required when a specific audio stream is requested");
+            }
+
+            if (options.SubtitleStreamIndex.HasValue && string.IsNullOrEmpty(options.MediaSourceId))
+            {
+                throw new ArgumentException("MediaSourceId is required when a specific subtitle stream is requested");
+            }
+        }
+
+        private void ValidateAudioInput(AudioOptions options)
+        {
+            if (string.IsNullOrEmpty(options.ItemId))
+            {
+                throw new ArgumentException("ItemId is required");
+            }
+            if (string.IsNullOrEmpty(options.DeviceId))
+            {
+                throw new ArgumentException("DeviceId is required");
+            }
+            if (options.Profile == null)
+            {
+                throw new ArgumentException("Profile is required");
+            }
+            if (options.MediaSources == null)
+            {
+                throw new ArgumentException("MediaSources is required");
+            }
+        }
+
+        private void ApplyTranscodingConditions(StreamInfo item, IEnumerable<ProfileCondition> conditions)
+        {
+            foreach (var condition in conditions
+                .Where(i => !string.IsNullOrEmpty(i.Value)))
+            {
+                var value = condition.Value;
+
+                switch (condition.Property)
+                {
+                    case ProfileConditionValue.AudioBitrate:
+                        {
+                            int num;
+                            if (int.TryParse(value, NumberStyles.Any, _usCulture, out num))
+                            {
+                                item.AudioBitrate = num;
+                            }
+                            break;
+                        }
+                    case ProfileConditionValue.AudioChannels:
+                        {
+                            int num;
+                            if (int.TryParse(value, NumberStyles.Any, _usCulture, out num))
+                            {
+                                item.MaxAudioChannels = num;
+                            }
+                            break;
+                        }
+                    case ProfileConditionValue.AudioProfile:
+                    case ProfileConditionValue.Has64BitOffsets:
+                    case ProfileConditionValue.VideoBitDepth:
+                    case ProfileConditionValue.VideoProfile:
+                        {
+                            // Not supported yet
+                            break;
+                        }
+                    case ProfileConditionValue.Height:
+                        {
+                            int num;
+                            if (int.TryParse(value, NumberStyles.Any, _usCulture, out num))
+                            {
+                                item.MaxHeight = num;
+                            }
+                            break;
+                        }
+                    case ProfileConditionValue.VideoBitrate:
+                        {
+                            int num;
+                            if (int.TryParse(value, NumberStyles.Any, _usCulture, out num))
+                            {
+                                item.VideoBitrate = num;
+                            }
+                            break;
+                        }
+                    case ProfileConditionValue.VideoFramerate:
+                        {
+                            int num;
+                            if (int.TryParse(value, NumberStyles.Any, _usCulture, out num))
+                            {
+                                item.MaxFramerate = num;
+                            }
+                            break;
+                        }
+                    case ProfileConditionValue.VideoLevel:
+                        {
+                            int num;
+                            if (int.TryParse(value, NumberStyles.Any, _usCulture, out num))
+                            {
+                                item.VideoLevel = num;
+                            }
+                            break;
+                        }
+                    case ProfileConditionValue.Width:
+                        {
+                            int num;
+                            if (int.TryParse(value, NumberStyles.Any, _usCulture, out num))
+                            {
+                                item.MaxWidth = num;
+                            }
+                            break;
+                        }
+                    default:
+                        throw new ArgumentException("Unrecognized ProfileConditionValue");
+                }
+            }
+        }
+
+        private bool IsAudioProfileSupported(DirectPlayProfile profile, MediaSourceInfo item, MediaStream audioStream)
+        {
+            if (profile.Container.Length > 0)
+            {
+                // Check container type
+                var mediaContainer = item.Container ?? string.Empty;
+                if (!profile.GetContainers().Any(i => string.Equals(i, mediaContainer, StringComparison.OrdinalIgnoreCase)))
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        private bool IsVideoProfileSupported(DirectPlayProfile profile, MediaSourceInfo item, MediaStream videoStream, MediaStream audioStream)
+        {
+            // Only plain video files can be direct played
+            if (item.VideoType != VideoType.VideoFile)
+            {
+                return false;
+            }
+
+            if (profile.Container.Length > 0)
+            {
+                // Check container type
+                var mediaContainer = item.Container ?? string.Empty;
+                if (!profile.GetContainers().Any(i => string.Equals(i, mediaContainer, StringComparison.OrdinalIgnoreCase)))
+                {
+                    return false;
+                }
+            }
+
+            // Check video codec
+            var videoCodecs = profile.GetVideoCodecs();
+            if (videoCodecs.Count > 0)
+            {
+                var videoCodec = videoStream == null ? null : videoStream.Codec;
+                if (string.IsNullOrEmpty(videoCodec) || !videoCodecs.Contains(videoCodec, StringComparer.OrdinalIgnoreCase))
+                {
+                    return false;
+                }
+            }
+
+            var audioCodecs = profile.GetAudioCodecs();
+            if (audioCodecs.Count > 0)
+            {
+                // Check audio codecs
+                var audioCodec = audioStream == null ? null : audioStream.Codec;
+                if (string.IsNullOrEmpty(audioCodec) || !audioCodecs.Contains(audioCodec, StringComparer.OrdinalIgnoreCase))
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        private bool AreConditionsSatisfied(IEnumerable<ProfileCondition> conditions, string mediaPath, MediaStream videoStream, MediaStream audioStream)
+        {
+            return conditions.All(i => IsConditionSatisfied(i, mediaPath, videoStream, audioStream));
+        }
+
+        /// <summary>
+        /// Determines whether [is condition satisfied] [the specified condition].
+        /// </summary>
+        /// <param name="condition">The condition.</param>
+        /// <param name="mediaPath">The media path.</param>
+        /// <param name="videoStream">The video stream.</param>
+        /// <param name="audioStream">The audio stream.</param>
+        /// <returns><c>true</c> if [is condition satisfied] [the specified condition]; otherwise, <c>false</c>.</returns>
+        /// <exception cref="System.InvalidOperationException">Unexpected ProfileConditionType</exception>
+        private bool IsConditionSatisfied(ProfileCondition condition, string mediaPath, MediaStream videoStream, MediaStream audioStream)
+        {
+            if (condition.Property == ProfileConditionValue.Has64BitOffsets)
+            {
+                // TODO: Determine how to evaluate this
+            }
+
+            if (condition.Property == ProfileConditionValue.VideoProfile)
+            {
+                var profile = videoStream == null ? null : videoStream.Profile;
+
+                if (!string.IsNullOrEmpty(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 if (condition.Property == ProfileConditionValue.AudioProfile)
+            {
+                var profile = audioStream == null ? null : audioStream.Profile;
+
+                if (!string.IsNullOrEmpty(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>
+        /// Gets the condition value.
+        /// </summary>
+        /// <param name="condition">The condition.</param>
+        /// <param name="mediaPath">The media path.</param>
+        /// <param name="videoStream">The video stream.</param>
+        /// <param name="audioStream">The audio stream.</param>
+        /// <returns>System.Nullable{System.Int64}.</returns>
+        /// <exception cref="System.InvalidOperationException">Unexpected Property</exception>
+        private long? GetConditionValue(ProfileCondition condition, string mediaPath, MediaStream videoStream, MediaStream audioStream)
+        {
+            switch (condition.Property)
+            {
+                case ProfileConditionValue.AudioBitrate:
+                    return audioStream == null ? null : audioStream.BitRate;
+                case ProfileConditionValue.AudioChannels:
+                    return audioStream == null ? null : audioStream.Channels;
+                case ProfileConditionValue.VideoBitrate:
+                    return videoStream == null ? null : videoStream.BitRate;
+                case ProfileConditionValue.VideoFramerate:
+                    return videoStream == null ? null : (ConvertToLong(videoStream.AverageFrameRate ?? videoStream.RealFrameRate));
+                case ProfileConditionValue.Height:
+                    return videoStream == null ? null : videoStream.Height;
+                case ProfileConditionValue.Width:
+                    return videoStream == null ? null : videoStream.Width;
+                case ProfileConditionValue.VideoLevel:
+                    return videoStream == null ? null : ConvertToLong(videoStream.Level);
+                default:
+                    throw new InvalidOperationException("Unexpected Property");
+            }
+        }
+
+        /// <summary>
+        /// Converts to long.
+        /// </summary>
+        /// <param name="val">The value.</param>
+        /// <returns>System.Nullable{System.Int64}.</returns>
+        private long? ConvertToLong(float? val)
+        {
+            return val.HasValue ? Convert.ToInt64(val.Value) : (long?)null;
+        }
+
+        /// <summary>
+        /// Converts to long.
+        /// </summary>
+        /// <param name="val">The value.</param>
+        /// <returns>System.Nullable{System.Int64}.</returns>
+        private long? ConvertToLong(double? val)
+        {
+            return val.HasValue ? Convert.ToInt64(val.Value) : (long?)null;
+        }
+    }
+
+}

+ 124 - 0
MediaBrowser.Model/Dlna/StreamInfo.cs

@@ -0,0 +1,124 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using MediaBrowser.Model.Dto;
+
+namespace MediaBrowser.Model.Dlna
+{
+    /// <summary>
+    /// Class StreamInfo.
+    /// </summary>
+    public class StreamInfo
+    {
+        public string ItemId { get; set; }
+
+        public string MediaSourceId { get; set; }
+
+        public bool IsDirectStream { get; set; }
+
+        public DlnaProfileType MediaType { get; set; }
+
+        public string Container { get; set; }
+
+        public long StartPositionTicks { get; set; }
+
+        public string VideoCodec { get; set; }
+
+        public string AudioCodec { get; set; }
+
+        public int? AudioStreamIndex { get; set; }
+
+        public int? SubtitleStreamIndex { get; set; }
+
+        public int? MaxAudioChannels { get; set; }
+
+        public int? AudioBitrate { get; set; }
+
+        public int? VideoBitrate { get; set; }
+
+        public int? VideoLevel { get; set; }
+
+        public int? MaxWidth { get; set; }
+        public int? MaxHeight { get; set; }
+
+        public int? MaxFramerate { get; set; }
+
+        public string DeviceProfileId { get; set; }
+        public string DeviceId { get; set; }
+
+        public string ToUrl(string baseUrl)
+        {
+            return ToDlnaUrl(baseUrl);
+        }
+
+        public string ToDlnaUrl(string baseUrl)
+        {
+            if (string.IsNullOrEmpty(baseUrl))
+            {
+                throw new ArgumentNullException(baseUrl);
+            }
+
+            var dlnaCommand = BuildDlnaParam(this);
+
+            var extension = string.IsNullOrEmpty(Container) ? string.Empty : "." + Container;
+
+            baseUrl = baseUrl.TrimEnd('/');
+
+            if (MediaType == DlnaProfileType.Audio)
+            {
+                return string.Format("{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, dlnaCommand);
+            }
+            return string.Format("{0}/videos/{1}/stream{2}?{3}", baseUrl, ItemId, extension, dlnaCommand);
+        }
+
+        private static string BuildDlnaParam(StreamInfo item)
+        {
+            var usCulture = new CultureInfo("en-US");
+
+            var list = new List<string>
+            {
+                item.DeviceProfileId ?? string.Empty,
+                item.DeviceId ?? string.Empty,
+                item.MediaSourceId ?? string.Empty,
+                (item.IsDirectStream).ToString().ToLower(),
+                item.VideoCodec ?? string.Empty,
+                item.AudioCodec ?? string.Empty,
+                item.AudioStreamIndex.HasValue ? item.AudioStreamIndex.Value.ToString(usCulture) : string.Empty,
+                item.SubtitleStreamIndex.HasValue ? item.SubtitleStreamIndex.Value.ToString(usCulture) : string.Empty,
+                item.VideoBitrate.HasValue ? item.VideoBitrate.Value.ToString(usCulture) : string.Empty,
+                item.AudioBitrate.HasValue ? item.AudioBitrate.Value.ToString(usCulture) : string.Empty,
+                item.MaxAudioChannels.HasValue ? item.MaxAudioChannels.Value.ToString(usCulture) : string.Empty,
+                item.MaxFramerate.HasValue ? item.MaxFramerate.Value.ToString(usCulture) : string.Empty,
+                item.MaxWidth.HasValue ? item.MaxWidth.Value.ToString(usCulture) : string.Empty,
+                item.MaxHeight.HasValue ? item.MaxHeight.Value.ToString(usCulture) : string.Empty,
+                item.StartPositionTicks.ToString(usCulture),
+                item.VideoLevel.HasValue ? item.VideoLevel.Value.ToString(usCulture) : string.Empty
+            };
+
+            return string.Format("Params={0}", string.Join(";", list.ToArray()));
+        }
+
+    }
+
+    /// <summary>
+    /// Class AudioOptions.
+    /// </summary>
+    public class AudioOptions
+    {
+        public string ItemId { get; set; }
+        public List<MediaSourceInfo> MediaSources { get; set; }
+        public int? MaxBitrateSetting { get; set; }
+        public DeviceProfile Profile { get; set; }
+        public string MediaSourceId { get; set; }
+        public string DeviceId { get; set; }
+    }
+
+    /// <summary>
+    /// Class VideoOptions.
+    /// </summary>
+    public class VideoOptions : AudioOptions
+    {
+        public int? AudioStreamIndex { get; set; }
+        public int? SubtitleStreamIndex { get; set; }
+    }
+}

+ 2 - 2
MediaBrowser.Controller/Dlna/TranscodingProfile.cs → MediaBrowser.Model/Dlna/TranscodingProfile.cs

@@ -2,7 +2,7 @@
 using System.Linq;
 using System.Xml.Serialization;
 
-namespace MediaBrowser.Controller.Dlna
+namespace MediaBrowser.Model.Dlna
 {
     public class TranscodingProfile
     {
@@ -35,7 +35,7 @@ namespace MediaBrowser.Controller.Dlna
 
         public List<string> GetAudioCodecs()
         {
-            return (AudioCodec ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+            return (AudioCodec ?? string.Empty).Split(',').Where(i => !string.IsNullOrEmpty(i)).ToList();
         }
     }
 

+ 2 - 0
MediaBrowser.Model/Dto/MediaVersionInfo.cs

@@ -9,6 +9,8 @@ namespace MediaBrowser.Model.Dto
 
         public string Path { get; set; }
 
+        public string Container { get; set; }
+
         public LocationType LocationType { get; set; }
         
         public string Name { get; set; }

+ 0 - 20
MediaBrowser.Model/Extensions/ModelExtensions.cs

@@ -1,20 +0,0 @@
-
-namespace MediaBrowser.Model.Extensions
-{
-    /// <summary>
-    /// Class ModelExtensions
-    /// </summary>
-    static class ModelExtensions
-    {
-        /// <summary>
-        /// Values the or default.
-        /// </summary>
-        /// <param name="str">The STR.</param>
-        /// <param name="def">The def.</param>
-        /// <returns>System.String.</returns>
-        internal static string ValueOrDefault(this string str, string def = "")
-        {
-            return string.IsNullOrEmpty(str) ? def : str;
-        }
-    }
-}

+ 10 - 1
MediaBrowser.Model/MediaBrowser.Model.csproj

@@ -66,7 +66,16 @@
     <Compile Include="Configuration\MetadataPlugin.cs" />
     <Compile Include="Configuration\MetadataOptions.cs" />
     <Compile Include="Configuration\ServerConfiguration.cs" />
+    <Compile Include="Dlna\CodecProfile.cs" />
+    <Compile Include="Dlna\ContainerProfile.cs" />
+    <Compile Include="Dlna\DeviceIdentification.cs" />
+    <Compile Include="Dlna\DeviceProfile.cs" />
     <Compile Include="Dlna\DeviceProfileInfo.cs" />
+    <Compile Include="Dlna\DirectPlayProfile.cs" />
+    <Compile Include="Dlna\ResponseProfile.cs" />
+    <Compile Include="Dlna\StreamBuilder.cs" />
+    <Compile Include="Dlna\StreamInfo.cs" />
+    <Compile Include="Dlna\TranscodingProfile.cs" />
     <Compile Include="Drawing\ImageOutputFormat.cs" />
     <Compile Include="Dto\BaseItemPerson.cs" />
     <Compile Include="Dto\ChapterInfoDto.cs" />
@@ -155,7 +164,6 @@
     <Compile Include="Entities\ParentalRating.cs" />
     <Compile Include="Dto\StreamOptions.cs" />
     <Compile Include="Entities\VirtualFolderInfo.cs" />
-    <Compile Include="Extensions\ModelExtensions.cs" />
     <Compile Include="IO\IZipClient.cs" />
     <Compile Include="Logging\ILogger.cs" />
     <Compile Include="Logging\LogSeverity.cs" />
@@ -223,6 +231,7 @@
       <HintPath>..\packages\PropertyChanged.Fody.1.41.0.0\Lib\NET35\PropertyChanged.dll</HintPath>
       <Private>False</Private>
     </Reference>
+    <Reference Include="System.XML" />
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <PropertyGroup>

+ 13 - 3
MediaBrowser.Model/Updates/PackageVersionInfo.cs

@@ -1,5 +1,4 @@
-using MediaBrowser.Model.Extensions;
-using System;
+using System;
 using System.Runtime.Serialization;
 
 namespace MediaBrowser.Model.Updates
@@ -39,7 +38,18 @@ namespace MediaBrowser.Model.Updates
         [IgnoreDataMember]
         public Version version
         {
-            get { return _version ?? (_version = new Version(versionStr.ValueOrDefault("0.0.0.1"))); }
+            get { return _version ?? (_version = new Version(ValueOrDefault(versionStr, "0.0.0.1"))); }
+        }
+
+        /// <summary>
+        /// Values the or default.
+        /// </summary>
+        /// <param name="str">The STR.</param>
+        /// <param name="def">The def.</param>
+        /// <returns>System.String.</returns>
+        private static string ValueOrDefault(string str, string def = "")
+        {
+            return string.IsNullOrEmpty(str) ? def : str;
         }
 
         /// <summary>

+ 2 - 1
MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs

@@ -34,7 +34,8 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
                 {
                     var collectionType = args.GetCollectionType();
 
-                    if (string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase))
+                    if (string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase) ||
+                        string.IsNullOrWhiteSpace(collectionType))
                     {
                         return new Controller.Entities.Audio.Audio();
                     }

+ 1 - 31
MediaBrowser.Server.Implementations/Localization/JavaScript/de.json

@@ -1,31 +1 @@
-{
-	"SettingsSaved": "Einstellungen gespeichert",
-	"AddUser": "Benutzer hinzuf\u00fcgen",
-	"Users": "Benutzer",
-	"Delete": "L\u00f6schen",
-	"Administrator": "Administrator",
-	"Password": "Passwort",
-	"CreatePassword": "Passwort erstellen",
-	"DeleteImage": "Bild l\u00f6schen",
-	"DeleteImageConfirmation": "M\u00f6chten Sie das Bild wirklich l\u00f6schen?",
-	"FileReadCancelled": "Das Einlesen der Datei wurde abgebrochen.",
-	"FileNotFound": "Datei nicht gefunden",
-	"FileReadError": "Beim Lesen der Datei ist ein Fehler aufgetreten.",
-	"DeleteUser": "Benutzer l\u00f6schen",
-	"DeleteUserConfirmation": "M\u00f6chten Sie {0} wirklich l\u00f6schen?",
-	"PasswordResetHeader": "Passwort zur\u00fccksetzen",
-	"PasswordResetComplete": "Das Passwort wurde zur\u00fcckgesetzt.",
-	"PasswordResetConfirmation": "M\u00f6chten Sie das Passwort wirklich zur\u00fccksetzen?",
-	"PasswordSaved": "Passwort gespeichert",
-	"PasswordMatchError": "Passwort und Passwortbest\u00e4tigung stimmen nicht \u00fcberein.",
-	"OptionOff": "Aus",
-	"OptionOn": "Ein",
-	"OptionRelease": "Release",
-	"OptionBeta": "Beta",
-	"OptionDev": "Dev",
-	"UninstallPluginHeader": "Deinstalliere Plugin",
-	"UninstallPluginConfirmation": "M\u00f6chten Sie {0} wirklich deinstallieren?",
-	"NoPluginConfigurationMessage": "Bei diesem Plugin kann nichts eingestellt werden.",
-	"NoPluginsInstalledMessage": "Sie haben keine Plugins installiert.",
-	"BrowsePluginCatalogMessage": "Durchsuchen Sie unsere Bibliothek um alle verf\u00fcgbaren Plugins anzuzeigen."
-}
+{"SettingsSaved":"Einstellungen gespeichert","AddUser":"Benutzer hinzuf\u00fcgen","Users":"Benutzer","Delete":"L\u00f6schen","Administrator":"Administrator","Password":"Passwort","DeleteImage":"Bild l\u00f6schen","DeleteImageConfirmation":"M\u00f6chten Sie das Bild wirklich l\u00f6schen?","FileReadCancelled":"Das Einlesen der Datei wurde abgebrochen.","FileNotFound":"Datei nicht gefunden","FileReadError":"Beim Lesen der Datei ist ein Fehler aufgetreten.","DeleteUser":"Benutzer l\u00f6schen","DeleteUserConfirmation":"M\u00f6chten Sie {0} wirklich l\u00f6schen?","PasswordResetHeader":"Passwort zur\u00fccksetzen","PasswordResetComplete":"Das Passwort wurde zur\u00fcckgesetzt.","PasswordResetConfirmation":"M\u00f6chten Sie das Passwort wirklich zur\u00fccksetzen?","PasswordSaved":"Passwort gespeichert","PasswordMatchError":"Passwort und Passwortbest\u00e4tigung stimmen nicht \u00fcberein.","OptionOff":"Aus","OptionOn":"Ein","OptionRelease":"Release","OptionBeta":"Beta","OptionDev":"Dev","UninstallPluginHeader":"Deinstalliere Plugin","UninstallPluginConfirmation":"M\u00f6chten Sie {0} wirklich deinstallieren?","NoPluginConfigurationMessage":"Bei diesem Plugin kann nichts eingestellt werden.","NoPluginsInstalledMessage":"Sie haben keine Plugins installiert.","BrowsePluginCatalogMessage":"Durchsuchen Sie unsere Bibliothek um alle verf\u00fcgbaren Plugins anzuzeigen."}

+ 1 - 31
MediaBrowser.Server.Implementations/Localization/JavaScript/en_US.json

@@ -1,31 +1 @@
-{
-	"SettingsSaved": "Settings saved.",
-	"AddUser": "Add User",
-	"Users": "Users",
-	"Delete": "Delete",
-	"Administrator": "Administrator",
-	"Password": "Password",
-	"CreatePassword": "Create Password",
-	"DeleteImage": "Delete Image",
-	"DeleteImageConfirmation": "Are you sure you wish to delete this image?",
-	"FileReadCancelled": "The file read has been cancelled.",
-	"FileNotFound": "File not found.",
-	"FileReadError": "An error occurred while reading the file.",
-	"DeleteUser": "Delete User",
-	"DeleteUserConfirmation": "Are you sure you wish to delete {0}?",
-	"PasswordResetHeader": "Password Reset",
-	"PasswordResetComplete": "The password has been reset.",
-	"PasswordResetConfirmation": "Are you sure you wish to reset the password?",
-	"PasswordSaved": "Password saved.",
-	"PasswordMatchError": "Password and password confirmation must match.",
-	"OptionOff": "Off",
-	"OptionOn": "On",
-	"OptionRelease": "Release",
-	"OptionBeta": "Beta",
-	"OptionDev": "Dev",
-	"UninstallPluginHeader": "Uninstall Plugin",
-	"UninstallPluginConfirmation": "Are you sure you wish to uninstall {0}?",
-	"NoPluginConfigurationMessage": "This plugin has nothing to configure.",
-	"NoPluginsInstalledMessage": "You have no plugins installed.",
-	"BrowsePluginCatalogMessage": "Browse our plugin catalog to view available plugins."
-}
+{"SettingsSaved":"Settings saved.","AddUser":"Add User","Users":"Users","Delete":"Delete","Administrator":"Administrator","Password":"Password","DeleteImage":"Delete Image","DeleteImageConfirmation":"Are you sure you wish to delete this image?","FileReadCancelled":"The file read has been cancelled.","FileNotFound":"File not found.","FileReadError":"An error occurred while reading the file.","DeleteUser":"Delete User","DeleteUserConfirmation":"Are you sure you wish to delete {0}?","PasswordResetHeader":"Password Reset","PasswordResetComplete":"The password has been reset.","PasswordResetConfirmation":"Are you sure you wish to reset the password?","PasswordSaved":"Password saved.","PasswordMatchError":"Password and password confirmation must match.","OptionOff":"Off","OptionOn":"On","OptionRelease":"Release","OptionBeta":"Beta","OptionDev":"Dev","UninstallPluginHeader":"Uninstall Plugin","UninstallPluginConfirmation":"Are you sure you wish to uninstall {0}?","NoPluginConfigurationMessage":"This plugin has nothing to configure.","NoPluginsInstalledMessage":"You have no plugins installed.","BrowsePluginCatalogMessage":"Browse our plugin catalog to view available plugins."}

+ 1 - 31
MediaBrowser.Server.Implementations/Localization/JavaScript/es.json

@@ -1,31 +1 @@
-{
-	"SettingsSaved": "Configuracion guardada",
-	"AddUser": "Agregar usuario",
-	"Users": "Usuarios",
-	"Delete": "Borrar",
-	"Administrator": "Administrador",
-	"Password": "Contrase\u00f1a",
-	"CreatePassword": "Crear Contrase\u00f1a",
-	"DeleteImage": "Borrar Imagen",
-	"DeleteImageConfirmation": "Esta seguro que desea borrar esta imagen?",
-	"FileReadCancelled": "La lectura del archivo se ha cancelado.",
-	"FileNotFound": "Archivo no encontrado.",
-	"FileReadError": "Se encontr\u00f3 un error al leer el archivo.",
-	"DeleteUser": "Borrar Usuario",
-	"DeleteUserConfirmation": "Esta seguro que desea eliminar a {0}?",
-	"PasswordResetHeader": "Restablecer contrase\u00f1a",
-	"PasswordResetComplete": "La contrase\u00f1a se ha restablecido.",
-	"PasswordResetConfirmation": "Esta seguro que desea restablecer la contrase\u00f1a?",
-	"PasswordSaved": "Contrase\u00f1a guardada.",
-	"PasswordMatchError": "La contrase\u00f1a y la confirmaci\u00f3n de la contrase\u00f1a deben de ser iguales.",
-	"OptionOff": "Apagado",
-	"OptionOn": "Prendido",
-	"OptionRelease": "Liberar",
-	"OptionBeta": "Beta",
-	"OptionDev": "Desarrollo",
-	"UninstallPluginHeader": "Desinstalar Plugin",
-	"UninstallPluginConfirmation": "Esta seguro que desea desinstalar {0}?",
-	"NoPluginConfigurationMessage": "El plugin no requiere configuraci\u00f3n",
-	"NoPluginsInstalledMessage": "No tiene plugins instalados.",
-	"BrowsePluginCatalogMessage": "Navegar el catalogo de plugins para ver los plugins disponibles."
-}
+{"SettingsSaved":"Configuracion guardada","AddUser":"Agregar usuario","Users":"Usuarios","Delete":"Borrar","Administrator":"Administrador","Password":"Contrase\u00f1a","DeleteImage":"Borrar Imagen","DeleteImageConfirmation":"Esta seguro que desea borrar esta imagen?","FileReadCancelled":"La lectura del archivo se ha cancelado.","FileNotFound":"Archivo no encontrado.","FileReadError":"Se encontr\u00f3 un error al leer el archivo.","DeleteUser":"Borrar Usuario","DeleteUserConfirmation":"Esta seguro que desea eliminar a {0}?","PasswordResetHeader":"Restablecer contrase\u00f1a","PasswordResetComplete":"La contrase\u00f1a se ha restablecido.","PasswordResetConfirmation":"Esta seguro que desea restablecer la contrase\u00f1a?","PasswordSaved":"Contrase\u00f1a guardada.","PasswordMatchError":"La contrase\u00f1a y la confirmaci\u00f3n de la contrase\u00f1a deben de ser iguales.","OptionOff":"Apagado","OptionOn":"Prendido","OptionRelease":"Liberar","OptionBeta":"Beta","OptionDev":"Desarrollo","UninstallPluginHeader":"Desinstalar Plugin","UninstallPluginConfirmation":"Esta seguro que desea desinstalar {0}?","NoPluginConfigurationMessage":"El plugin no requiere configuraci\u00f3n","NoPluginsInstalledMessage":"No tiene plugins instalados.","BrowsePluginCatalogMessage":"Navegar el catalogo de plugins para ver los plugins disponibles."}

+ 1 - 31
MediaBrowser.Server.Implementations/Localization/JavaScript/fr.json

@@ -1,31 +1 @@
-{
-	"SettingsSaved": "Param\u00e8tres sauvegard\u00e9s.",
-	"AddUser": "Ajout\u00e9 Usager",
-	"Users": "Usagers",
-	"Delete": "Supprimer",
-	"Administrator": "Administrateur",
-	"Password": "Mot de passe",
-	"CreatePassword": "Cr\u00e9er mot de passe",
-	"DeleteImage": "Supprimer Image",
-	"DeleteImageConfirmation": "\u00cates-vous s\u00fbr de vouloir supprimer l'image?",
-	"FileReadCancelled": "La lecture du fichier a \u00e9t\u00e9 annul\u00e9e.",
-	"FileNotFound": "Fichier non trouv\u00e9",
-	"FileReadError": "Un erreur est survenue pendant la lecture du fichier.",
-	"DeleteUser": "Supprimer Usager",
-	"DeleteUserConfirmation": "\u00cates-vous s\u00fbr de vouloir supprimer {0}?",
-	"PasswordResetHeader": "Red\u00e9marrage du mot de passe",
-	"PasswordResetComplete": "Le mot de passe a \u00e9t\u00e9 red\u00e9marr\u00e9.",
-	"PasswordResetConfirmation": "\u00cates-vous s\u00fbr de vouloir red\u00e9marrer le mot de passe?",
-	"PasswordSaved": "Mot de passe sauvegard\u00e9.",
-	"PasswordMatchError": "Mot de passe et confirmation de mot de passe doivent correspondre.",
-	"OptionOff": "Off",
-	"OptionOn": "On",
-	"OptionRelease": "Lancement",
-	"OptionBeta": "Beta",
-	"OptionDev": "Dev",
-	"UninstallPluginHeader": "D\u00e9sinstaller module d'extention",
-	"UninstallPluginConfirmation": "\u00cates-vous s\u00fbr de vouloir d\u00e9sinstaller {0}?",
-	"NoPluginConfigurationMessage": "Ce module d'extension n'a rien \u00e0 configurer.",
-	"NoPluginsInstalledMessage": "Vous n'avez aucun module d'extension install\u00e9.",
-	"BrowsePluginCatalogMessage": "Explorer notre catalogue de modules d'extension pour voir ce qui est disponible."
-}
+{"SettingsSaved":"Param\u00e8tres sauvegard\u00e9s.","AddUser":"Ajouter utilisateur","Users":"Utilisateur","Delete":"Supprimer","Administrator":"Administrateur","Password":"Mot de passe","DeleteImage":"Supprimer Image","DeleteImageConfirmation":"\u00cates-vous s\u00fbr de vouloir supprimer l'image?","FileReadCancelled":"La lecture du fichier a \u00e9t\u00e9 annul\u00e9e.","FileNotFound":"Fichier non trouv\u00e9","FileReadError":"Un erreur est survenue pendant la lecture du fichier.","DeleteUser":"Supprimer utilisateur","DeleteUserConfirmation":"\u00cates-vous s\u00fbr de vouloir supprimer {0}?","PasswordResetHeader":"R\u00e9initialisation du mot de passe","PasswordResetComplete":"Le mot de passe a \u00e9t\u00e9 r\u00e9initialis\u00e9.","PasswordResetConfirmation":"\u00cates-vous s\u00fbr de vouloir r\u00e9initialiser le mot de passe?","PasswordSaved":"Mot de passe sauvegard\u00e9.","PasswordMatchError":"Mot de passe et confirmation de mot de passe doivent correspondre.","OptionOff":"Off","OptionOn":"On","OptionRelease":"Lancement","OptionBeta":"Beta","OptionDev":"Dev","UninstallPluginHeader":"D\u00e9sinstaller Plug-in","UninstallPluginConfirmation":"\u00cates-vous s\u00fbr de vouloir d\u00e9sinstaller {0}?","NoPluginConfigurationMessage":"Ce module d'extension n'a rien \u00e0 configurer.","NoPluginsInstalledMessage":"Vous n'avez aucun module d'extension install\u00e9.","BrowsePluginCatalogMessage":"Explorer notre catalogue de modules d'extension pour voir ce qui est disponible."}

+ 1 - 0
MediaBrowser.Server.Implementations/Localization/JavaScript/it.json

@@ -0,0 +1 @@
+{"SettingsSaved":"Settaggi salvati.","AddUser":"Aggiungi utente","Users":"Utenti","Delete":"Elimina","Administrator":"Amministratore","Password":"Password","DeleteImage":"Elimina immagine","DeleteImageConfirmation":"Sei sicuro di voler eliminare questa immagine?","FileReadCancelled":"Il file letto \u00e8 stato cancellato.","FileNotFound":"File non trovato","FileReadError":"Errore durante la lettura del file.","DeleteUser":"Elimina utente","DeleteUserConfirmation":"Sei sicuro di voler eliminare {0}?","PasswordResetHeader":"Ripristina Password","PasswordResetComplete":"la password \u00e8 stata ripristinata.","PasswordResetConfirmation":"Sei sicuro di voler ripristinare la password?","PasswordSaved":"Password salvata.","PasswordMatchError":"Le password non coincidono.","OptionOff":"Spegni","OptionOn":"Accendi","OptionRelease":"Versione","OptionBeta":"Beta","OptionDev":"Dev","UninstallPluginHeader":"Disinstalla Plugin","UninstallPluginConfirmation":"Sei sicuro di voler Disinstallare {0}?","NoPluginConfigurationMessage":"Questo Plugin non \u00e8 stato configurato.","NoPluginsInstalledMessage":"non ci sono Plugins installati.","BrowsePluginCatalogMessage":"Sfoglia il catalogo dei Plugins."}

+ 0 - 1
MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json

@@ -5,7 +5,6 @@
 	"Delete": "Delete",
 	"Administrator": "Administrator",
 	"Password": "Password",
-	"CreatePassword": "Create Password",
 	"DeleteImage": "Delete Image",
 	"DeleteImageConfirmation": "Are you sure you wish to delete this image?",
 	"FileReadCancelled": "The file read has been cancelled.",

+ 1 - 31
MediaBrowser.Server.Implementations/Localization/JavaScript/nl.json

@@ -1,31 +1 @@
-{
-	"SettingsSaved": "Instellingen opgeslagen.",
-	"AddUser": "Gebruiker toevoegen",
-	"Users": "Gebruikers",
-	"Delete": "Verwijderen",
-	"Administrator": "Beheerder",
-	"Password": "Wachtwoord",
-	"CreatePassword": "Maak wachtwoord",
-	"DeleteImage": "Verwijder afbeelding",
-	"DeleteImageConfirmation": "Weet je zeker dat je deze afbeelding wilt verwijderen?",
-	"FileReadCancelled": "Het lezen van het bestand is geannuleerd",
-	"FileNotFound": "Bestand niet gevonden.",
-	"FileReadError": "Er is een fout opgetreden bij het lezen van het bestand.",
-	"DeleteUser": "Verwijder gebruiker",
-	"DeleteUserConfirmation": "Weet je zeker dat je {0} wilt verwijderen?",
-	"PasswordResetHeader": "Wachtwoord opnieuw instellen",
-	"PasswordResetComplete": "Het wachtwoord is opnieuw ingesteld.",
-	"PasswordResetConfirmation": "Weet je zeker dat je het wachtwoord opnieuw in wilt stellen?",
-	"PasswordSaved": "Wachtwoord opgeslagen.",
-	"PasswordMatchError": "Wachtwoord en wachtwoord bevestiging moeten hetzelfde zijn.",
-	"OptionOff": "Uit",
-	"OptionOn": "Aan",
-	"OptionRelease": "Release",
-	"OptionBeta": "Beta",
-	"OptionDev": "Dev",
-	"UninstallPluginHeader": "Deinstalleer Plugin",
-	"UninstallPluginConfirmation": "Weet u zeker dat u {0} wilt deinstalleren?",
-	"NoPluginConfigurationMessage": "Deze plugin heeft niets in te stellen",
-	"NoPluginsInstalledMessage": "U heeft geen plugins geinstalleerd",
-	"BrowsePluginCatalogMessage": "Blader door de Plugincatalogus voor beschikbare plugins."
-}
+{"SettingsSaved":"Instellingen opgeslagen.","AddUser":"Gebruiker toevoegen","Users":"Gebruikers","Delete":"Verwijderen","Administrator":"Beheerder","Password":"Wachtwoord","DeleteImage":"Verwijder afbeelding","DeleteImageConfirmation":"Weet je zeker dat je deze afbeelding wilt verwijderen?","FileReadCancelled":"Het lezen van het bestand is geannuleerd","FileNotFound":"Bestand niet gevonden.","FileReadError":"Er is een fout opgetreden bij het lezen van het bestand.","DeleteUser":"Verwijder gebruiker","DeleteUserConfirmation":"Weet je zeker dat je {0} wilt verwijderen?","PasswordResetHeader":"Wachtwoord opnieuw instellen","PasswordResetComplete":"Het wachtwoord is opnieuw ingesteld.","PasswordResetConfirmation":"Weet je zeker dat je het wachtwoord opnieuw in wilt stellen?","PasswordSaved":"Wachtwoord opgeslagen.","PasswordMatchError":"Wachtwoord en wachtwoord bevestiging moeten hetzelfde zijn.","OptionOff":"Uit","OptionOn":"Aan","OptionRelease":"Release","OptionBeta":"Beta","OptionDev":"Dev","UninstallPluginHeader":"Deinstalleer Plugin","UninstallPluginConfirmation":"Weet u zeker dat u {0} wilt deinstalleren?","NoPluginConfigurationMessage":"Deze plugin heeft niets in te stellen","NoPluginsInstalledMessage":"U heeft geen plugins geinstalleerd","BrowsePluginCatalogMessage":"Blader door de Plugincatalogus voor beschikbare plugins."}

+ 1 - 31
MediaBrowser.Server.Implementations/Localization/JavaScript/pt_BR.json

@@ -1,31 +1 @@
-{
-	"SettingsSaved": "Prefer\u00eancias salvas.",
-	"AddUser": "Adicionar Usu\u00e1rio",
-	"Users": "Usu\u00e1rios",
-	"Delete": "Apagar",
-	"Administrator": "Administrador",
-	"Password": "Senha",
-	"CreatePassword": "Criar Senha",
-	"DeleteImage": "Apagar Imagem",
-	"DeleteImageConfirmation": "Tem certeza que deseja apagar esta imagem?",
-	"FileReadCancelled": "A leitura do arquivo foi cancelada.",
-	"FileNotFound": "Arquivo n\u00e3o encontrado.",
-	"FileReadError": "Ocorreu um erro ao ler o arquivo.",
-	"DeleteUser": "Apagar Usu\u00e1rio",
-	"DeleteUserConfirmation": "Tem certeza que deseja apagar {0}?",
-	"PasswordResetHeader": "Redefinir Senha",
-	"PasswordResetComplete": "A senha foi redefinida.",
-	"PasswordResetConfirmation": "Deseja realmente redefinir a senha?",
-	"PasswordSaved": "Senha salva.",
-	"PasswordMatchError": "A senha e confirma\u00e7\u00e3o da senha devem conferir.",
-	"OptionOff": "Off",
-	"OptionOn": "On",
-	"OptionRelease": "Release",
-	"OptionBeta": "Beta",
-	"OptionDev": "Dev",
-	"UninstallPluginHeader": "Desintalar Plugin",
-	"UninstallPluginConfirmation": "Deseja realmente desinstalar {0}?",
-	"NoPluginConfigurationMessage": "Este plugin n\u00e3o necessita configurar.",
-	"NoPluginsInstalledMessage": "N\u00e3o existem plugins instalados.",
-	"BrowsePluginCatalogMessage": "Navegue pelo cat\u00e1logo de plugins para ver os dispon\u00edveis."
-}
+{"SettingsSaved":"Prefer\u00eancias salvas.","AddUser":"Adicionar Usu\u00e1rio","Users":"Usu\u00e1rios","Delete":"Apagar","Administrator":"Administrador","Password":"Senha","DeleteImage":"Apagar Imagem","DeleteImageConfirmation":"Tem certeza que deseja apagar esta imagem?","FileReadCancelled":"A leitura do arquivo foi cancelada.","FileNotFound":"Arquivo n\u00e3o encontrado.","FileReadError":"Ocorreu um erro ao ler o arquivo.","DeleteUser":"Apagar Usu\u00e1rio","DeleteUserConfirmation":"Tem certeza que deseja apagar {0}?","PasswordResetHeader":"Redefinir Senha","PasswordResetComplete":"A senha foi redefinida.","PasswordResetConfirmation":"Deseja realmente redefinir a senha?","PasswordSaved":"Senha salva.","PasswordMatchError":"A senha e confirma\u00e7\u00e3o da senha devem conferir.","OptionOff":"Off","OptionOn":"On","OptionRelease":"Release","OptionBeta":"Beta","OptionDev":"Dev","UninstallPluginHeader":"Desintalar Plugin","UninstallPluginConfirmation":"Deseja realmente desinstalar {0}?","NoPluginConfigurationMessage":"Este plugin n\u00e3o necessita configurar.","NoPluginsInstalledMessage":"N\u00e3o existem plugins instalados.","BrowsePluginCatalogMessage":"Navegue pelo cat\u00e1logo de plugins para ver os dispon\u00edveis."}

+ 1 - 31
MediaBrowser.Server.Implementations/Localization/JavaScript/pt_PT.json

@@ -1,31 +1 @@
-{
-	"SettingsSaved": "Configura\u00e7\u00f5es guardadas.",
-	"AddUser": "Adicionar Utilizador",
-	"Users": "Utilizadores",
-	"Delete": "Apagar",
-	"Administrator": "Administrador",
-	"Password": "Senha",
-	"CreatePassword": "Criar Senha",
-	"DeleteImage": "Apagar Imagem",
-	"DeleteImageConfirmation": "Tem a certeza que pretende apagar a imagem?",
-	"FileReadCancelled": "A leitura do ficheiro foi cancelada.",
-	"FileNotFound": "Ficheiro n\u00e3o encontrado",
-	"FileReadError": "Ocorreu um erro ao ler o ficheiro.",
-	"DeleteUser": "Apagar Utilizador",
-	"DeleteUserConfirmation": "Tem a certeza que pretende apagar {0}?",
-	"PasswordResetHeader": "Redefinir Senha",
-	"PasswordResetComplete": "A senha foi redefinida.",
-	"PasswordResetConfirmation": "Tem a certeza que pretende redefinir a senha?",
-	"PasswordSaved": "Senha guardada.",
-	"PasswordMatchError": "A senha e a confirma\u00e7\u00e3o da senha devem coincidir.",
-	"OptionOff": "Desligado",
-	"OptionOn": "Ligado",
-	"OptionRelease": "Final",
-	"OptionBeta": "Beta",
-	"OptionDev": "Dev",
-	"UninstallPluginHeader": "Desinstalar extens\u00e3o",
-	"UninstallPluginConfirmation": "Tem a certeza que pretende desinstalar {0}?",
-	"NoPluginConfigurationMessage": "Esta extens\u00e3o n\u00e3o \u00e9 configur\u00e1vel.",
-	"NoPluginsInstalledMessage": "N\u00e3o tem extens\u00f5es instaladas.",
-	"BrowsePluginCatalogMessage": "Navegue o nosso cat\u00e1logo de extens\u00f5es para ver as extens\u00f5es dispon\u00edveis."
-}
+{"SettingsSaved":"Configura\u00e7\u00f5es guardadas.","AddUser":"Adicionar Utilizador","Users":"Utilizadores","Delete":"Apagar","Administrator":"Administrador","Password":"Senha","DeleteImage":"Apagar Imagem","DeleteImageConfirmation":"Tem a certeza que pretende apagar a imagem?","FileReadCancelled":"A leitura do ficheiro foi cancelada.","FileNotFound":"Ficheiro n\u00e3o encontrado","FileReadError":"Ocorreu um erro ao ler o ficheiro.","DeleteUser":"Apagar Utilizador","DeleteUserConfirmation":"Tem a certeza que pretende apagar {0}?","PasswordResetHeader":"Redefinir Senha","PasswordResetComplete":"A senha foi redefinida.","PasswordResetConfirmation":"Tem a certeza que pretende redefinir a senha?","PasswordSaved":"Senha guardada.","PasswordMatchError":"A senha e a confirma\u00e7\u00e3o da senha devem coincidir.","OptionOff":"Desligado","OptionOn":"Ligado","OptionRelease":"Final","OptionBeta":"Beta","OptionDev":"Dev","UninstallPluginHeader":"Desinstalar extens\u00e3o","UninstallPluginConfirmation":"Tem a certeza que pretende desinstalar {0}?","NoPluginConfigurationMessage":"Esta extens\u00e3o n\u00e3o \u00e9 configur\u00e1vel.","NoPluginsInstalledMessage":"N\u00e3o tem extens\u00f5es instaladas.","BrowsePluginCatalogMessage":"Navegue o nosso cat\u00e1logo de extens\u00f5es para ver as extens\u00f5es dispon\u00edveis."}

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 31
MediaBrowser.Server.Implementations/Localization/JavaScript/ru.json


+ 1 - 31
MediaBrowser.Server.Implementations/Localization/JavaScript/zh_TW.json

@@ -1,31 +1 @@
-{
-	"SettingsSaved": "\u8a2d\u7f6e\u5df2\u4fdd\u5b58",
-	"AddUser": "Add User",
-	"Users": "\u7528\u6236",
-	"Delete": "\u522a\u9664",
-	"Administrator": "\u7ba1\u7406\u54e1",
-	"Password": "\u5bc6\u78bc",
-	"CreatePassword": "\u5275\u5efa\u5bc6\u78bc",
-	"DeleteImage": "\u522a\u9664\u5716\u50cf",
-	"DeleteImageConfirmation": "\u4f60\u78ba\u5b9a\u8981\u522a\u9664\u9019\u5f35\u5716\u7247\uff1f",
-	"FileReadCancelled": "The file read has been cancelled.",
-	"FileNotFound": "File not found.",
-	"FileReadError": "An error occurred while reading the file.",
-	"DeleteUser": "\u522a\u9664\u7528\u6236",
-	"DeleteUserConfirmation": "Are you sure you wish to delete {0}?",
-	"PasswordResetHeader": "\u91cd\u8a2d\u5bc6\u78bc",
-	"PasswordResetComplete": "\u5bc6\u78bc\u5df2\u91cd\u8a2d",
-	"PasswordResetConfirmation": "\u4f60\u78ba\u5b9a\u8981\u91cd\u8a2d\u5bc6\u78bc\uff1f",
-	"PasswordSaved": "\u5bc6\u78bc\u5df2\u4fdd\u5b58\u3002",
-	"PasswordMatchError": "\u5bc6\u78bc\u548c\u78ba\u8a8d\u5bc6\u78bc\u5fc5\u9808\u4e00\u81f4\u3002",
-	"OptionOff": "Off",
-	"OptionOn": "On",
-	"OptionRelease": "Release",
-	"OptionBeta": "Beta",
-	"OptionDev": "Dev",
-	"UninstallPluginHeader": "Uninstall Plugin",
-	"UninstallPluginConfirmation": "Are you sure you wish to uninstall {0}?",
-	"NoPluginConfigurationMessage": "This plugin has nothing to configure.",
-	"NoPluginsInstalledMessage": "You have no plugins installed.",
-	"BrowsePluginCatalogMessage": "Browse our plugin catalog to view available plugins."
-}
+{"SettingsSaved":"\u8a2d\u7f6e\u5df2\u4fdd\u5b58","AddUser":"Add User","Users":"\u7528\u6236","Delete":"\u522a\u9664","Administrator":"\u7ba1\u7406\u54e1","Password":"\u5bc6\u78bc","DeleteImage":"\u522a\u9664\u5716\u50cf","DeleteImageConfirmation":"\u4f60\u78ba\u5b9a\u8981\u522a\u9664\u9019\u5f35\u5716\u7247\uff1f","FileReadCancelled":"The file read has been cancelled.","FileNotFound":"File not found.","FileReadError":"An error occurred while reading the file.","DeleteUser":"\u522a\u9664\u7528\u6236","DeleteUserConfirmation":"Are you sure you wish to delete {0}?","PasswordResetHeader":"\u91cd\u8a2d\u5bc6\u78bc","PasswordResetComplete":"\u5bc6\u78bc\u5df2\u91cd\u8a2d","PasswordResetConfirmation":"\u4f60\u78ba\u5b9a\u8981\u91cd\u8a2d\u5bc6\u78bc\uff1f","PasswordSaved":"\u5bc6\u78bc\u5df2\u4fdd\u5b58\u3002","PasswordMatchError":"\u5bc6\u78bc\u548c\u78ba\u8a8d\u5bc6\u78bc\u5fc5\u9808\u4e00\u81f4\u3002","OptionOff":"Off","OptionOn":"On","OptionRelease":"Release","OptionBeta":"Beta","OptionDev":"Dev","UninstallPluginHeader":"Uninstall Plugin","UninstallPluginConfirmation":"Are you sure you wish to uninstall {0}?","NoPluginConfigurationMessage":"This plugin has nothing to configure.","NoPluginsInstalledMessage":"You have no plugins installed.","BrowsePluginCatalogMessage":"Browse our plugin catalog to view available plugins."}

+ 2 - 0
MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs

@@ -338,6 +338,8 @@ namespace MediaBrowser.Server.Implementations.Localization
                 new LocalizatonOption{ Name="Dutch", Value="nl"},
                 new LocalizatonOption{ Name="French", Value="fr"},
                 new LocalizatonOption{ Name="German", Value="de"},
+                new LocalizatonOption{ Name="Hebrew", Value="he"},
+                new LocalizatonOption{ Name="Italian", Value="it"},
                 new LocalizatonOption{ Name="Portuguese (Brazil)", Value="pt-BR"},
                 new LocalizatonOption{ Name="Portuguese (Portugal)", Value="pt-PT"},
                 new LocalizatonOption{ Name="Russian", Value="ru"},

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 50
MediaBrowser.Server.Implementations/Localization/Server/de.json


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 50
MediaBrowser.Server.Implementations/Localization/Server/en_US.json


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 50
MediaBrowser.Server.Implementations/Localization/Server/es.json


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 50
MediaBrowser.Server.Implementations/Localization/Server/fr.json


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/he.json


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/it.json


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 50
MediaBrowser.Server.Implementations/Localization/Server/nl.json


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 50
MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 50
MediaBrowser.Server.Implementations/Localization/Server/pt_PT.json


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 50
MediaBrowser.Server.Implementations/Localization/Server/ru.json


+ 41 - 2
MediaBrowser.Server.Implementations/Localization/Server/server.json

@@ -15,7 +15,7 @@
 	"LabelNext": "Next",
 	"LabelYoureDone": "You're Done!",
 	"WelcomeToMediaBrowser": "Welcome to Media Browser!",
-	"LabelMediaBrowser": "Media Browser",
+	"TitleMediaBrowser": "Media Browser",
 	"ThisWizardWillGuideYou": "This wizard will help guide you through the setup process.",
 	"TellUsAboutYourself": "Tell us about yourself",
 	"LabelYourFirstName": "Your first name:",
@@ -46,5 +46,44 @@
 	"LabelSaveLocalMetadata": "Save artwork and metadata into media folders",
 	"LabelSaveLocalMetadataHelp": "Saving artwork and metadata directly into media folders will put them in a place where they can be easily edited.",
 	"LabelDownloadInternetMetadata": "Download artwork and metadata from the internet",
-	"LabelDownloadInternetMetadataHelp": "Media Browser can download information about your media to enable rich presentations."
+	"LabelDownloadInternetMetadataHelp": "Media Browser can download information about your media to enable rich presentations.",
+	"TabPreferences": "Preferences",
+	"TabPassword": "Password",
+	"TabLibraryAccess": "Library Access",
+	"TabImage": "Image",
+	"TabProfile": "Profile",
+	"LabelDisplayMissingEpisodesWithinSeasons": "Display missing episodes within seasons",
+	"LabelUnairedMissingEpisodesWithinSeasons": "Display unaired episodes within seasons",
+	"HeaderVideoPlaybackSettings": "Video Playback Settings",
+	"LabelAudioLanguagePreference": "Audio language preference:",
+	"LabelSubtitleLanguagePreference": "Subtitle language preference:",
+	"LabelDisplayForcedSubtitlesOnly": "Display only forced subtitles",
+	"TabProfiles": "Profiles",
+	"TabSecurity": "Security",
+	"ButtonAddUser": "Add User",
+	"ButtonSave": "Save",
+	"ButtonResetPassword": "Reset Password",
+	"LabelNewPassword": "New password:",
+	"LabelNewPasswordConfirm": "New password confirm:",
+	"HeaderCreatePassword": "Create Password",
+	"LabelCurrentPassword": "Current password:",
+	"LabelMaxParentalRating": "Maximum allowed parental rating:",
+	"MaxParentalRatingHelp": "Content with a higher rating will be hidden from this user.",
+	"LibraryAccessHelp": "Select the media folders to share with this user. Administrators will be able to edit all folders using the metadata manager.",
+	"ButtonDeleteImage": "Delete Image",
+	"ButtonUpload": "Upload",
+	"HeaderUploadNewImage": "Upload New Image",
+	"LabelDropImageHere": "Drop Image Here",
+	"ImageUploadAspectRatioHelp": "1:1 Aspect Ratio Recommended. JPG/PNG only.",
+	"MessageNothingHere": "Nothing here.",
+	"MessagePleaseEnsureInternetMetadata": "Please ensure downloading of internet metadata is enabled.",
+	"TabSuggested": "Suggested",
+	"TabLatest": "Latest",
+	"TabUpcoming": "Upcoming",
+	"TabShows": "Shows",
+	"TabEpisodes": "Episodes",
+	"TabGenres": "Genres",
+	"TabPeople": "People",
+	"TabNetworks": "Networks",
+	"HeaderUsers": "Users"
 }

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 50
MediaBrowser.Server.Implementations/Localization/Server/zh_TW.json


+ 4 - 0
MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

@@ -301,6 +301,10 @@
     <EmbeddedResource Include="Localization\JavaScript\es.json" />
     <EmbeddedResource Include="Localization\Server\es.json" />
     <EmbeddedResource Include="Localization\Server\pt_BR.json" />
+    <EmbeddedResource Include="Localization\JavaScript\it.json" />
+    <EmbeddedResource Include="Localization\JavaScript\pt_BR.json" />
+    <EmbeddedResource Include="Localization\Server\he.json" />
+    <EmbeddedResource Include="Localization\Server\it.json" />
     <None Include="packages.config" />
   </ItemGroup>
   <ItemGroup>

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно