浏览代码

fix live tv over dlna

Luke Pulverenti 7 年之前
父节点
当前提交
5207067811

+ 37 - 19
Emby.Server.Implementations/ApplicationHost.cs

@@ -393,6 +393,7 @@ namespace Emby.Server.Implementations
             ISystemEvents systemEvents,
             INetworkManager networkManager)
         {
+
             // hack alert, until common can target .net core
             BaseExtensions.CryptographyProvider = CryptographyProvider;
 
@@ -423,6 +424,13 @@ namespace Emby.Server.Implementations
             SetBaseExceptionMessage();
 
             fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
+
+            NetworkManager.NetworkChanged += NetworkManager_NetworkChanged;
+        }
+
+        private void NetworkManager_NetworkChanged(object sender, EventArgs e)
+        {
+            _validAddressResults.Clear();
         }
 
         private Version _version;
@@ -1960,9 +1968,9 @@ namespace Emby.Server.Implementations
             try
             {
                 // Return the first matched address, if found, or the first known local address
-                var address = (await GetLocalIpAddresses(cancellationToken).ConfigureAwait(false)).FirstOrDefault(i => !i.Equals(IpAddressInfo.Loopback) && !i.Equals(IpAddressInfo.IPv6Loopback));
+                var addresses = await GetLocalIpAddressesInternal(false, 1, cancellationToken).ConfigureAwait(false);
 
-                if (address != null)
+                foreach (var address in addresses)
                 {
                     return GetLocalApiUrl(address);
                 }
@@ -1994,7 +2002,12 @@ namespace Emby.Server.Implementations
                 HttpPort.ToString(CultureInfo.InvariantCulture));
         }
 
-        public async Task<List<IpAddressInfo>> GetLocalIpAddresses(CancellationToken cancellationToken)
+        public Task<List<IpAddressInfo>> GetLocalIpAddresses(CancellationToken cancellationToken)
+        {
+            return GetLocalIpAddressesInternal(true, 0, cancellationToken);
+        }
+
+        private async Task<List<IpAddressInfo>> GetLocalIpAddressesInternal(bool allowLoopback, int limit, CancellationToken cancellationToken)
         {
             var addresses = ServerConfigurationManager
                 .Configuration
@@ -2006,22 +2019,33 @@ namespace Emby.Server.Implementations
             if (addresses.Count == 0)
             {
                 addresses.AddRange(NetworkManager.GetLocalIpAddresses());
+            }
 
-                var list = new List<IpAddressInfo>();
+            var resultList = new List<IpAddressInfo>();
 
-                foreach (var address in addresses)
+            foreach (var address in addresses)
+            {
+                if (!allowLoopback)
                 {
-                    var valid = await IsIpAddressValidAsync(address, cancellationToken).ConfigureAwait(false);
-                    if (valid)
+                    if (address.Equals(IpAddressInfo.Loopback) || address.Equals(IpAddressInfo.IPv6Loopback))
                     {
-                        list.Add(address);
+                        continue;
                     }
                 }
 
-                addresses = list;
+                var valid = await IsIpAddressValidAsync(address, cancellationToken).ConfigureAwait(false);
+                if (valid)
+                {
+                    resultList.Add(address);
+
+                    if (limit > 0 && resultList.Count >= limit)
+                    {
+                        return resultList;
+                    }
+                }
             }
 
-            return addresses;
+            return resultList;
         }
 
         private IpAddressInfo NormalizeConfiguredLocalAddress(string address)
@@ -2042,7 +2066,6 @@ namespace Emby.Server.Implementations
         }
 
         private readonly ConcurrentDictionary<string, bool> _validAddressResults = new ConcurrentDictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
