浏览代码

Merge pull request #1476 from MediaBrowser/dev

merge from dev
Luke 9 年之前
父节点
当前提交
09e01d9aad

+ 6 - 10
MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs

@@ -127,10 +127,14 @@ namespace MediaBrowser.Dlna.Ssdp
                             args.EndPoint = endPoint;
                             args.LocalEndPoint = new IPEndPoint(localIp, 0);
 
-                            if (!_ssdpHandler.IsSelfNotification(args))
+                            if (_ssdpHandler.IgnoreMessage(args, true))
                             {
-                                TryCreateDevice(args);
+                                return;
                             }
+
+                            _ssdpHandler.LogMessageReceived(args, true);
+
+                            TryCreateDevice(args);
                         }
                     }
 
@@ -217,14 +221,6 @@ namespace MediaBrowser.Dlna.Ssdp
                 return;
             }
 
-            if (_config.GetDlnaConfiguration().EnableDebugLog)
-            {
-                var headerTexts = args.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value));
-                var headerText = string.Join(",", headerTexts.ToArray());
-
-                _logger.Debug("{0} Device message received from {1}. Headers: {2}", args.Method, args.EndPoint, headerText);
-            }
-
             EventHelper.FireEventIfNotNull(DeviceDiscovered, this, args, _logger);
         }
 

+ 128 - 63
MediaBrowser.Dlna/Ssdp/SsdpHandler.cs

@@ -86,8 +86,15 @@ namespace MediaBrowser.Dlna.Ssdp
 
         public event EventHandler<SsdpMessageEventArgs> MessageReceived;
 
-        private async void OnMessageReceived(SsdpMessageEventArgs args)
+        private async void OnMessageReceived(SsdpMessageEventArgs args, bool isMulticast)
         {
+            if (IgnoreMessage(args, isMulticast))
+            {
+                return;
+            }
+
+            LogMessageReceived(args, isMulticast);
+
             var headers = args.Headers;
             string st;
 
@@ -108,6 +115,59 @@ namespace MediaBrowser.Dlna.Ssdp
             EventHelper.FireEventIfNotNull(MessageReceived, this, args, _logger);
         }
 
