aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Pulverenti <luke.pulverenti@gmail.com>2017-11-29 15:50:18 -0500
committerLuke Pulverenti <luke.pulverenti@gmail.com>2017-11-29 15:50:18 -0500
commit5207067811afdd5e4c0f3bbcfcb254bfbb6d7ce9 (patch)
tree35d93f67b56e62eb7be8d84856b1f4ee2f3b1fb7
parent26edcfefbb031a98f2d6f64b692bba07587614b7 (diff)
fix live tv over dlna
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs56
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs2
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj2
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs16
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs3
-rw-r--r--Emby.Server.Implementations/Localization/Core/ca.json126
-rw-r--r--Emby.Server.Implementations/Localization/Core/de.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/el.json91
-rw-r--r--Emby.Server.Implementations/Localization/Core/es-MX.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/gsw.json91
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt-BR.json2
-rw-r--r--Emby.Server.Implementations/Networking/NetworkManager.cs14
-rw-r--r--MediaBrowser.Common/Net/INetworkManager.cs4
-rw-r--r--MediaBrowser.Model/Dlna/StreamInfo.cs5
14 files changed, 327 insertions, 91 deletions
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 2e5c4be90..9a159194e 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/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;
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index eb0f5150f..ddead897e 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/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);
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 7ccf54d20..cef37910e 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/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" />
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
index e6c9b184e..04c5303f1 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
+++ b/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);
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
index b8c7c7b18..af7491e86 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
+++ b/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;
}
diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json
index f21a0b295..7c55540e5 100644
--- a/Emby.Server.Implementations/Localization/Core/ca.json
+++ b/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",
diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json
index 7bab689eb..bcfadb61c 100644
--- a/Emby.Server.Implementations/Localization/Core/de.json
+++ b/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}",
diff --git a/Emby.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json
new file mode 100644
index 000000000..ab229e111
--- /dev/null
+++ b/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"
+} \ No newline at end of file
diff --git a/Emby.Server.Implementations/Localization/Core/es-MX.json b/Emby.Server.Implementations/Localization/Core/es-MX.json
index 2cc1dd759..8bfaffec8 100644
--- a/Emby.Server.Implementations/Localization/Core/es-MX.json
+++ b/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",
diff --git a/Emby.Server.Implementations/Localization/Core/gsw.json b/Emby.Server.Implementations/Localization/Core/gsw.json
new file mode 100644
index 000000000..5c7ff5d6d
--- /dev/null
+++ b/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"
+} \ No newline at end of file
diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json
index fa4eac1c4..e0a375170 100644
--- a/Emby.Server.Implementations/Localization/Core/pt-BR.json
+++ b/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}",
diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs
index 50e9135b0..30a3ff9e8 100644
--- a/Emby.Server.Implementations/Networking/NetworkManager.cs
+++ b/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;
diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs
index e2ab47322..6ddc2e799 100644
--- a/MediaBrowser.Common/Net/INetworkManager.cs
+++ b/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>
diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs
index 93a46aaf4..6ded1f6dd 100644
--- a/MediaBrowser.Model/Dlna/StreamInfo.cs
+++ b/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);
}