-        private DateTime _lastAddressCacheClear;
         private async Task<bool> IsIpAddressValidAsync(IpAddressInfo address, CancellationToken cancellationToken)
         {
             if (address.Equals(IpAddressInfo.Loopback) ||
@@ -2054,12 +2077,6 @@ namespace Emby.Server.Implementations
             var apiUrl = GetLocalApiUrl(address);
             apiUrl += "/system/ping";
 
-            if ((DateTime.UtcNow - _lastAddressCacheClear).TotalMinutes >= 15)
-            {
-                _lastAddressCacheClear = DateTime.UtcNow;
-                _validAddressResults.Clear();
-            }
-
             bool cachedResult;
             if (_validAddressResults.TryGetValue(apiUrl, out cachedResult))
             {
@@ -2087,18 +2104,19 @@ namespace Emby.Server.Implementations
                         var valid = string.Equals(Name, result, StringComparison.OrdinalIgnoreCase);
 
                         _validAddressResults.AddOrUpdate(apiUrl, valid, (k, v) => valid);
-                        //Logger.Debug("Ping test result to {0}. Success: {1}", apiUrl, valid);
+                        Logger.Debug("Ping test result to {0}. Success: {1}", apiUrl, valid);
                         return valid;
                     }
                 }
             }
             catch (OperationCanceledException)
             {
+                Logger.Debug("Ping test result to {0}. Success: {1}", apiUrl, "Cancelled");
                 throw;
             }
             catch
             {
-                //Logger.Debug("Ping test result to {0}. Success: {1}", apiUrl, false);
+                Logger.Debug("Ping test result to {0}. Success: {1}", apiUrl, false);
 
                 _validAddressResults.AddOrUpdate(apiUrl, false, (k, v) => false);
                 return false;

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

@@ -4286,7 +4286,7 @@ namespace Emby.Server.Implementations.Data
 
             if (query.MinParentalRating.HasValue)
             {
-                whereClauses.Add("InheritedParentalRatingValue<=@MinParentalRating");
+                whereClauses.Add("InheritedParentalRatingValue>=@MinParentalRating");
                 if (statement != null)
                 {
                     statement.TryBind("@MinParentalRating", query.MinParentalRating.Value);

+ 2 - 0
Emby.Server.Implementations/Emby.Server.Implementations.csproj

@@ -708,6 +708,8 @@
     <EmbeddedResource Include="Localization\Core\zh-CN.json" />
     <EmbeddedResource Include="Localization\Core\zh-HK.json" />
     <EmbeddedResource Include="Localization\Core\en-US.json" />
+    <EmbeddedResource Include="Localization\Core\el.json" />
+    <EmbeddedResource Include="Localization\Core\gsw.json" />
     <None Include="packages.config" />
     <None Include="TextEncoding\NLangDetect\Profiles\afr" />
     <None Include="TextEncoding\NLangDetect\Profiles\ara" />

+ 15 - 1
Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs

@@ -17,6 +17,7 @@ using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.System;
+using System.IO;
 
 namespace Emby.Server.Implementations.LiveTv.TunerHosts
 {
@@ -75,6 +76,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             return Task.FromResult(list);
         }
 
+        private string[] _disallowedSharedStreamExtensions = new string[] 
+        {
+            ".mkv",
+            ".mp4",
+            ".m3u8",
+            ".mpd"
+        };
+
         protected override async Task<ILiveStream> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken)
         {
             var tunerCount = info.TunerCount;
@@ -95,7 +104,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
 
             if (mediaSource.Protocol == MediaProtocol.Http && !mediaSource.RequiresLooping)
             {
-                return new SharedHttpStream(mediaSource, info, streamId, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _environment);
+                var extension = Path.GetExtension(mediaSource.Path) ?? string.Empty;
+
+                if (!_disallowedSharedStreamExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
+                {
+                    return new SharedHttpStream(mediaSource, info, streamId, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _environment);
+                }
             }
 
             return new LiveStream(mediaSource, info, _environment, FileSystem, Logger, Config.ApplicationPaths);

+ 2 - 1
Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs

@@ -71,7 +71,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             }
             else if (contentType.IndexOf("mp4", StringComparison.OrdinalIgnoreCase) != -1 ||
                contentType.IndexOf("dash", StringComparison.OrdinalIgnoreCase) != -1 ||
-               contentType.IndexOf("mpegURL", StringComparison.OrdinalIgnoreCase) != -1)
+               contentType.IndexOf("mpegURL", StringComparison.OrdinalIgnoreCase) != -1 ||
+               contentType.IndexOf("text/", StringComparison.OrdinalIgnoreCase) != -1)
             {
                 requiresRemux = true;
             }

+ 63 - 63
Emby.Server.Implementations/Localization/Core/ca.json

@@ -1,74 +1,74 @@
 {
     "Latest": "Darreres",
     "ValueSpecialEpisodeName": "Especial - {0}",
-    "Inherit": "Inherit",
-    "Books": "Books",
-    "Music": "Music",
-    "Games": "Games",
-    "Photos": "Photos",
-    "MixedContent": "Mixed content",
-    "MusicVideos": "Music videos",
-    "HomeVideos": "Home videos",
-    "Playlists": "Playlists",
-    "HeaderRecordingGroups": "Recording Groups",
+    "Inherit": "Heretat",
+    "Books": "Llibres",
+    "Music": "M\u00fasica",
+    "Games": "Jocs",
+    "Photos": "Fotos",
+    "MixedContent": "Contingut mesclat",
+    "MusicVideos": "V\u00eddeos musicals",
+    "HomeVideos": "V\u00eddeos dom\u00e8stics",
+    "Playlists": "Llistes de reproducci\u00f3",
+    "HeaderRecordingGroups": "Grups d'Enregistrament",
     "HeaderContinueWatching": "Continua Veient",
-    "HeaderFavoriteArtists": "Favorite Artists",
-    "HeaderFavoriteSongs": "Favorite Songs",
+    "HeaderFavoriteArtists": "Artistes Preferits",
+    "HeaderFavoriteSongs": "Can\u00e7ons Preferides",
     "HeaderAlbumArtists": "Album Artists",
-    "HeaderFavoriteAlbums": "Favorite Albums",
-    "HeaderFavoriteEpisodes": "Favorite Episodes",
+    "HeaderFavoriteAlbums": "\u00c0lbums Preferits",
+    "HeaderFavoriteEpisodes": "Episodis Preferits",
     "HeaderFavoriteShows": "Programes Preferits",
     "HeaderNextUp": "A continuaci\u00f3",
-    "Favorites": "Favorites",
-    "Collections": "Collections",
-    "Channels": "Channels",
-    "Movies": "Movies",
-    "Albums": "Albums",
-    "Artists": "Artists",
-    "Folders": "Folders",
-    "Songs": "Songs",
-    "TvShows": "TV Shows",
-    "Shows": "Shows",
+    "Favorites": "Preferits",
+    "Collections": "Col\u00b7leccions",
+    "Channels": "Canals",
+    "Movies": "Pel\u00b7l\u00edcules",
+    "Albums": "\u00c0lbums",
+    "Artists": "Artistes",
+    "Folders": "Directoris",
+    "Songs": "Can\u00e7ons",
+    "TvShows": "Espectacles de TV",
+    "Shows": "Espectacles",
     "Genres": "G\u00e8neres",
-    "NameSeasonNumber": "Season {0}",
-    "AppDeviceValues": "App: {0}, Device: {1}",
-    "UserDownloadingItemWithValues": "{0} is downloading {1}",
-    "HeaderLiveTV": "Live TV",
-    "ChapterNameValue": "Chapter {0}",
-    "ScheduledTaskFailedWithName": "{0} failed",
-    "LabelRunningTimeValue": "Running time: {0}",
-    "ScheduledTaskStartedWithName": "{0} started",
-    "VersionNumber": "Version {0}",
-    "PluginInstalledWithName": "{0} was installed",
-    "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly.",
-    "PluginUpdatedWithName": "{0} was updated",
-    "PluginUninstalledWithName": "{0} was uninstalled",
-    "ItemAddedWithName": "{0} was added to the library",
-    "ItemRemovedWithName": "{0} was removed from the library",
-    "LabelIpAddressValue": "Ip address: {0}",
-    "DeviceOnlineWithName": "{0} is connected",
-    "UserOnlineFromDevice": "{0} is online from {1}",
-    "ProviderValue": "Provider: {0}",
-    "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
-    "UserCreatedWithName": "User {0} has been created",
-    "UserPasswordChangedWithName": "Password has been changed for user {0}",
-    "UserDeletedWithName": "User {0} has been deleted",
-    "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
-    "MessageServerConfigurationUpdated": "Server configuration has been updated",
-    "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
-    "MessageApplicationUpdated": "Emby Server has been updated",
-    "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
-    "AuthenticationSucceededWithUserName": "{0} successfully authenticated",
-    "UserOfflineFromDevice": "{0} has disconnected from {1}",
-    "DeviceOfflineWithName": "{0} has disconnected",
-    "UserStartedPlayingItemWithValues": "{0} has started playing {1}",
-    "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
-    "NotificationOptionPluginError": "Plugin failure",
-    "NotificationOptionApplicationUpdateAvailable": "Application update available",
-    "NotificationOptionApplicationUpdateInstalled": "Application update installed",
-    "NotificationOptionPluginUpdateInstalled": "Plugin update installed",
-    "NotificationOptionPluginInstalled": "Plugin installed",
-    "NotificationOptionPluginUninstalled": "Plugin uninstalled",
+    "NameSeasonNumber": "Temporada {0}",
+    "AppDeviceValues": "App: {0}, Dispositiu: {1}",
+    "UserDownloadingItemWithValues": "{0} est\u00e0 descarregant {1}",
+    "HeaderLiveTV": "TV en Directe",
+    "ChapterNameValue": "Episodi {0}",
+    "ScheduledTaskFailedWithName": "{0} ha fallat",
+    "LabelRunningTimeValue": "Temps en marxa: {0}",
+    "ScheduledTaskStartedWithName": "{0} iniciat",
+    "VersionNumber": "Versi\u00f3 {0}",
+    "PluginInstalledWithName": "{0} ha estat instal\u00b7lat",
+    "StartupEmbyServerIsLoading": "El Servidor d'Emby est&agrave; carregant. Si et plau, prova de nou en breus.",
+    "PluginUpdatedWithName": "{0} ha estat actualitzat",
+    "PluginUninstalledWithName": "{0} ha estat desinstal\u00b7lat",
+    "ItemAddedWithName": "{0} afegit a la biblioteca",
+    "ItemRemovedWithName": "{0} eliminat de la biblioteca",
+    "LabelIpAddressValue": "Adre\u00e7a IP: {0}",
+    "DeviceOnlineWithName": "{0} est\u00e0 connectat",
+    "UserOnlineFromDevice": "{0} est\u00e0 connectat des de {1}",
+    "ProviderValue": "Prove\u00efdor: {0}",
+    "SubtitlesDownloadedForItem": "Subt\u00edtols descarregats per a {0}",
+    "UserCreatedWithName": "S'ha creat l'usuari {0}",
+    "UserPasswordChangedWithName": "La contrasenya ha estat canviada per a l'usuari {0}",
+    "UserDeletedWithName": "L'usuari {0} ha estat eliminat",
+    "UserConfigurationUpdatedWithName": "La configuraci\u00f3 d'usuari ha estat actualitzada per a {0}",
+    "MessageServerConfigurationUpdated": "S'ha actualitzat la configuraci\u00f3 del servidor",
+    "MessageNamedServerConfigurationUpdatedWithValue": "La secci\u00f3 de configuraci\u00f3 {0} ha estat actualitzada",
+    "MessageApplicationUpdated": "El Servidor d'Emby ha estat actualitzat",
+    "FailedLoginAttemptWithUserName": "Intent de connexi\u00f3 fallit des de {0}",
+    "AuthenticationSucceededWithUserName": "{0} s'ha autenticat correctament",
+    "UserOfflineFromDevice": "{0} s'ha desconnectat de {1}",
+    "DeviceOfflineWithName": "{0} s'ha desconnectat",
+    "UserStartedPlayingItemWithValues": "{0} ha comen\u00e7at a reproduir {1}",
+    "UserStoppedPlayingItemWithValues": "{0} ha parat de reproduir {1}",
+    "NotificationOptionPluginError": "Un component ha fallat",
+    "NotificationOptionApplicationUpdateAvailable": "Actualitzaci\u00f3 d'aplicaci\u00f3 disponible",
+    "NotificationOptionApplicationUpdateInstalled": "Actualitzaci\u00f3 d'aplicaci\u00f3 instal\u00b7lada",
+    "NotificationOptionPluginUpdateInstalled": "Actualitzaci\u00f3 de complement instal\u00b7lada",
+    "NotificationOptionPluginInstalled": "Complement instal\u00b7lat",
+    "NotificationOptionPluginUninstalled": "Complement desinstal\u00b7lat",
     "NotificationOptionVideoPlayback": "Video playback started",
     "NotificationOptionAudioPlayback": "Audio playback started",
     "NotificationOptionGamePlayback": "Game playback started",

+ 2 - 2
Emby.Server.Implementations/Localization/Core/de.json

@@ -7,7 +7,7 @@
     "Games": "Spiele",
     "Photos": "Fotos",
     "MixedContent": "Gemischte Inhalte",
-    "MusicVideos": "Musik-Videos",
+    "MusicVideos": "Musikvideos",
     "HomeVideos": "Heimvideos",
     "Playlists": "Wiedergabelisten",
     "HeaderRecordingGroups": "Aufnahme-Gruppen",
@@ -27,7 +27,7 @@
     "Artists": "Interpreten",
     "Folders": "Verzeichnisse",
     "Songs": "Songs",
-    "TvShows": "TV Shows",
+    "TvShows": "TV Sendungen",
     "Shows": "Serien",
     "Genres": "Genres",
     "NameSeasonNumber": "Staffel {0}",

+ 91 - 0
Emby.Server.Implementations/Localization/Core/el.json

@@ -0,0 +1,91 @@
+{
+    "Latest": "\u03a4\u03b5\u03bb\u03b5\u03c5\u03c4\u03b1\u03af\u03b1",
+    "ValueSpecialEpisodeName": "\u0395\u03b9\u03b4\u03b9\u03ba\u03ac - {0} ",
+    "Inherit": "Inherit",
+    "Books": "\u0392\u03b9\u03b2\u03bb\u03af\u03b1",
+    "Music": "\u039c\u03bf\u03c5\u03c3\u03b9\u03ba\u03ae",
+    "Games": "\u03a0\u03b1\u03b9\u03c7\u03bd\u03af\u03b4\u03b9\u03b1",
+    "Photos": "\u03a6\u03c9\u03c4\u03bf\u03b3\u03c1\u03b1\u03c6\u03af\u03b5\u03c2",
+    "MixedContent": "\u0391\u03bd\u03ac\u03bc\u03b5\u03b9\u03ba\u03c4\u03bf \u03a0\u03b5\u03c1\u03b9\u03b5\u03c7\u03cc\u03bc\u03b5\u03bd\u03bf",
+    "MusicVideos": "\u039c\u03bf\u03c5\u03c3\u03b9\u03ba\u03ac \u03b2\u03af\u03bd\u03c4\u03b5\u03bf",
+    "HomeVideos": "\u03a0\u03c1\u03bf\u03c3\u03c9\u03c0\u03b9\u03ba\u03ac \u0392\u03af\u03bd\u03c4\u03b5\u03bf",
+    "Playlists": "\u039b\u03af\u03c3\u03c4\u03b5\u03c2 \u03b1\u03bd\u03b1\u03c0\u03b1\u03c1\u03b1\u03b3\u03c9\u03b3\u03ae\u03c2",
+    "HeaderRecordingGroups": "\u0393\u03ba\u03c1\u03bf\u03c5\u03c0 \u0395\u03b3\u03b3\u03c1\u03b1\u03c6\u03ce\u03bd",
+    "HeaderContinueWatching": "\u03a3\u03c5\u03bd\u03b5\u03c7\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03cd\u03b8\u03b7\u03c3\u03b7",
+    "HeaderFavoriteArtists": "\u0391\u03b3\u03b1\u03c0\u03b7\u03bc\u03ad\u03bd\u03bf\u03b9 \u039a\u03b1\u03bb\u03bb\u03b9\u03c4\u03ad\u03c7\u03bd\u03b5\u03c2",
+    "HeaderFavoriteSongs": "\u0391\u03b3\u03b1\u03c0\u03b7\u03bc\u03ad\u03bd\u03b1 \u03a4\u03c1\u03b1\u03b3\u03bf\u03cd\u03b4\u03b9\u03b1",
+    "HeaderAlbumArtists": "\u0386\u03bb\u03bc\u03c0\u03bf\u03c5\u03bc \u039a\u03b1\u03bb\u03bb\u03b9\u03c4\u03b5\u03c7\u03bd\u03ce\u03bd",
+    "HeaderFavoriteAlbums": "\u0391\u03b3\u03b1\u03c0\u03b7\u03bc\u03ad\u03bd\u03b1 \u0386\u03bb\u03bc\u03c0\u03bf\u03c5\u03bc",
+    "HeaderFavoriteEpisodes": "\u0391\u03b3\u03b1\u03c0\u03b7\u03bc\u03ad\u03bd\u03b1 \u03b5\u03c0\u03b5\u03b9\u03c3\u03cc\u03b4\u03b9\u03b1",
+    "HeaderFavoriteShows": "\u0391\u03b3\u03b1\u03c0\u03b7\u03bc\u03ad\u03bd\u03b5\u03c2 \u03a3\u03b5\u03b9\u03c1\u03ad\u03c2",
+    "HeaderNextUp": "\u0395\u03c0\u03cc\u03bc\u03b5\u03bd\u03bf",
+    "Favorites": "\u0391\u03b3\u03b1\u03c0\u03b7\u03bc\u03ad\u03bd\u03b1",
+    "Collections": "\u03a3\u03c5\u03bb\u03bb\u03bf\u03b3\u03ad\u03c2",
+    "Channels": "\u039a\u03b1\u03bd\u03ac\u03bb\u03b9\u03b1",
+    "Movies": "\u03a4\u03b1\u03b9\u03bd\u03af\u03b5\u03c2",
+    "Albums": "\u0386\u03bb\u03bc\u03c0\u03bf\u03c5\u03bc",
+    "Artists": "\u039a\u03b1\u03bb\u03bb\u03b9\u03c4\u03ad\u03c7\u03bd\u03b5\u03c2",
+    "Folders": "\u03a6\u03ac\u03ba\u03b5\u03bb\u03bf\u03b9",
+    "Songs": "\u03a4\u03c1\u03b1\u03b3\u03bf\u03cd\u03b4\u03b9\u03b1",
+    "TvShows": "\u03a4\u03b7\u03bb\u03b5\u03bf\u03c0\u03c4\u03b9\u03ba\u03ac \u03c0\u03c1\u03bf\u03b3\u03c1\u03ac\u03bc\u03bc\u03b1\u03c4\u03b1",
+    "Shows": "\u03a3\u03b5\u03b9\u03c1\u03ad\u03c2",
+    "Genres": "\u0395\u03af\u03b4\u03b7",
+    "NameSeasonNumber": "\u039a\u03cd\u03ba\u03bb\u03bf\u03c2 {0}",
+    "AppDeviceValues": "\u0395\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae: {0}, \u03a3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae: {1}",
+    "UserDownloadingItemWithValues": "{0} \u03ba\u03b1\u03c4\u03b5\u03b2\u03ac\u03b6\u03b5\u03b9 {1}",
+    "HeaderLiveTV": "\u0396\u03c9\u03bd\u03c4\u03b1\u03bd\u03ae \u03a4\u03b7\u03bb\u03b5\u03cc\u03c1\u03b1\u03c3\u03b7",
+    "ChapterNameValue": "\u039a\u03b5\u03c6\u03ac\u03bb\u03b1\u03b9\u03bf {0}",
+    "ScheduledTaskFailedWithName": "{0} \u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1",
+    "LabelRunningTimeValue": "\u0394\u03b9\u03ac\u03c1\u03ba\u03b5\u03b9\u03b1: {0}",
+    "ScheduledTaskStartedWithName": "{0} \u03ad\u03bd\u03b1\u03c1\u03be\u03b7",
+    "VersionNumber": "\u0388\u03ba\u03b4\u03bf\u03c3\u03b7 {0}",
+    "PluginInstalledWithName": "{0} \u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ae\u03b8\u03b7\u03ba\u03b5",
+    "StartupEmbyServerIsLoading": "\u039f \u03a3\u03ad\u03c1\u03b2\u03b5\u03c1 \u03c6\u03bf\u03c1\u03c4\u03ce\u03bd\u03b5\u03b9. \u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03ce \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03c3\u03b5 \u03bb\u03af\u03b3\u03bf",
+    "PluginUpdatedWithName": "{0} \u03ad\u03c7\u03b5\u03b9 \u03b1\u03bd\u03b1\u03b2\u03b1\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af",
+    "PluginUninstalledWithName": "{0} \u03ad\u03c7\u03b5\u03b9 \u03b1\u03c0\u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03b1\u03b8\u03b5\u03af",
+    "ItemAddedWithName": "{0} \u03c0\u03c1\u03bf\u03c3\u03c4\u03ad\u03b8\u03b7\u03ba\u03b5 \u03c3\u03c4\u03b7 \u03b2\u03b9\u03b2\u03bb\u03b9\u03bf\u03b8\u03ae\u03ba\u03b7",
+    "ItemRemovedWithName": "{0} \u03b4\u03b9\u03b1\u03b3\u03c1\u03ac\u03c6\u03b7\u03ba\u03b5 \u03b1\u03c0\u03cc \u03c4\u03b7 \u03b2\u03b9\u03b2\u03bb\u03b9\u03bf\u03b8\u03ae\u03ba\u03b7",
+    "LabelIpAddressValue": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP: {0}",
+    "DeviceOnlineWithName": "{0} \u03c3\u03c5\u03bd\u03b4\u03ad\u03b8\u03b7\u03ba\u03b5",
+    "UserOnlineFromDevice": "{0} \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b4\u03b5\u03bc\u03ad\u03bd\u03bf\u03c2 \u03b1\u03c0\u03bf {1}",
+    "ProviderValue": "\u03a0\u03ac\u03c1\u03bf\u03c7\u03bf\u03c2: {0}",
+    "SubtitlesDownloadedForItem": "\u03a5\u03c0\u03cc\u03c4\u03b9\u03c4\u03bb\u03bf\u03b9 \u03bb\u03ae\u03c6\u03b8\u03b7\u03ba\u03b1\u03bd \u03b1\u03c0\u03cc {0}",
+    "UserCreatedWithName": "\u0394\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03b8\u03b7\u03ba\u03b5 \u03bf \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7\u03c2 {0}",
+    "UserPasswordChangedWithName": "\u039f \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c4\u03bf\u03c5 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7 {0} \u03b1\u03bb\u03bb\u03ac\u03c7\u03b8\u03b7\u03ba\u03b5",
+    "UserDeletedWithName": "\u039f \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7\u03c2 {0} \u03b4\u03b9\u03b5\u03b3\u03c1\u03ac\u03c6\u03b5\u03b9",
+    "UserConfigurationUpdatedWithName": "\u039f\u03b9 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03c4\u03bf\u03c5 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7 {0} \u03ad\u03c7\u03bf\u03c5\u03bd \u03b1\u03bb\u03bb\u03ac\u03be\u03b5\u03b9",
+    "MessageServerConfigurationUpdated": "Server configuration has been updated",
+    "MessageNamedServerConfigurationUpdatedWithValue": "\u039f\u03b9 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03c4\u03bf\u03bc\u03ad\u03b1 \u03c4\u03bf\u03c5 \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae {0} \u03ad\u03c7\u03bf\u03c5\u03bd \u03b1\u03bb\u03bb\u03ac\u03be\u03b5\u03b9",
+    "MessageApplicationUpdated": "\u039f \u03a3\u03ad\u03c1\u03b2\u03b5\u03c1 \u03ad\u03c7\u03b5\u03b9 \u03b1\u03bd\u03b1\u03b2\u03b1\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af",
+    "FailedLoginAttemptWithUserName": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5 \u03b1\u03c0\u03cc {0}",
+    "AuthenticationSucceededWithUserName": "{0} \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03b5\u03af\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7",
+    "UserOfflineFromDevice": "{0} \u03b1\u03c0\u03bf\u03c3\u03c5\u03bd\u03b4\u03ad\u03b8\u03b7\u03ba\u03b5 \u03b1\u03c0\u03cc {1}",
+    "DeviceOfflineWithName": "{0} \u03b1\u03c0\u03bf\u03c3\u03c5\u03bd\u03b4\u03ad\u03b8\u03b7\u03ba\u03b5",
+    "UserStartedPlayingItemWithValues": "{0} \u03be\u03b5\u03ba\u03af\u03bd\u03b7\u03c3\u03b5 \u03bd\u03b1 \u03c0\u03b1\u03af\u03b6\u03b5\u03b9 {1}",
+    "UserStoppedPlayingItemWithValues": "{0} \u03c3\u03c4\u03b1\u03bc\u03ac\u03c4\u03b7\u03c3\u03b5 \u03bd\u03b1 \u03c0\u03b1\u03af\u03b6\u03b5\u03b9 {1}",
+    "NotificationOptionPluginError": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c4\u03bf\u03c5",
+    "NotificationOptionApplicationUpdateAvailable": "\u03a5\u03c0\u03ac\u03c1\u03c7\u03b5\u03b9 \u03b1\u03bd\u03b1\u03b2\u03ac\u03b8\u03bc\u03b9\u03c3\u03b7",
+    "NotificationOptionApplicationUpdateInstalled": "\u0397 \u03b1\u03bd\u03b1\u03b2\u03ac\u03b8\u03bc\u03b9\u03c3\u03b7 \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03ce\u03b8\u03b7\u03ba\u03b5",
+    "NotificationOptionPluginUpdateInstalled": "\u0397 \u03b1\u03bd\u03b1\u03b2\u03ac\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03bf\u03c5 plugin \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03ce\u03b8\u03b7\u03ba\u03b5",
+    "NotificationOptionPluginInstalled": "\u03a4\u03bf plugin \u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03b8\u03b7\u03ba\u03b5",
+    "NotificationOptionPluginUninstalled": "\u03a4\u03bf plugin \u03b1\u03c0\u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03b8\u03b7\u03ba\u03b5",
+    "NotificationOptionVideoPlayback": "\u03a4\u03bf \u03b2\u03af\u03bd\u03c4\u03b5\u03bf \u03c0\u03c1\u03bf\u03b2\u03ac\u03bb\u03bb\u03b5\u03c4\u03b1\u03b9",
+    "NotificationOptionAudioPlayback": "\u0397 \u03bc\u03bf\u03c5\u03c3\u03b9\u03ba\u03ae \u03c0\u03b1\u03af\u03b6\u03b5\u03b9",
+    "NotificationOptionGamePlayback": "\u03a4\u03bf \u03c0\u03b1\u03b9\u03c7\u03bd\u03af\u03b4\u03b9 \u03be\u03b5\u03ba\u03af\u03bd\u03b7\u03c3\u03b5",
+    "NotificationOptionVideoPlaybackStopped": "\u03a4\u03bf \u03b2\u03af\u03bd\u03c4\u03b5\u03bf \u03c3\u03c4\u03b1\u03bc\u03ac\u03c4\u03b7\u03c3\u03b5",
+    "NotificationOptionAudioPlaybackStopped": "\u0397 \u03bc\u03bf\u03c5\u03c3\u03b9\u03ba\u03ae \u03c3\u03c4\u03b1\u03bc\u03ac\u03c4\u03b7\u03c3\u03b5",
+    "NotificationOptionGamePlaybackStopped": "\u03a4\u03bf \u03c0\u03b1\u03b9\u03c7\u03bd\u03af\u03b4\u03b9 \u03c3\u03c4\u03b1\u03bc\u03ac\u03c4\u03b7\u03c3\u03b5",
+    "NotificationOptionTaskFailed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c0\u03c1\u03bf\u03b3\u03c1\u03b1\u03bc\u03bc\u03b1\u03c4\u03b9\u03c3\u03bc\u03ad\u03bd\u03b7\u03c2 \u03b5\u03c1\u03b3\u03b1\u03c3\u03af\u03b1\u03c2",
+    "NotificationOptionInstallationFailed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2",
+    "NotificationOptionNewLibraryContent": "\u03a0\u03c1\u03bf\u03c3\u03c4\u03ad\u03b8\u03b7\u03ba\u03b5 \u03bd\u03ad\u03bf \u03c0\u03b5\u03c1\u03b9\u03b5\u03c7\u03cc\u03bc\u03b5\u03bd\u03bf",
+    "NotificationOptionCameraImageUploaded": "Camera image uploaded",
+    "NotificationOptionUserLockedOut": "\u039f \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7\u03c2 \u03b1\u03c0\u03bf\u03ba\u03bb\u03b5\u03af\u03c3\u03c4\u03b7\u03ba\u03b5",
+    "NotificationOptionServerRestartRequired": "\u0391\u03c0\u03b1\u03b9\u03c4\u03b5\u03af\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae",
+    "UserLockedOutWithName": "\u039f \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7\u03c2 {0} \u03b1\u03c0\u03bf\u03ba\u03bb\u03b5\u03af\u03c3\u03c4\u03b7\u03ba\u03b5",
+    "SubtitleDownloadFailureForItem": "\u0391\u03b4\u03c5\u03bd\u03b1\u03bc\u03af\u03b1 \u03bb\u03ae\u03c8\u03b7\u03c2 \u03c5\u03c0\u03bf\u03c4\u03af\u03c4\u03bb\u03c9\u03bd \u03b1\u03c0\u03cc {0}",
+    "Sync": "\u03a3\u03c5\u03b3\u03c7\u03c1\u03bf\u03bd\u03b9\u03c3\u03bc\u03cc\u03c2",
+    "User": "\u03a7\u03c1\u03ae\u03c3\u03c4\u03b7\u03c2",
+    "System": "\u03a3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1",
+    "Application": "\u0395\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae",
+    "Plugin": "\u03a0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf"
+}

+ 1 - 1
Emby.Server.Implementations/Localization/Core/es-MX.json

@@ -83,7 +83,7 @@
     "NotificationOptionServerRestartRequired": "Se necesita reiniciar el Servidor",
     "UserLockedOutWithName": "El usuario {0} ha sido bloqueado",
     "SubtitleDownloadFailureForItem": "Fall\u00f3 la descarga de subt\u00edtulos para {0}",
-    "Sync": "Sinc.",
+    "Sync": "Sincronizar",
     "User": "Usuario",
     "System": "Sistema",
     "Application": "Aplicaci\u00f3n",

+ 91 - 0
Emby.Server.Implementations/Localization/Core/gsw.json

@@ -0,0 +1,91 @@
+{
+    "Latest": "Letschte",
+    "ValueSpecialEpisodeName": "Spezial - {0}",
+    "Inherit": "Hinzuef\u00fcege",
+    "Books": "B\u00fcecher",
+    "Music": "Musig",
+    "Games": "Spiel",
+    "Photos": "Fotis",
+    "MixedContent": "Gmischte Inhalt",
+    "MusicVideos": "Musigfilm",
+    "HomeVideos": "Heimfilmli",
+    "Playlists": "Abspielliste",
+    "HeaderRecordingGroups": "Ufnahmegruppe",
+    "HeaderContinueWatching": "Wiiterluege",
+    "HeaderFavoriteArtists": "Besti Interpret",
+    "HeaderFavoriteSongs": "Besti Lieder",
+    "HeaderAlbumArtists": "Albuminterprete",
+    "HeaderFavoriteAlbums": "Favorite Albums",
+    "HeaderFavoriteEpisodes": "Favorite Episodes",
+    "HeaderFavoriteShows": "Favorite Shows",
+    "HeaderNextUp": "Next Up",
+    "Favorites": "Favorites",
+    "Collections": "Collections",
+    "Channels": "Channels",
+    "Movies": "Movies",
+    "Albums": "Albums",
+    "Artists": "Artists",
+    "Folders": "Folders",
+    "Songs": "Songs",
+    "TvShows": "TV Shows",
+    "Shows": "Shows",
+    "Genres": "Genres",
+    "NameSeasonNumber": "Season {0}",
+    "AppDeviceValues": "App: {0}, Device: {1}",
+    "UserDownloadingItemWithValues": "{0} is downloading {1}",
+    "HeaderLiveTV": "Live TV",
+    "ChapterNameValue": "Chapter {0}",
+    "ScheduledTaskFailedWithName": "{0} failed",
+    "LabelRunningTimeValue": "Running time: {0}",
+    "ScheduledTaskStartedWithName": "{0} started",
+    "VersionNumber": "Version {0}",
+    "PluginInstalledWithName": "{0} was installed",
+    "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly.",
+    "PluginUpdatedWithName": "{0} was updated",
+    "PluginUninstalledWithName": "{0} was uninstalled",
+    "ItemAddedWithName": "{0} was added to the library",
+    "ItemRemovedWithName": "{0} was removed from the library",
+    "LabelIpAddressValue": "Ip address: {0}",
+    "DeviceOnlineWithName": "{0} is connected",
+    "UserOnlineFromDevice": "{0} is online from {1}",
+    "ProviderValue": "Provider: {0}",
+    "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
+    "UserCreatedWithName": "User {0} has been created",
+    "UserPasswordChangedWithName": "Password has been changed for user {0}",
+    "UserDeletedWithName": "User {0} has been deleted",
+    "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
+    "MessageServerConfigurationUpdated": "Server configuration has been updated",
+    "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
+    "MessageApplicationUpdated": "Emby Server has been updated",
+    "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
+    "AuthenticationSucceededWithUserName": "{0} successfully authenticated",
+    "UserOfflineFromDevice": "{0} has disconnected from {1}",
+    "DeviceOfflineWithName": "{0} has disconnected",
+    "UserStartedPlayingItemWithValues": "{0} has started playing {1}",
+    "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
+    "NotificationOptionPluginError": "Plugin failure",
+    "NotificationOptionApplicationUpdateAvailable": "Application update available",
+    "NotificationOptionApplicationUpdateInstalled": "Application update installed",
+    "NotificationOptionPluginUpdateInstalled": "Plugin update installed",
+    "NotificationOptionPluginInstalled": "Plugin installed",
+    "NotificationOptionPluginUninstalled": "Plugin uninstalled",
+    "NotificationOptionVideoPlayback": "Video playback started",
+    "NotificationOptionAudioPlayback": "Audio playback started",
+    "NotificationOptionGamePlayback": "Game playback started",
+    "NotificationOptionVideoPlaybackStopped": "Video playback stopped",
+    "NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
+    "NotificationOptionGamePlaybackStopped": "Game playback stopped",
+    "NotificationOptionTaskFailed": "Scheduled task failure",
+    "NotificationOptionInstallationFailed": "Installation failure",
+    "NotificationOptionNewLibraryContent": "New content added",
+    "NotificationOptionCameraImageUploaded": "Camera image uploaded",
+    "NotificationOptionUserLockedOut": "User locked out",
+    "NotificationOptionServerRestartRequired": "Server restart required",
+    "UserLockedOutWithName": "User {0} has been locked out",
+    "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
+    "Sync": "Sync",
+    "User": "User",
+    "System": "System",
+    "Application": "Application",
+    "Plugin": "Plugin"
+}

+ 1 - 1
Emby.Server.Implementations/Localization/Core/pt-BR.json

@@ -27,7 +27,7 @@
     "Artists": "Artistas",
     "Folders": "Pastas",
     "Songs": "M\u00fasicas",
-    "TvShows": "TV Shows",
+    "TvShows": "S\u00e9ries de TV",
     "Shows": "S\u00e9ries",
     "Genres": "G\u00eaneros",
     "NameSeasonNumber": "Temporada {0}",

+ 13 - 1
Emby.Server.Implementations/Networking/NetworkManager.cs

@@ -19,7 +19,9 @@ namespace Emby.Server.Implementations.Networking
     {
         protected ILogger Logger { get; private set; }
         private DateTime _lastRefresh;
-        private int NetworkCacheMinutes = 360;
+        private int NetworkCacheMinutes = 720;
+
+        public event EventHandler NetworkChanged;
 
         public NetworkManager(ILogger logger)
         {
@@ -50,12 +52,22 @@ namespace Emby.Server.Implementations.Networking
         {
             Logger.Debug("NetworkAvailabilityChanged");
             _lastRefresh = DateTime.MinValue;
+            OnNetworkChanged();
         }
 
         private void NetworkChange_NetworkAddressChanged(object sender, EventArgs e)
         {
             Logger.Debug("NetworkAddressChanged");
             _lastRefresh = DateTime.MinValue;
+            OnNetworkChanged();
+        }
+
+        private void OnNetworkChanged()
+        {
+            if (NetworkChanged != null)
+            {
+                NetworkChanged(this, EventArgs.Empty);
+            }
         }
 
         private List<IpAddressInfo> _localIpAddresses;

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

@@ -1,13 +1,15 @@
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Net;
 using System.Collections.Generic;
-using System.Net;
+using System;
 using System.Threading.Tasks;
 
 namespace MediaBrowser.Common.Net
 {
     public interface INetworkManager
     {
+        event EventHandler NetworkChanged;
+
         /// <summary>
         /// Gets a random port number that is currently available
         /// </summary>

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

@@ -186,6 +186,11 @@ namespace MediaBrowser.Model.Dlna
                 return MediaSource.Path;
             }
 
+            if (string.IsNullOrWhiteSpace(PlaySessionId))
+            {
+                PlaySessionId = Guid.NewGuid().ToString("N");
+            }
+
             string dlnaCommand = BuildDlnaParam(this, accessToken);
             return GetUrl(baseUrl, dlnaCommand);
         }