+        internal void LogMessageReceived(SsdpMessageEventArgs args, bool isMulticast)
+        {
+            var enableDebugLogging = _config.GetDlnaConfiguration().EnableDebugLog;
+
+            if (enableDebugLogging)
+            {
+                var headerTexts = args.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value));
+                var headerText = string.Join(",", headerTexts.ToArray());
+
+                var protocol = isMulticast ? "Multicast" : "Unicast";
+                var localEndPointString = args.LocalEndPoint == null ? "null" : args.LocalEndPoint.ToString();
+                _logger.Debug("{0} message received from {1} on {3}. Protocol: {4} Headers: {2}", args.Method, args.EndPoint, headerText, localEndPointString, protocol);
+            }
+        }
+
+        internal bool IgnoreMessage(SsdpMessageEventArgs args, bool isMulticast)
+        {
+            string usn;
+            if (args.Headers.TryGetValue("USN", out usn))
+            {
+                // USN=uuid:b67df29b5c379445fde78c3774ab518d::urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1
+                if (RegisteredDevices.Select(i => i.USN).Contains(usn, StringComparer.OrdinalIgnoreCase))
+                {
+                    //var headerTexts = args.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value));
+                    //var headerText = string.Join(",", headerTexts.ToArray());
+
+                    //var protocol = isMulticast ? "Multicast" : "Unicast";
+                    //var localEndPointString = args.LocalEndPoint == null ? "null" : args.LocalEndPoint.ToString();
+                    //_logger.Debug("IGNORING {0} message received from {1} on {3}. Protocol: {4} Headers: {2}", args.Method, args.EndPoint, headerText, localEndPointString, protocol);
+
+                    return true;
+                }
+            }
+
+            string serverId;
+            if (args.Headers.TryGetValue("X-EMBY-SERVERID", out serverId))
+            {
+                if (string.Equals(serverId, _appHost.SystemId, StringComparison.OrdinalIgnoreCase))
+                {
+                    //var headerTexts = args.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value));
+                    //var headerText = string.Join(",", headerTexts.ToArray());
+
+                    //var protocol = isMulticast ? "Multicast" : "Unicast";
+                    //var localEndPointString = args.LocalEndPoint == null ? "null" : args.LocalEndPoint.ToString();
+                    //_logger.Debug("IGNORING {0} message received from {1} on {3}. Protocol: {4} Headers: {2}", args.Method, args.EndPoint, headerText, localEndPointString, protocol);
+
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
         public IEnumerable<UpnpDevice> RegisteredDevices
         {
             get
@@ -126,7 +186,7 @@ namespace MediaBrowser.Dlna.Ssdp
             RestartSocketListener();
             ReloadAliveNotifier();
 
-            //CreateUnicastClient();
+            CreateUnicastClient();
 
             SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
             SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
@@ -146,6 +206,7 @@ namespace MediaBrowser.Dlna.Ssdp
 
             values["HOST"] = "239.255.255.250:1900";
             values["USER-AGENT"] = "UPnP/1.0 DLNADOC/1.50 Platinum/1.0.4.2";
+            values["X-EMBY-SERVERID"] = _appHost.SystemId;
 
             values["MAN"] = "\"ssdp:discover\"";
 
@@ -162,7 +223,7 @@ namespace MediaBrowser.Dlna.Ssdp
             // UDP is unreliable, so send 3 requests at a time (per Upnp spec, sec 1.1.2)
             SendDatagram(msg, _ssdpEndp, localIp, true);
 
-            //SendUnicastRequest(msg);
+            SendUnicastRequest(msg);
         }
 
         public async void SendDatagram(string msg,
@@ -242,9 +303,17 @@ namespace MediaBrowser.Dlna.Ssdp
 
                     var msg = new SsdpMessageBuilder().BuildMessage(header, values);
 
-                    SendDatagram(msg, endpoint, null, false, 1);
-                    SendDatagram(msg, endpoint, new IPEndPoint(d.Address, 0), false, 1);
-                    //SendDatagram(header, values, endpoint, null, true);
+                    var ipEndPoint = endpoint as IPEndPoint;
+                    if (ipEndPoint != null)
+                    {
+                        SendUnicastRequest(msg, ipEndPoint);
+                    }
+                    else
+                    {
+                        SendDatagram(msg, endpoint, null, false, 2);
+                        SendDatagram(msg, endpoint, new IPEndPoint(d.Address, 0), false, 2);
+                        //SendDatagram(header, values, endpoint, null, true);
+                    }
 
                     if (enableDebugLogging)
                     {
@@ -324,20 +393,7 @@ namespace MediaBrowser.Dlna.Ssdp
                 var args = SsdpHelper.ParseSsdpResponse(received);
                 args.EndPoint = endpoint;
 
-                if (IsSelfNotification(args))
-                {
-                    return;
-                }
-
-                if (enableDebugLogging)
-                {
-                    var headerTexts = args.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value));
-                    var headerText = string.Join(",", headerTexts.ToArray());
-
-                    _logger.Debug("{0} message received from {1} on {3}. Headers: {2}", args.Method, args.EndPoint, headerText, _multicastSocket.LocalEndPoint);
-                }
-
-                OnMessageReceived(args);
+                OnMessageReceived(args, true);
             }
             catch (ObjectDisposedException)
             {
@@ -357,26 +413,6 @@ namespace MediaBrowser.Dlna.Ssdp
             }
         }
 
-        internal bool IsSelfNotification(SsdpMessageEventArgs args)
-        {
-            // Avoid responding to self search messages
-            //string serverId;
-            //if (args.Headers.TryGetValue("X-EMBYSERVERID", out serverId) &&
-            //    string.Equals(serverId, _appHost.SystemId, StringComparison.OrdinalIgnoreCase))
-            //{
-            //    return true;
-            //}
-
-            string server;
-            args.Headers.TryGetValue("SERVER", out server);
-
-            if (string.Equals(server, _serverSignature, StringComparison.OrdinalIgnoreCase))
-            {
-                //return true;
-            }
-            return false;
-        }
-
         public void Dispose()
         {
             _config.NamedConfigurationUpdated -= _config_ConfigurationUpdated;
@@ -440,6 +476,7 @@ namespace MediaBrowser.Dlna.Ssdp
             values["NTS"] = "ssdp:" + type;
             values["NT"] = dev.Type;
             values["USN"] = dev.USN;
+            values["X-EMBY-SERVERID"] = _appHost.SystemId;
 
             if (logMessage)
             {
@@ -449,6 +486,7 @@ namespace MediaBrowser.Dlna.Ssdp
             var msg = new SsdpMessageBuilder().BuildMessage(header, values);
 
             SendDatagram(msg, _ssdpEndp, new IPEndPoint(dev.Address, 0), true);
+            //SendUnicastRequest(msg, 1);
         }
 
         public void RegisterNotification(string uuid, Uri descriptionUri, IPAddress address, IEnumerable<string> services)
@@ -489,14 +527,7 @@ namespace MediaBrowser.Dlna.Ssdp
                     _logger.ErrorException("Error creating unicast client", ex);
                 }
 
-                try
-                {
-                    UnicastSetBeginReceive();
-                }
-                catch (Exception ex)
-                {
-                    _logger.ErrorException("Error in UnicastSetBeginReceive", ex);
-                }
+                UnicastSetBeginReceive();
             }
         }
 
@@ -522,11 +553,18 @@ namespace MediaBrowser.Dlna.Ssdp
         /// </summary>
         private void UnicastSetBeginReceive()
         {
-            var ipRxEnd = new IPEndPoint(IPAddress.Any, _unicastPort);
-            var udpListener = new UdpState { EndPoint = ipRxEnd };
+            try
+            {
+                var ipRxEnd = new IPEndPoint(IPAddress.Any, _unicastPort);
+                var udpListener = new UdpState { EndPoint = ipRxEnd };
 
-            udpListener.UdpClient = _unicastClient;
-            _unicastClient.BeginReceive(UnicastReceiveCallback, udpListener);
+                udpListener.UdpClient = _unicastClient;
+                _unicastClient.BeginReceive(UnicastReceiveCallback, udpListener);
+            }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("Error in UnicastSetBeginReceive", ex);
+            }
         }
 
         /// <summary>
@@ -540,18 +578,25 @@ namespace MediaBrowser.Dlna.Ssdp
             var endpoint = ((UdpState)(ar.AsyncState)).EndPoint;
             if (udpClient.Client != null)
             {
-                var responseBytes = udpClient.EndReceive(ar, ref endpoint);
-                var args = SsdpHelper.ParseSsdpResponse(responseBytes);
+                try
+                {
+                    var responseBytes = udpClient.EndReceive(ar, ref endpoint);
+                    var args = SsdpHelper.ParseSsdpResponse(responseBytes);
 
-                args.EndPoint = endpoint;
+                    args.EndPoint = endpoint;
 
-                OnMessageReceived(args);
+                    OnMessageReceived(args, false);
 
-                UnicastSetBeginReceive();
+                    UnicastSetBeginReceive();
+                }
+                catch (ObjectDisposedException)
+                {
+
+                }
             }
         }
 
-        private async void SendUnicastRequest(string request)
+        private void SendUnicastRequest(string request, int sendCount = 3)
         {
             if (_unicastClient == null)
             {
@@ -560,17 +605,37 @@ namespace MediaBrowser.Dlna.Ssdp
 
             _logger.Debug("Sending unicast search request");
 
-            byte[] req = Encoding.ASCII.GetBytes(request);
             var ipSsdp = IPAddress.Parse(SSDPAddr);
             var ipTxEnd = new IPEndPoint(ipSsdp, SSDPPort);
 
-            for (var i = 0; i < 3; i++)
+            SendUnicastRequest(request, ipTxEnd, sendCount);
+        }
+
+        private async void SendUnicastRequest(string request, IPEndPoint toEndPoint, int sendCount = 3)
+        {
+            if (_unicastClient == null)
             {
-                if (i > 0)
+                return;
+            }
+
+            _logger.Debug("Sending unicast search request");
+
+            byte[] req = Encoding.ASCII.GetBytes(request);
+
+            try
+            {
+                for (var i = 0; i < sendCount; i++)
                 {
-                    await Task.Delay(50).ConfigureAwait(false);
+                    if (i > 0)
+                    {
+                        await Task.Delay(50).ConfigureAwait(false);
+                    }
+                    _unicastClient.Send(req, req.Length, toEndPoint);
                 }
-                _unicastClient.Send(req, req.Length, ipTxEnd);
+            }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("Error in SendUnicastRequest", ex);
             }
         }
 

+ 2 - 2
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -129,7 +129,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
         /// <param name="request">The request.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        public Task<Model.MediaInfo.MediaInfo> GetMediaInfo(MediaInfoRequest request, CancellationToken cancellationToken)
+        public Task<MediaInfo> GetMediaInfo(MediaInfoRequest request, CancellationToken cancellationToken)
         {
             var extractChapters = request.MediaType == DlnaProfileType.Video && request.ExtractChapters;
 
@@ -175,7 +175,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{MediaInfoResult}.</returns>
         /// <exception cref="System.ApplicationException">ffprobe failed - streams and format are both null.</exception>
-        private async Task<Model.MediaInfo.MediaInfo> GetMediaInfoInternal(string inputPath,
+        private async Task<MediaInfo> GetMediaInfoInternal(string inputPath,
             string primaryPath,
             MediaProtocol protocol,
             bool extractChapters,

+ 77 - 48
MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs

@@ -27,7 +27,7 @@ namespace MediaBrowser.MediaEncoding.Probing
 
         public MediaInfo GetMediaInfo(InternalMediaInfoResult data, VideoType videoType, bool isAudio, string path, MediaProtocol protocol)
         {
-            var info = new Model.MediaInfo.MediaInfo
+            var info = new MediaInfo
             {
                 Path = path,
                 Protocol = protocol
@@ -56,40 +56,81 @@ namespace MediaBrowser.MediaEncoding.Probing
                 }
             }
 
-            if (isAudio)
-            {
-                SetAudioRuntimeTicks(data, info);
+            var tags = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+            var tagStreamType = isAudio ? "audio" : "video";
 
-                var tags = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-
-                // tags are normally located under data.format, but we've seen some cases with ogg where they're part of the audio stream
-                // so let's create a combined list of both
+            if (data.streams != null)
+            {
+                var tagStream = data.streams.FirstOrDefault(i => string.Equals(i.codec_type, tagStreamType, StringComparison.OrdinalIgnoreCase));
 
-                if (data.streams != null)
+                if (tagStream != null && tagStream.tags != null)
                 {
-                    var audioStream = data.streams.FirstOrDefault(i => string.Equals(i.codec_type, "audio", StringComparison.OrdinalIgnoreCase));
-
-                    if (audioStream != null && audioStream.tags != null)
+                    foreach (var pair in tagStream.tags)
                     {
-                        foreach (var pair in audioStream.tags)
-                        {
-                            tags[pair.Key] = pair.Value;
-                        }
+                        tags[pair.Key] = pair.Value;
                     }
                 }
+            }
 
-                if (data.format != null && data.format.tags != null)
+            if (data.format != null && data.format.tags != null)
+            {
+                foreach (var pair in data.format.tags)
                 {
-                    foreach (var pair in data.format.tags)
-                    {
-                        tags[pair.Key] = pair.Value;
-                    }
+                    tags[pair.Key] = pair.Value;
                 }
+            }
+
+            FetchGenres(info, tags);
+            var overview = FFProbeHelpers.GetDictionaryValue(tags, "description");
+            if (!string.IsNullOrWhiteSpace(overview))
+            {
+                info.Overview = overview;
+            }
+
+            var title = FFProbeHelpers.GetDictionaryValue(tags, "title");
+            if (!string.IsNullOrWhiteSpace(title))
+            {
+                info.Name = title;
+            }
+
+            info.ProductionYear = FFProbeHelpers.GetDictionaryNumericValue(tags, "date");
+
+            // Several different forms of retaildate
+            info.PremiereDate = FFProbeHelpers.GetDictionaryDateTime(tags, "retaildate") ??
+                FFProbeHelpers.GetDictionaryDateTime(tags, "retail date") ??
+                FFProbeHelpers.GetDictionaryDateTime(tags, "retail_date") ??
+                FFProbeHelpers.GetDictionaryDateTime(tags, "date");
+
+            if (isAudio)
+            {
+                SetAudioRuntimeTicks(data, info);
+
+                // tags are normally located under data.format, but we've seen some cases with ogg where they're part of the audio stream
+                // so let's create a combined list of both
 
                 SetAudioInfoFromTags(info, tags);
             }
             else
             {
+                var iTunEXTC = FFProbeHelpers.GetDictionaryValue(tags, "iTunEXTC");
+                if (!string.IsNullOrWhiteSpace(iTunEXTC))
+                {
+                    var parts = iTunEXTC.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
+                    // Example 
+                    // mpaa|G|100|For crude humor
+                    if (parts.Length == 4)
+                    {
+                        info.OfficialRating = parts[1];
+                        info.OfficialRatingDescription = parts[3];
+                    }
+                }
+                
+                var itunesXml = FFProbeHelpers.GetDictionaryValue(tags, "iTunMOVI");
+                if (!string.IsNullOrWhiteSpace(itunesXml))
+                {
+                    FetchFromItunesInfo(itunesXml, info);
+                }
+                
                 if (data.format != null && !string.IsNullOrEmpty(data.format.duration))
                 {
                     info.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(data.format.duration, _usCulture)).Ticks;
@@ -108,6 +149,11 @@ namespace MediaBrowser.MediaEncoding.Probing
             return info;
         }
 
+        private void FetchFromItunesInfo(string xml, MediaInfo info)
+        {
+            // <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>cast</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>name</key>\n\t\t\t<string>Blender Foundation</string>\n\t\t</dict>\n\t\t<dict>\n\t\t\t<key>name</key>\n\t\t\t<string>Janus Bager Kristensen</string>\n\t\t</dict>\n\t</array>\n\t<key>directors</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>name</key>\n\t\t\t<string>Sacha Goedegebure</string>\n\t\t</dict>\n\t</array>\n\t<key>studio</key>\n\t<string>Blender Foundation</string>\n</dict>\n</plist>\n
+        }
+
         /// <summary>
         /// Converts ffprobe stream info to our MediaStream class
         /// </summary>
@@ -430,16 +476,8 @@ namespace MediaBrowser.MediaEncoding.Probing
             }
         }
 
-        private void SetAudioInfoFromTags(Model.MediaInfo.MediaInfo audio, Dictionary<string, string> tags)
+        private void SetAudioInfoFromTags(MediaInfo audio, Dictionary<string, string> tags)
         {
-            var title = FFProbeHelpers.GetDictionaryValue(tags, "title");
-
-            // Only set Name if title was found in the dictionary
-            if (!string.IsNullOrEmpty(title))
-            {
-                audio.Title = title;
-            }
-
             var composer = FFProbeHelpers.GetDictionaryValue(tags, "composer");
             if (!string.IsNullOrWhiteSpace(composer))
             {
@@ -511,22 +549,12 @@ namespace MediaBrowser.MediaEncoding.Probing
             // Disc number
             audio.ParentIndexNumber = GetDictionaryDiscValue(tags, "disc");
 
-            audio.ProductionYear = FFProbeHelpers.GetDictionaryNumericValue(tags, "date");
-
-            // Several different forms of retaildate
-            audio.PremiereDate = FFProbeHelpers.GetDictionaryDateTime(tags, "retaildate") ??
-                FFProbeHelpers.GetDictionaryDateTime(tags, "retail date") ??
-                FFProbeHelpers.GetDictionaryDateTime(tags, "retail_date") ??
-                FFProbeHelpers.GetDictionaryDateTime(tags, "date");
-
             // If we don't have a ProductionYear try and get it from PremiereDate
             if (audio.PremiereDate.HasValue && !audio.ProductionYear.HasValue)
             {
                 audio.ProductionYear = audio.PremiereDate.Value.ToLocalTime().Year;
             }
 
-            FetchGenres(audio, tags);
-
             // There's several values in tags may or may not be present
             FetchStudios(audio, tags, "organization");
             FetchStudios(audio, tags, "ensemble");
@@ -693,7 +721,7 @@ namespace MediaBrowser.MediaEncoding.Probing
         /// </summary>
         /// <param name="info">The information.</param>
         /// <param name="tags">The tags.</param>
-        private void FetchGenres(Model.MediaInfo.MediaInfo info, Dictionary<string, string> tags)
+        private void FetchGenres(MediaInfo info, Dictionary<string, string> tags)
         {
             var val = FFProbeHelpers.GetDictionaryValue(tags, "genre");
 
@@ -764,7 +792,7 @@ namespace MediaBrowser.MediaEncoding.Probing
 
         private const int MaxSubtitleDescriptionExtractionLength = 100; // When extracting subtitles, the maximum length to consider (to avoid invalid filenames)
 
-        private void FetchWtvInfo(Model.MediaInfo.MediaInfo video, InternalMediaInfoResult data)
+        private void FetchWtvInfo(MediaInfo video, InternalMediaInfoResult data)
         {
             if (data.format == null || data.format.tags == null)
             {
@@ -775,15 +803,16 @@ namespace MediaBrowser.MediaEncoding.Probing
 
             if (!string.IsNullOrWhiteSpace(genres))
             {
-                //genres = FFProbeHelpers.GetDictionaryValue(data.format.tags, "genre");
-            }
-
-            if (!string.IsNullOrWhiteSpace(genres))
-            {
-                video.Genres = genres.Split(new[] { ';', '/', ',' }, StringSplitOptions.RemoveEmptyEntries)
+                var genreList = genres.Split(new[] { ';', '/', ',' }, StringSplitOptions.RemoveEmptyEntries)
                     .Where(i => !string.IsNullOrWhiteSpace(i))
                     .Select(i => i.Trim())
                     .ToList();
+
+                // If this is empty then don't overwrite genres that might have been fetched earlier
+                if (genreList.Count > 0)
+                {
+                    video.Genres = genreList;
+                }
             }
 
             var officialRating = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/ParentalRating");

+ 1 - 0
MediaBrowser.Model/LiveTv/LiveTvOptions.cs

@@ -47,5 +47,6 @@ namespace MediaBrowser.Model.LiveTv
         public string ListingsId { get; set; }
         public string ZipCode { get; set; }
         public string Country { get; set; }
+        public string Path { get; set; }
     }
 }

+ 5 - 5
MediaBrowser.Model/MediaInfo/MediaInfo.cs

@@ -9,11 +9,6 @@ namespace MediaBrowser.Model.MediaInfo
     {
         public List<ChapterInfo> Chapters { get; set; }
 
-        /// <summary>
-        /// Gets or sets the title.
-        /// </summary>
-        /// <value>The title.</value>
-        public string Title { get; set; }
         /// <summary>
         /// Gets or sets the album.
         /// </summary>
@@ -47,6 +42,11 @@ namespace MediaBrowser.Model.MediaInfo
         /// <value>The official rating.</value>
         public string OfficialRating { get; set; }
         /// <summary>
+        /// Gets or sets the official rating description.
+        /// </summary>
+        /// <value>The official rating description.</value>
+        public string OfficialRatingDescription { get; set; }
+        /// <summary>
         /// Gets or sets the overview.
         /// </summary>
         /// <value>The overview.</value>

+ 2 - 2
MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs

@@ -125,9 +125,9 @@ namespace MediaBrowser.Providers.MediaInfo
         private async Task FetchDataFromTags(Audio audio, Model.MediaInfo.MediaInfo data)
         {
             // Only set Name if title was found in the dictionary
-            if (!string.IsNullOrEmpty(data.Title))
+            if (!string.IsNullOrEmpty(data.Name))
             {
-                audio.Name = data.Title;
+                audio.Name = data.Name;
             }
 
             if (!audio.LockedFields.Contains(MetadataFields.Cast))

+ 12 - 0
MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs

@@ -383,6 +383,11 @@ namespace MediaBrowser.Providers.MediaInfo
                 }
             }
 
+            if (!string.IsNullOrWhiteSpace(data.OfficialRatingDescription) || isFullRefresh)
+            {
+                video.OfficialRatingDescription = data.OfficialRatingDescription;
+            }
+
             if (!video.LockedFields.Contains(MetadataFields.Genres))
             {
                 if (video.Genres.Count == 0 || isFullRefresh)
@@ -437,6 +442,13 @@ namespace MediaBrowser.Providers.MediaInfo
                     video.ParentIndexNumber = data.ParentIndexNumber;
                 }
             }
+            if (!string.IsNullOrWhiteSpace(data.Name))
+            {
+                if (string.IsNullOrWhiteSpace(video.Name) || string.Equals(video.Name, Path.GetFileNameWithoutExtension(video.Path), StringComparison.OrdinalIgnoreCase))
+                {
+                    video.Name = data.Name;
+                }
+            }
 
             // If we don't have a ProductionYear try and get it from PremiereDate
             if (video.PremiereDate.HasValue && !video.ProductionYear.HasValue)

+ 6 - 1
MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpDiscovery.cs

@@ -42,7 +42,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp
         void _deviceDiscovery_DeviceDiscovered(object sender, SsdpMessageEventArgs e)
         {
             string st = null;
-            if (e.Headers.TryGetValue("ST", out st) && string.Equals(st, "urn:ses-com:device:SatIPServer:1", StringComparison.OrdinalIgnoreCase))
+            string nt = null;
+            e.Headers.TryGetValue("ST", out st);
+            e.Headers.TryGetValue("NT", out nt);
+
+            if (string.Equals(st, "urn:ses-com:device:SatIPServer:1", StringComparison.OrdinalIgnoreCase) ||
+                string.Equals(nt, "urn:ses-com:device:SatIPServer:1", StringComparison.OrdinalIgnoreCase))
             {
                 string location;
                 if (e.Headers.TryGetValue("Location", out location) && !string.IsNullOrWhiteSpace(location))

+ 2 - 2
MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

@@ -52,9 +52,9 @@
     <Reference Include="Interfaces.IO">
       <HintPath>..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll</HintPath>
     </Reference>
-    <Reference Include="MediaBrowser.Naming, Version=1.0.5884.23751, Culture=neutral, processorArchitecture=MSIL">
+    <Reference Include="MediaBrowser.Naming, Version=1.0.5891.29179, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\MediaBrowser.Naming.1.0.0.47\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll</HintPath>
+      <HintPath>..\packages\MediaBrowser.Naming.1.0.0.48\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll</HintPath>
     </Reference>
     <Reference Include="MoreLinq">
       <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath>

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

@@ -3,7 +3,7 @@
   <package id="CommonIO" version="1.0.0.7" targetFramework="net45" />
   <package id="Emby.XmlTv" version="1.0.0.48" targetFramework="net45" />
   <package id="Interfaces.IO" version="1.0.0.5" targetFramework="net45" />
-  <package id="MediaBrowser.Naming" version="1.0.0.47" targetFramework="net45" />
+  <package id="MediaBrowser.Naming" version="1.0.0.48" targetFramework="net45" />
   <package id="Mono.Nat" version="1.2.24.0" targetFramework="net45" />
   <package id="morelinq" version="1.4.0" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />

+ 3 - 3
MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

@@ -140,6 +140,9 @@
     <Content Include="dashboard-ui\components\remotecontrolautoplay.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
+    <Content Include="dashboard-ui\devices\windowsphone\wp.css">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\legacy\buttonenabled.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
@@ -254,9 +257,6 @@
     <Content Include="dashboard-ui\favorites.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <None Include="dashboard-ui\legacy\deferred.js">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None>
     <Content Include="dashboard-ui\livetvguideprovider.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>