瀏覽代碼

Merge pull request #3055 from MediaBrowser/beta

Beta
Luke 7 年之前
父節點
當前提交
c32d865638
共有 100 個文件被更改,包括 1228 次插入559 次删除
  1. 2 1
      Emby.Dlna/Main/DlnaEntryPoint.cs
  2. 8 8
      Emby.Dlna/PlayTo/Device.cs
  3. 7 1
      Emby.Dlna/PlayTo/PlayToController.cs
  4. 106 90
      Emby.Dlna/PlayTo/PlayToManager.cs
  5. 5 2
      Emby.Dlna/PlayTo/SsdpHttpClient.cs
  6. 1 1
      Emby.Dlna/Profiles/DishHopperJoeyProfile.cs
  7. 1 1
      Emby.Dlna/Profiles/LinksysDMA2100Profile.cs
  8. 2 2
      Emby.Dlna/Profiles/PanasonicVieraProfile.cs
  9. 1 1
      Emby.Dlna/Profiles/PopcornHourProfile.cs
  10. 5 2
      Emby.Dlna/Profiles/SamsungSmartTvProfile.cs
  11. 3 3
      Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs
  12. 6 6
      Emby.Dlna/Profiles/SonyBravia2010Profile.cs
  13. 6 6
      Emby.Dlna/Profiles/SonyBravia2011Profile.cs
  14. 6 6
      Emby.Dlna/Profiles/SonyBravia2012Profile.cs
  15. 6 6
      Emby.Dlna/Profiles/SonyBravia2013Profile.cs
  16. 6 6
      Emby.Dlna/Profiles/SonyBravia2014Profile.cs
  17. 1 1
      Emby.Dlna/Profiles/SonyPs3Profile.cs
  18. 1 1
      Emby.Dlna/Profiles/SonyPs4Profile.cs
  19. 2 2
      Emby.Dlna/Profiles/WdtvLiveProfile.cs
  20. 1 1
      Emby.Dlna/Profiles/XboxOneProfile.cs
  21. 1 1
      Emby.Dlna/Profiles/Xml/Default.xml
  22. 1 1
      Emby.Dlna/Profiles/Xml/Denon AVR.xml
  23. 1 1
      Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml
  24. 2 2
      Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml
  25. 1 1
      Emby.Dlna/Profiles/Xml/LG Smart TV.xml
  26. 2 2
      Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml
  27. 1 1
      Emby.Dlna/Profiles/Xml/Marantz.xml
  28. 1 1
      Emby.Dlna/Profiles/Xml/MediaMonkey.xml
  29. 3 3
      Emby.Dlna/Profiles/Xml/Panasonic Viera.xml
  30. 2 2
      Emby.Dlna/Profiles/Xml/Popcorn Hour.xml
  31. 4 4
      Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml
  32. 1 1
      Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml
  33. 1 1
      Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml
  34. 1 1
      Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml
  35. 1 1
      Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml
  36. 1 1
      Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml
  37. 4 4
      Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml
  38. 7 7
      Emby.Dlna/Profiles/Xml/Sony Bravia (2010).xml
  39. 7 7
      Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml
  40. 7 7
      Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml
  41. 7 7
      Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml
  42. 7 7
      Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml
  43. 2 2
      Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml
  44. 2 2
      Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml
  45. 3 3
      Emby.Dlna/Profiles/Xml/WDTV Live.xml
  46. 2 2
      Emby.Dlna/Profiles/Xml/Xbox One.xml
  47. 1 1
      Emby.Dlna/Profiles/Xml/foobar2000.xml
  48. 2 0
      Emby.Drawing.ImageMagick/ImageMagickEncoder.cs
  49. 0 3
      Emby.Drawing.Skia/Emby.Drawing.Skia.csproj
  50. 11 37
      Emby.Drawing.Skia/PlayedIndicatorDrawer.cs
  51. 2 0
      Emby.Drawing.Skia/SkiaEncoder.cs
  52. 1 1
      Emby.Drawing.Skia/UnplayedCountIndicator.cs
  53. 73 31
      Emby.Drawing/ImageProcessor.cs
  54. 96 29
      Emby.Server.Implementations/ApplicationHost.cs
  55. 18 0
      Emby.Server.Implementations/Archiving/ZipClient.cs
  56. 1 1
      Emby.Server.Implementations/Browser/BrowserLauncher.cs
  57. 8 2
      Emby.Server.Implementations/Data/SqliteItemRepository.cs
  58. 2 0
      Emby.Server.Implementations/Emby.Server.Implementations.csproj
  59. 2 1
      Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
  60. 33 3
      Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
  61. 19 14
      Emby.Server.Implementations/EntryPoints/StartupWizard.cs
  62. 4 3
      Emby.Server.Implementations/IO/LibraryMonitor.cs
  63. 15 0
      Emby.Server.Implementations/IO/ManagedFileSystem.cs
  64. 0 11
      Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
  65. 15 6
      Emby.Server.Implementations/Library/LibraryManager.cs
  66. 0 20
      Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
  67. 25 2
      Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
  68. 2 1
      Emby.Server.Implementations/Library/SearchEngine.cs
  69. 7 4
      Emby.Server.Implementations/Library/UserManager.cs
  70. 32 3
      Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
  71. 53 17
      Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
  72. 5 3
      Emby.Server.Implementations/LiveTv/LiveTvManager.cs
  73. 1 1
      Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
  74. 8 0
      Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
  75. 19 2
      Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
  76. 19 6
      Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
  77. 63 63
      Emby.Server.Implementations/Localization/Core/ca.json
  78. 2 2
      Emby.Server.Implementations/Localization/Core/de.json
  79. 91 0
      Emby.Server.Implementations/Localization/Core/el.json
  80. 1 1
      Emby.Server.Implementations/Localization/Core/es-MX.json
  81. 91 0
      Emby.Server.Implementations/Localization/Core/gsw.json
  82. 1 1
      Emby.Server.Implementations/Localization/Core/pt-BR.json
  83. 15 10
      Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
  84. 51 13
      Emby.Server.Implementations/Networking/NetworkManager.cs
  85. 5 1
      Emby.Server.Implementations/Session/HttpSessionController.cs
  86. 2 4
      Emby.Server.Implementations/Session/SessionManager.cs
  87. 3 3
      Emby.Server.Implementations/Session/WebSocketController.cs
  88. 3 2
      Emby.Server.Implementations/Social/SharingManager.cs
  89. 17 11
      Emby.Server.Implementations/Udp/UdpServer.cs
  90. 120 4
      MediaBrowser.Api/FilterService.cs
  91. 9 0
      MediaBrowser.Api/LiveTv/LiveTvService.cs
  92. 3 2
      MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs
  93. 12 1
      MediaBrowser.Api/SearchService.cs
  94. 3 2
      MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs
  95. 1 0
      MediaBrowser.Api/StartupWizardService.cs
  96. 2 1
      MediaBrowser.Api/System/ActivityLogWebSocketListener.cs
  97. 3 2
      MediaBrowser.Api/System/SystemInfoWebSocketListener.cs
  98. 4 13
      MediaBrowser.Api/System/SystemService.cs
  99. 1 1
      MediaBrowser.Api/UserService.cs
  100. 3 1
      MediaBrowser.Common/Net/INetworkManager.cs

+ 2 - 1
Emby.Dlna/Main/DlnaEntryPoint.cs

@@ -24,6 +24,7 @@ using MediaBrowser.Model.System;
 using MediaBrowser.Model.Threading;
 using Rssdp;
 using Rssdp.Infrastructure;
+using System.Threading;
 
 namespace Emby.Dlna.Main
 {
@@ -252,7 +253,7 @@ namespace Emby.Dlna.Main
             var cacheLength = _config.GetDlnaConfiguration().BlastAliveMessageIntervalSeconds;
             _Publisher.SupportPnpRootDevice = false;
 
-            var addresses = (await _appHost.GetLocalIpAddresses().ConfigureAwait(false)).ToList();
+            var addresses = (await _appHost.GetLocalIpAddresses(CancellationToken.None).ConfigureAwait(false)).ToList();
 
             var udn = CreateUuid(_appHost.SystemId);
 

+ 8 - 8
Emby.Dlna/PlayTo/Device.cs

@@ -831,7 +831,7 @@ namespace Emby.Dlna.PlayTo
 
         #region From XML
 
-        private async Task GetAVProtocolAsync()
+        private async Task GetAVProtocolAsync(CancellationToken cancellationToken)
         {
             if (_disposed)
             {
@@ -845,12 +845,12 @@ namespace Emby.Dlna.PlayTo
             string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
 
             var httpClient = new SsdpHttpClient(_httpClient, _config);
-            var document = await httpClient.GetDataAsync(url).ConfigureAwait(false);
+            var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
 
             AvCommands = TransportCommands.Create(document);
         }
 
-        private async Task GetRenderingProtocolAsync()
+        private async Task GetRenderingProtocolAsync(CancellationToken cancellationToken)
         {
             if (_disposed)
             {
@@ -864,7 +864,7 @@ namespace Emby.Dlna.PlayTo
             string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
 
             var httpClient = new SsdpHttpClient(_httpClient, _config);
-            var document = await httpClient.GetDataAsync(url).ConfigureAwait(false);
+            var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
 
             RendererCommands = TransportCommands.Create(document);
         }
@@ -897,11 +897,11 @@ namespace Emby.Dlna.PlayTo
             set;
         }
 
-        public static async Task<Device> CreateuPnpDeviceAsync(Uri url, IHttpClient httpClient, IServerConfigurationManager config, ILogger logger, ITimerFactory timerFactory)
+        public static async Task<Device> CreateuPnpDeviceAsync(Uri url, IHttpClient httpClient, IServerConfigurationManager config, ILogger logger, ITimerFactory timerFactory, CancellationToken cancellationToken)
         {
             var ssdpHttpClient = new SsdpHttpClient(httpClient, config);
 
-            var document = await ssdpHttpClient.GetDataAsync(url.ToString()).ConfigureAwait(false);
+            var document = await ssdpHttpClient.GetDataAsync(url.ToString(), cancellationToken).ConfigureAwait(false);
 
             var deviceProperties = new DeviceInfo();
 
@@ -987,8 +987,8 @@ namespace Emby.Dlna.PlayTo
 
             if (device.GetAvTransportService() != null)
             {
-                await device.GetRenderingProtocolAsync().ConfigureAwait(false);
-                await device.GetAVProtocolAsync().ConfigureAwait(false);
+                await device.GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
+                await device.GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
             }
 
             return device;

+ 7 - 1
Emby.Dlna/PlayTo/PlayToController.cs

@@ -321,6 +321,12 @@ namespace Emby.Dlna.PlayTo
                 AddItemFromId(Guid.Parse(id), items);
             }
 
+            var startIndex = command.StartIndex ?? 0;
+            if (startIndex > 0)
+            {
+                items = items.Skip(startIndex).ToList();
+            }
+
             var playlist = new List<PlaylistItem>();
             var isFirst = true;
 
@@ -424,7 +430,7 @@ namespace Emby.Dlna.PlayTo
             return Task.FromResult(true);
         }
 
-        public Task SendRestartRequiredNotification(SystemInfo info, CancellationToken cancellationToken)
+        public Task SendRestartRequiredNotification(CancellationToken cancellationToken)
         {
             return Task.FromResult(true);
         }

+ 106 - 90
Emby.Dlna/PlayTo/PlayToManager.cs

@@ -18,6 +18,7 @@ using MediaBrowser.Model.Events;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Threading;
+using System.Threading;
 
 namespace Emby.Dlna.PlayTo
 {
@@ -44,6 +45,8 @@ namespace Emby.Dlna.PlayTo
         private readonly List<string> _nonRendererUrls = new List<string>();
         private DateTime _lastRendererClear;
         private bool _disposed;
+        private SemaphoreSlim _sessionLock = new SemaphoreSlim(1, 1);
+        private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
 
         public PlayToManager(ILogger logger, ISessionManager sessionManager, ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IImageProcessor imageProcessor, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient, IServerConfigurationManager config, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, ITimerFactory timerFactory)
         {
@@ -90,6 +93,7 @@ namespace Emby.Dlna.PlayTo
             if (usn.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) == -1 &&
                      nt.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) == -1)
             {
+                //_logger.Debug("Upnp device {0} does not contain a MediaRenderer device (0).", location);
                 return;
             }
 
@@ -98,92 +102,105 @@ namespace Emby.Dlna.PlayTo
                 return;
             }
 
+            var cancellationToken = _disposeCancellationTokenSource.Token;
+
+            await _sessionLock.WaitAsync(cancellationToken).ConfigureAwait(false);
+
             try
             {
-                lock (_nonRendererUrls)
+                if (_disposed)
                 {
-                    if ((DateTime.UtcNow - _lastRendererClear).TotalMinutes >= 10)
-                    {
-                        _nonRendererUrls.Clear();
-                        _lastRendererClear = DateTime.UtcNow;
-                    }
-
-                    if (_nonRendererUrls.Contains(location, StringComparer.OrdinalIgnoreCase))
-                    {
-                        return;
-                    }
+                    return;
                 }
 
-                var uri = info.Location;
-                _logger.Debug("Attempting to create PlayToController from location {0}", location);
-                var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger, _timerFactory).ConfigureAwait(false);
+                await AddDevice(info, location, cancellationToken).ConfigureAwait(false);
+            }
+            catch (OperationCanceledException)
+            {
+
+            }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("Error creating PlayTo device.", ex);
+
+                _nonRendererUrls.Add(location);
+            }
+            finally
+            {
+                _sessionLock.Release();
+            }
+        }
+
+        private async Task AddDevice(UpnpDeviceInfo info, string location, CancellationToken cancellationToken)
+        {
+            if ((DateTime.UtcNow - _lastRendererClear).TotalMinutes >= 10)
+            {
+                _nonRendererUrls.Clear();
+                _lastRendererClear = DateTime.UtcNow;
+            }
+
+            if (_nonRendererUrls.Contains(location, StringComparer.OrdinalIgnoreCase))
+            {
+                return;
+            }
+
+            var uri = info.Location;
+            _logger.Debug("Attempting to create PlayToController from location {0}", location);
+            var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger, _timerFactory, cancellationToken).ConfigureAwait(false);
+
+            if (device.RendererCommands == null)
+            {
+                //_logger.Debug("Upnp device {0} does not contain a MediaRenderer device (1).", location);
+                _nonRendererUrls.Add(location);
+                return;
+            }
+
+            _logger.Debug("Logging session activity from location {0}", location);
+            var sessionInfo = await _sessionManager.LogSessionActivity(device.Properties.ClientType, _appHost.ApplicationVersion.ToString(), device.Properties.UUID, device.Properties.Name, uri.OriginalString, null).ConfigureAwait(false);
+
+            var controller = sessionInfo.SessionController as PlayToController;
 
-                if (device.RendererCommands == null)
+            if (controller == null)
+            {
+                string serverAddress;
+                if (info.LocalIpAddress == null || info.LocalIpAddress.Equals(IpAddressInfo.Any) || info.LocalIpAddress.Equals(IpAddressInfo.IPv6Loopback))
                 {
-                    lock (_nonRendererUrls)
-                    {
-                        _nonRendererUrls.Add(location);
-                        return;
-                    }
+                    serverAddress = await GetServerAddress(null, cancellationToken).ConfigureAwait(false);
                 }
-
-                if (_disposed)
+                else
                 {
-                    return;
+                    serverAddress = await GetServerAddress(info.LocalIpAddress, cancellationToken).ConfigureAwait(false);
                 }
 
-                _logger.Debug("Logging session activity from location {0}", location);
-                var sessionInfo = await _sessionManager.LogSessionActivity(device.Properties.ClientType, _appHost.ApplicationVersion.ToString(), device.Properties.UUID, device.Properties.Name, uri.OriginalString, null)
-                    .ConfigureAwait(false);
-
-                var controller = sessionInfo.SessionController as PlayToController;
-
-                if (controller == null)
+                string accessToken = null;
+
+                sessionInfo.SessionController = controller = new PlayToController(sessionInfo,
+                    _sessionManager,
+                    _libraryManager,
+                    _logger,
+                    _dlnaManager,
+                    _userManager,
+                    _imageProcessor,
+                    serverAddress,
+                    accessToken,
+                    _deviceDiscovery,
+                    _userDataManager,
+                    _localization,
+                    _mediaSourceManager,
+                    _config,
+                    _mediaEncoder);
+
+                controller.Init(device);
+
+                var profile = _dlnaManager.GetProfile(device.Properties.ToDeviceIdentification()) ??
+                              _dlnaManager.GetDefaultProfile();
+
+                _sessionManager.ReportCapabilities(sessionInfo.Id, new ClientCapabilities
                 {
-                    if (_disposed)
-                    {
-                        return;
-                    }
+                    PlayableMediaTypes = profile.GetSupportedMediaTypes(),
 
-                    string serverAddress;
-                    if (info.LocalIpAddress == null || info.LocalIpAddress.Equals(IpAddressInfo.Any) || info.LocalIpAddress.Equals(IpAddressInfo.IPv6Loopback))
+                    SupportedCommands = new string[]
                     {
-                        serverAddress = await GetServerAddress(null).ConfigureAwait(false);
-                    }
-                    else
-                    {
-                        serverAddress = await GetServerAddress(info.LocalIpAddress).ConfigureAwait(false);
-                    }
-
-                    string accessToken = null;
-
-                    sessionInfo.SessionController = controller = new PlayToController(sessionInfo,
-                        _sessionManager,
-                        _libraryManager,
-                        _logger,
-                        _dlnaManager,
-                        _userManager,
-                        _imageProcessor,
-                        serverAddress,
-                        accessToken,
-                        _deviceDiscovery,
-                        _userDataManager,
-                        _localization,
-                        _mediaSourceManager,
-                        _config,
-                        _mediaEncoder);
-
-                    controller.Init(device);
-
-                    var profile = _dlnaManager.GetProfile(device.Properties.ToDeviceIdentification()) ??
-                                  _dlnaManager.GetDefaultProfile();
-
-                    _sessionManager.ReportCapabilities(sessionInfo.Id, new ClientCapabilities
-                    {
-                        PlayableMediaTypes = profile.GetSupportedMediaTypes(),
-
-                        SupportedCommands = new string[]
-                        {
                             GeneralCommandType.VolumeDown.ToString(),
                             GeneralCommandType.VolumeUp.ToString(),
                             GeneralCommandType.Mute.ToString(),
@@ -192,33 +209,23 @@ namespace Emby.Dlna.PlayTo
                             GeneralCommandType.SetVolume.ToString(),
                             GeneralCommandType.SetAudioStreamIndex.ToString(),
                             GeneralCommandType.SetSubtitleStreamIndex.ToString()
-                        },
+                    },
 
-                        SupportsMediaControl = true,
+                    SupportsMediaControl = true,
 
-                        // xbox one creates a new uuid everytime it restarts
-                        SupportsPersistentIdentifier = (device.Properties.ModelName ?? string.Empty).IndexOf("xbox", StringComparison.OrdinalIgnoreCase) == -1
-                    });
+                    // xbox one creates a new uuid everytime it restarts
+                    SupportsPersistentIdentifier = (device.Properties.ModelName ?? string.Empty).IndexOf("xbox", StringComparison.OrdinalIgnoreCase) == -1
+                });
 
-                    _logger.Info("DLNA Session created for {0} - {1}", device.Properties.Name, device.Properties.ModelName);
-                }
-            }
-            catch (Exception ex)
-            {
-                _logger.ErrorException("Error creating PlayTo device.", ex);
-
-                lock (_nonRendererUrls)
-                {
-                    _nonRendererUrls.Add(location);
-                }
+                _logger.Info("DLNA Session created for {0} - {1}", device.Properties.Name, device.Properties.ModelName);
             }
         }
 
-        private Task<string> GetServerAddress(IpAddressInfo address)
+        private Task<string> GetServerAddress(IpAddressInfo address, CancellationToken cancellationToken)
         {
             if (address == null)
             {
-                return _appHost.GetLocalApiUrl();
+                return _appHost.GetLocalApiUrl(cancellationToken);
             }
 
             return Task.FromResult(_appHost.GetLocalApiUrl(address));
@@ -226,6 +233,15 @@ namespace Emby.Dlna.PlayTo
 
         public void Dispose()
         {
+            try
+            {
+                _disposeCancellationTokenSource.Cancel();
+            }
+            catch
+            {
+
+            }
+
             _disposed = true;
             _deviceDiscovery.DeviceDiscovered -= _deviceDiscovery_DeviceDiscovered;
             GC.SuppressFinalize(this);

+ 5 - 2
Emby.Dlna/PlayTo/SsdpHttpClient.cs

@@ -7,6 +7,7 @@ using System.IO;
 using System.Text;
 using System.Threading.Tasks;
 using System.Xml.Linq;
+using System.Threading;
 
 namespace Emby.Dlna.PlayTo
 {
@@ -89,7 +90,7 @@ namespace Emby.Dlna.PlayTo
             }
         }
 
-        public async Task<XDocument> GetDataAsync(string url)
+        public async Task<XDocument> GetDataAsync(string url, CancellationToken cancellationToken)
         {
             var options = new HttpRequestOptions
             {
@@ -99,7 +100,9 @@ namespace Emby.Dlna.PlayTo
                 BufferContent = false,
 
                 // The periodic requests may keep some devices awake
-                LogRequestAsDebug = true
+                LogRequestAsDebug = true,
+
+                CancellationToken = cancellationToken
             };
 
             options.RequestHeaders["FriendlyName.DLNA.ORG"] = FriendlyName;

+ 1 - 1
Emby.Dlna/Profiles/DishHopperJoeyProfile.cs

@@ -208,7 +208,7 @@ namespace Emby.Dlna.Profiles
             {
                 new ResponseProfile
                 {
-                    Container = "mkv,ts",
+                    Container = "mkv,ts,mpegts",
                     Type = DlnaProfileType.Video,
                     MimeType = "video/mp4"
                 }

+ 1 - 1
Emby.Dlna/Profiles/LinksysDMA2100Profile.cs

@@ -26,7 +26,7 @@ namespace Emby.Dlna.Profiles
 
                 new DirectPlayProfile
                 {
-                    Container = "avi,mp4,mkv,ts,m4v",
+                    Container = "avi,mp4,mkv,ts,mpegts,m4v",
                     Type = DlnaProfileType.Video
                 }
             };

+ 2 - 2
Emby.Dlna/Profiles/PanasonicVieraProfile.cs

@@ -72,7 +72,7 @@ namespace Emby.Dlna.Profiles
 
                 new DirectPlayProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec = "h264,mpeg2video",
                     AudioCodec = "aac,mp3,mp2",
                     Type = DlnaProfileType.Video
@@ -205,7 +205,7 @@ namespace Emby.Dlna.Profiles
                 new ResponseProfile
                 {
                     Type = DlnaProfileType.Video,
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
                     MimeType = "video/vnd.dlna.mpeg-tts"
                 },

+ 1 - 1
Emby.Dlna/Profiles/PopcornHourProfile.cs

@@ -46,7 +46,7 @@ namespace Emby.Dlna.Profiles
 
                 new DirectPlayProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     Type = DlnaProfileType.Video,
                     VideoCodec = "h264",
                     AudioCodec = "aac,ac3,eac3,mp3,mp2,pcm"

+ 5 - 2
Emby.Dlna/Profiles/SamsungSmartTvProfile.cs

@@ -12,6 +12,9 @@ namespace Emby.Dlna.Profiles
 
             EnableAlbumArtInDidl = true;
 
+            // Without this, older samsungs fail to browse
+            EnableSingleAlbumArtLimit = true;
+
             Identification = new DeviceIdentification
             {
                 ModelUrl = "samsung.com",
@@ -39,7 +42,7 @@ namespace Emby.Dlna.Profiles
                },
                new TranscodingProfile
                {
-                   Container = "ts",
+                   Container = "ts,mpegts",
                    AudioCodec = "ac3",
                    VideoCodec = "h264",
                    Type = DlnaProfileType.Video,
@@ -300,7 +303,7 @@ namespace Emby.Dlna.Profiles
                new CodecProfile
                {
                    Type = CodecType.VideoAudio,
-                   Codec = "ac3,wmav2,dca,aac,mp3,dts",
+                   Codec = "wmav2,dca,aac,mp3,dts",
 
                    Conditions = new[]
                    {

+ 3 - 3
Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs

@@ -69,7 +69,7 @@ namespace Emby.Dlna.Profiles
             {
                 new DirectPlayProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec = "mpeg1video,mpeg2video,h264",
                     AudioCodec = "ac3,aac,mp3,pcm",
                     Type = DlnaProfileType.Video
@@ -212,7 +212,7 @@ namespace Emby.Dlna.Profiles
             {
                 new ResponseProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec = "h264,mpeg4,vc1",
                     AudioCodec = "ac3,aac,mp3",
                     MimeType = "video/vnd.dlna.mpeg-tts",
@@ -236,7 +236,7 @@ namespace Emby.Dlna.Profiles
 
                 new ResponseProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     MimeType = "video/vnd.dlna.mpeg-tts",
                     Type = DlnaProfileType.Video
                 },

+ 6 - 6
Emby.Dlna/Profiles/SonyBravia2010Profile.cs

@@ -69,14 +69,14 @@ namespace Emby.Dlna.Profiles
             {
                 new DirectPlayProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec = "h264",
                     AudioCodec = "ac3,aac,mp3",
                     Type = DlnaProfileType.Video
                 },
                 new DirectPlayProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec = "mpeg1video,mpeg2video",
                     AudioCodec = "mp3,mp2",
                     Type = DlnaProfileType.Video
@@ -100,7 +100,7 @@ namespace Emby.Dlna.Profiles
             {
                 new ResponseProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec="h264",
                     AudioCodec="ac3,aac,mp3",
                     MimeType = "video/vnd.dlna.mpeg-tts",
@@ -126,7 +126,7 @@ namespace Emby.Dlna.Profiles
 
                 new ResponseProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec="h264",
                     AudioCodec="ac3,aac,mp3",
                     MimeType = "video/mpeg",
@@ -146,7 +146,7 @@ namespace Emby.Dlna.Profiles
 
                 new ResponseProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec="h264",
                     AudioCodec="ac3,aac,mp3",
                     MimeType = "video/vnd.dlna.mpeg-tts",
@@ -156,7 +156,7 @@ namespace Emby.Dlna.Profiles
 
                 new ResponseProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec="mpeg2video",
                     MimeType = "video/vnd.dlna.mpeg-tts",
                     OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",

+ 6 - 6
Emby.Dlna/Profiles/SonyBravia2011Profile.cs

@@ -66,14 +66,14 @@ namespace Emby.Dlna.Profiles
             {
                 new DirectPlayProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec = "h264",
                     AudioCodec = "ac3,aac,mp3",
                     Type = DlnaProfileType.Video
                 },
                 new DirectPlayProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec = "mpeg2video",
                     AudioCodec = "mp3",
                     Type = DlnaProfileType.Video
@@ -141,7 +141,7 @@ namespace Emby.Dlna.Profiles
             {
                 new ResponseProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec="h264",
                     AudioCodec="ac3,aac,mp3",
                     MimeType = "video/vnd.dlna.mpeg-tts",
@@ -167,7 +167,7 @@ namespace Emby.Dlna.Profiles
 
                 new ResponseProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec="h264",
                     AudioCodec="ac3,aac,mp3",
                     MimeType = "video/mpeg",
@@ -187,7 +187,7 @@ namespace Emby.Dlna.Profiles
 
                 new ResponseProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec="h264",
                     AudioCodec="ac3,aac,mp3",
                     MimeType = "video/vnd.dlna.mpeg-tts",
@@ -197,7 +197,7 @@ namespace Emby.Dlna.Profiles
 
                 new ResponseProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec="mpeg2video",
                     MimeType = "video/vnd.dlna.mpeg-tts",
                     OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",

+ 6 - 6
Emby.Dlna/Profiles/SonyBravia2012Profile.cs

@@ -66,14 +66,14 @@ namespace Emby.Dlna.Profiles
             {
                 new DirectPlayProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec = "h264",
                     AudioCodec = "ac3,aac,mp3",
                     Type = DlnaProfileType.Video
                 },
                 new DirectPlayProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec = "mpeg2video",
                     AudioCodec = "mp3,mp2",
                     Type = DlnaProfileType.Video
@@ -129,7 +129,7 @@ namespace Emby.Dlna.Profiles
             {
                 new ResponseProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec="h264",
                     AudioCodec="ac3,aac,mp3",
                     MimeType = "video/vnd.dlna.mpeg-tts",
@@ -155,7 +155,7 @@ namespace Emby.Dlna.Profiles
 
                 new ResponseProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec="h264",
                     AudioCodec="ac3,aac,mp3",
                     MimeType = "video/mpeg",
@@ -175,7 +175,7 @@ namespace Emby.Dlna.Profiles
 
                 new ResponseProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec="h264",
                     AudioCodec="ac3,aac,mp3",
                     MimeType = "video/vnd.dlna.mpeg-tts",
@@ -185,7 +185,7 @@ namespace Emby.Dlna.Profiles
 
                 new ResponseProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec="mpeg2video",
                     MimeType = "video/vnd.dlna.mpeg-tts",
                     OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",

+ 6 - 6
Emby.Dlna/Profiles/SonyBravia2013Profile.cs

@@ -65,14 +65,14 @@ namespace Emby.Dlna.Profiles
             {
                 new DirectPlayProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec = "h264",
                     AudioCodec = "ac3,eac3,aac,mp3",
                     Type = DlnaProfileType.Video
                 },
                 new DirectPlayProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec = "mpeg2video",
                     AudioCodec = "mp3,mp2",
                     Type = DlnaProfileType.Video
@@ -185,7 +185,7 @@ namespace Emby.Dlna.Profiles
             {
                 new ResponseProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec="h264",
                     AudioCodec="ac3,aac,mp3",
                     MimeType = "video/vnd.dlna.mpeg-tts",
@@ -211,7 +211,7 @@ namespace Emby.Dlna.Profiles
 
                 new ResponseProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec="h264",
                     AudioCodec="ac3,aac,mp3",
                     MimeType = "video/mpeg",
@@ -231,7 +231,7 @@ namespace Emby.Dlna.Profiles
 
                 new ResponseProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec="h264",
                     AudioCodec="ac3,aac,mp3",
                     MimeType = "video/vnd.dlna.mpeg-tts",
@@ -241,7 +241,7 @@ namespace Emby.Dlna.Profiles
 
                 new ResponseProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec="mpeg2video",
                     MimeType = "video/vnd.dlna.mpeg-tts",
                     OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",

+ 6 - 6
Emby.Dlna/Profiles/SonyBravia2014Profile.cs

@@ -65,14 +65,14 @@ namespace Emby.Dlna.Profiles
             {
                 new DirectPlayProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec = "h264",
                     AudioCodec = "ac3,eac3,aac,mp3",
                     Type = DlnaProfileType.Video
                 },
                 new DirectPlayProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec = "mpeg2video",
                     AudioCodec = "mp3,mp2",
                     Type = DlnaProfileType.Video
@@ -185,7 +185,7 @@ namespace Emby.Dlna.Profiles
             {
                 new ResponseProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec="h264",
                     AudioCodec="ac3,aac,mp3",
                     MimeType = "video/vnd.dlna.mpeg-tts",
@@ -211,7 +211,7 @@ namespace Emby.Dlna.Profiles
 
                 new ResponseProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec="h264",
                     AudioCodec="ac3,aac,mp3",
                     MimeType = "video/mpeg",
@@ -231,7 +231,7 @@ namespace Emby.Dlna.Profiles
 
                 new ResponseProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec="h264",
                     AudioCodec="ac3,aac,mp3",
                     MimeType = "video/vnd.dlna.mpeg-tts",
@@ -241,7 +241,7 @@ namespace Emby.Dlna.Profiles
 
                 new ResponseProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec="mpeg2video",
                     MimeType = "video/vnd.dlna.mpeg-tts",
                     OrgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",

+ 1 - 1
Emby.Dlna/Profiles/SonyPs3Profile.cs

@@ -49,7 +49,7 @@ namespace Emby.Dlna.Profiles
                 },
                 new DirectPlayProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     Type = DlnaProfileType.Video,
                     VideoCodec = "mpeg1video,mpeg2video,h264",
                     AudioCodec = "ac3,mp2,mp3,aac"

+ 1 - 1
Emby.Dlna/Profiles/SonyPs4Profile.cs

@@ -49,7 +49,7 @@ namespace Emby.Dlna.Profiles
                 },
                 new DirectPlayProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     Type = DlnaProfileType.Video,
                     VideoCodec = "mpeg1video,mpeg2video,h264",
                     AudioCodec = "ac3,mp2,mp3,aac"

+ 2 - 2
Emby.Dlna/Profiles/WdtvLiveProfile.cs

@@ -79,7 +79,7 @@ namespace Emby.Dlna.Profiles
 
                 new DirectPlayProfile
                 {
-                    Container = "ts,m2ts",
+                    Container = "ts,m2ts,mpegts",
                     Type = DlnaProfileType.Video,
                     VideoCodec = "mpeg1video,mpeg2video,h264,vc1",
                     AudioCodec = "ac3,eac3,dca,mp2,mp3,aac,dts"
@@ -155,7 +155,7 @@ namespace Emby.Dlna.Profiles
             {
                 new ResponseProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     OrgPn = "MPEG_TS_SD_NA",
                     Type = DlnaProfileType.Video
                 }

+ 1 - 1
Emby.Dlna/Profiles/XboxOneProfile.cs

@@ -59,7 +59,7 @@ namespace Emby.Dlna.Profiles
             {
                 new DirectPlayProfile
                 {
-                    Container = "ts",
+                    Container = "ts,mpegts",
                     VideoCodec = "h264,mpeg2video,hevc",
                     AudioCodec = "ac3,aac,mp3",
                     Type = DlnaProfileType.Video

+ 1 - 1
Emby.Dlna/Profiles/Xml/Default.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>Generic Device</Name>
   <Manufacturer>Emby</Manufacturer>
   <ManufacturerUrl>http://emby.media/</ManufacturerUrl>

+ 1 - 1
Emby.Dlna/Profiles/Xml/Denon AVR.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>Denon AVR</Name>
   <Identification>
     <FriendlyName>Denon:\[AVR:.*</FriendlyName>

+ 1 - 1
Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>DirecTV HD-DVR</Name>
   <Identification>
     <FriendlyName>^DIRECTV.*$</FriendlyName>

+ 2 - 2
Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>Dish Hopper-Joey</Name>
   <Identification>
     <Manufacturer>Echostar Technologies LLC</Manufacturer>
@@ -86,7 +86,7 @@
     </CodecProfile>
   </CodecProfiles>
   <ResponseProfiles>
-    <ResponseProfile container="mkv,ts" type="Video" mimeType="video/mp4">
+    <ResponseProfile container="mkv,ts,mpegts" type="Video" mimeType="video/mp4">
       <Conditions />
     </ResponseProfile>
   </ResponseProfiles>

+ 1 - 1
Emby.Dlna/Profiles/Xml/LG Smart TV.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>LG Smart TV</Name>
   <Identification>
     <FriendlyName>LG.*</FriendlyName>

+ 2 - 2
Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>Linksys DMA2100</Name>
   <Identification>
     <ModelName>DMA2100us</ModelName>
@@ -34,7 +34,7 @@
   <XmlRootAttributes />
   <DirectPlayProfiles>
     <DirectPlayProfile container="mp3,flac,m4a,wma" type="Audio" />
-    <DirectPlayProfile container="avi,mp4,mkv,ts,m4v" type="Video" />
+    <DirectPlayProfile container="avi,mp4,mkv,ts,mpegts,m4v" type="Video" />
   </DirectPlayProfiles>
   <TranscodingProfiles>
     <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />

+ 1 - 1
Emby.Dlna/Profiles/Xml/Marantz.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>Marantz</Name>
   <Identification>
     <Manufacturer>Marantz</Manufacturer>

+ 1 - 1
Emby.Dlna/Profiles/Xml/MediaMonkey.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>MediaMonkey</Name>
   <Identification>
     <FriendlyName>MediaMonkey</FriendlyName>

+ 3 - 3
Emby.Dlna/Profiles/Xml/Panasonic Viera.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>Panasonic Viera</Name>
   <Identification>
     <FriendlyName>VIERA</FriendlyName>
@@ -40,7 +40,7 @@
   <DirectPlayProfiles>
     <DirectPlayProfile container="mpeg,mpg" audioCodec="ac3,mp3,pcm_dvd" videoCodec="mpeg2video,mpeg4" type="Video" />
     <DirectPlayProfile container="mkv" audioCodec="aac,ac3,dca,mp3,mp2,pcm,dts" videoCodec="h264,mpeg2video" type="Video" />
-    <DirectPlayProfile container="ts" audioCodec="aac,mp3,mp2" videoCodec="h264,mpeg2video" type="Video" />
+    <DirectPlayProfile container="ts,mpegts" audioCodec="aac,mp3,mp2" videoCodec="h264,mpeg2video" type="Video" />
     <DirectPlayProfile container="mp4,m4v" audioCodec="aac,ac3,mp3,pcm" videoCodec="h264" type="Video" />
     <DirectPlayProfile container="mov" audioCodec="aac,pcm" videoCodec="h264" type="Video" />
     <DirectPlayProfile container="avi" audioCodec="pcm" videoCodec="mpeg4" type="Video" />
@@ -73,7 +73,7 @@
     </CodecProfile>
   </CodecProfiles>
   <ResponseProfiles>
-    <ResponseProfile container="ts" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
+    <ResponseProfile container="ts,mpegts" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
       <Conditions />
     </ResponseProfile>
     <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">

+ 2 - 2
Emby.Dlna/Profiles/Xml/Popcorn Hour.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>Popcorn Hour</Name>
   <Manufacturer>Emby</Manufacturer>
   <ManufacturerUrl>http://emby.media/</ManufacturerUrl>
@@ -30,7 +30,7 @@
   <XmlRootAttributes />
   <DirectPlayProfiles>
     <DirectPlayProfile container="mp4,mov,m4v" audioCodec="aac" videoCodec="h264,mpeg4" type="Video" />
-    <DirectPlayProfile container="ts" audioCodec="aac,ac3,eac3,mp3,mp2,pcm" videoCodec="h264" type="Video" />
+    <DirectPlayProfile container="ts,mpegts" audioCodec="aac,ac3,eac3,mp3,mp2,pcm" videoCodec="h264" type="Video" />
     <DirectPlayProfile container="asf,wmv" audioCodec="wmav2,wmapro" videoCodec="wmv3,vc1" type="Video" />
     <DirectPlayProfile container="avi" audioCodec="mp3,ac3,eac3,mp2,pcm" videoCodec="mpeg4,msmpeg4" type="Video" />
     <DirectPlayProfile container="mkv" audioCodec="aac,mp3,ac3,eac3,mp2,pcm" videoCodec="h264" type="Video" />

+ 4 - 4
Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>Samsung Smart TV</Name>
   <Identification>
     <ModelUrl>samsung.com</ModelUrl>
@@ -14,7 +14,7 @@
   <ModelNumber>Emby</ModelNumber>
   <ModelUrl>http://emby.media/</ModelUrl>
   <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
-  <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
+  <EnableSingleAlbumArtLimit>true</EnableSingleAlbumArtLimit>
   <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
   <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
   <AlbumArtPn>JPEG_SM</AlbumArtPn>
@@ -51,7 +51,7 @@
   </DirectPlayProfiles>
   <TranscodingProfiles>
     <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
+    <TranscodingProfile container="ts,mpegts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
     <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
   </TranscodingProfiles>
   <ContainerProfiles>
@@ -100,7 +100,7 @@
       </Conditions>
       <ApplyConditions />
     </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="ac3,wmav2,dca,aac,mp3,dts">
+    <CodecProfile type="VideoAudio" codec="wmav2,dca,aac,mp3,dts">
       <Conditions>
         <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="6" isRequired="true" />
       </Conditions>

+ 1 - 1
Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>Sharp Smart TV</Name>
   <Identification>
     <Manufacturer>Sharp</Manufacturer>

+ 1 - 1
Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>Sony Blu-ray Player 2013</Name>
   <Identification>
     <ModelNumber>BDP-2013</ModelNumber>

+ 1 - 1
Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>Sony Blu-ray Player 2014</Name>
   <Identification>
     <ModelNumber>BDP-2014</ModelNumber>

+ 1 - 1
Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>Sony Blu-ray Player 2015</Name>
   <Identification>
     <ModelNumber>BDP-2015</ModelNumber>

+ 1 - 1
Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>Sony Blu-ray Player 2016</Name>
   <Identification>
     <ModelNumber>BDP-2016</ModelNumber>

+ 4 - 4
Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>Sony Blu-ray Player</Name>
   <Identification>
     <FriendlyName>Blu-ray Disc Player</FriendlyName>
@@ -39,7 +39,7 @@
     <XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
   </XmlRootAttributes>
   <DirectPlayProfiles>
-    <DirectPlayProfile container="ts" audioCodec="ac3,aac,mp3,pcm" videoCodec="mpeg1video,mpeg2video,h264" type="Video" />
+    <DirectPlayProfile container="ts,mpegts" audioCodec="ac3,aac,mp3,pcm" videoCodec="mpeg1video,mpeg2video,h264" type="Video" />
     <DirectPlayProfile container="mpeg" audioCodec="ac3,mp3,pcm" videoCodec="mpeg1video,mpeg2video" type="Video" />
     <DirectPlayProfile container="avi,mp4,m4v" audioCodec="ac3,aac,mp3,pcm" videoCodec="mpeg4,h264" type="Video" />
     <DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" />
@@ -84,7 +84,7 @@
     </CodecProfile>
   </CodecProfiles>
   <ResponseProfiles>
-    <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264,mpeg4,vc1" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
+    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264,mpeg4,vc1" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
       <Conditions />
     </ResponseProfile>
     <ResponseProfile container="avi" type="Video" mimeType="video/mpeg">
@@ -93,7 +93,7 @@
     <ResponseProfile container="mkv" type="Video" mimeType="video/vnd.dlna.mpeg-tts">
       <Conditions />
     </ResponseProfile>
-    <ResponseProfile container="ts" type="Video" mimeType="video/vnd.dlna.mpeg-tts">
+    <ResponseProfile container="ts,mpegts" type="Video" mimeType="video/vnd.dlna.mpeg-tts">
       <Conditions />
     </ResponseProfile>
     <ResponseProfile container="mp4" type="Video" mimeType="video/mpeg">

+ 7 - 7
Emby.Dlna/Profiles/Xml/Sony Bravia (2010).xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>Sony Bravia (2010)</Name>
   <Identification>
     <FriendlyName>KDL-\d{2}[EHLNPB]X\d[01]\d.*</FriendlyName>
@@ -39,8 +39,8 @@
     <XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
   </XmlRootAttributes>
   <DirectPlayProfiles>
-    <DirectPlayProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="ts" audioCodec="mp3,mp2" videoCodec="mpeg1video,mpeg2video" type="Video" />
+    <DirectPlayProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" />
+    <DirectPlayProfile container="ts,mpegts" audioCodec="mp3,mp2" videoCodec="mpeg1video,mpeg2video" type="Video" />
     <DirectPlayProfile container="mpeg" audioCodec="mp3,mp2" videoCodec="mpeg2video,mpeg1video" type="Video" />
     <DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" />
   </DirectPlayProfiles>
@@ -106,21 +106,21 @@
     </CodecProfile>
   </CodecProfiles>
   <ResponseProfiles>
-    <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts">
+    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts">
       <Conditions>
         <ProfileCondition condition="Equals" property="PacketLength" value="192" isRequired="true" />
         <ProfileCondition condition="Equals" property="VideoTimestamp" value="Valid" isRequired="true" />
       </Conditions>
     </ResponseProfile>
-    <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg">
+    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg">
       <Conditions>
         <ProfileCondition condition="Equals" property="PacketLength" value="188" isRequired="true" />
       </Conditions>
     </ResponseProfile>
-    <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts">
+    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts">
       <Conditions />
     </ResponseProfile>
-    <ResponseProfile container="ts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
+    <ResponseProfile container="ts,mpegts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
       <Conditions />
     </ResponseProfile>
     <ResponseProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg">

+ 7 - 7
Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>Sony Bravia (2011)</Name>
   <Identification>
     <FriendlyName>KDL-\d{2}([A-Z]X\d2\d|CX400).*</FriendlyName>
@@ -39,8 +39,8 @@
     <XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
   </XmlRootAttributes>
   <DirectPlayProfiles>
-    <DirectPlayProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="ts" audioCodec="mp3" videoCodec="mpeg2video" type="Video" />
+    <DirectPlayProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" />
+    <DirectPlayProfile container="ts,mpegts" audioCodec="mp3" videoCodec="mpeg2video" type="Video" />
     <DirectPlayProfile container="mp4,m4v" audioCodec="ac3,aac,mp3" videoCodec="h264,mpeg4" type="Video" />
     <DirectPlayProfile container="mpeg" audioCodec="mp3" videoCodec="mpeg2video,mpeg1video" type="Video" />
     <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" videoCodec="wmv2,wmv3,vc1" type="Video" />
@@ -109,21 +109,21 @@
     </CodecProfile>
   </CodecProfiles>
   <ResponseProfiles>
-    <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts">
+    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts">
       <Conditions>
         <ProfileCondition condition="Equals" property="PacketLength" value="192" isRequired="true" />
         <ProfileCondition condition="Equals" property="VideoTimestamp" value="Valid" isRequired="true" />
       </Conditions>
     </ResponseProfile>
-    <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg">
+    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg">
       <Conditions>
         <ProfileCondition condition="Equals" property="PacketLength" value="188" isRequired="true" />
       </Conditions>
     </ResponseProfile>
-    <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts">
+    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts">
       <Conditions />
     </ResponseProfile>
-    <ResponseProfile container="ts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
+    <ResponseProfile container="ts,mpegts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
       <Conditions />
     </ResponseProfile>
     <ResponseProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg">

+ 7 - 7
Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>Sony Bravia (2012)</Name>
   <Identification>
     <FriendlyName>KDL-\d{2}[A-Z]X\d5(\d|G).*</FriendlyName>
@@ -39,8 +39,8 @@
     <XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
   </XmlRootAttributes>
   <DirectPlayProfiles>
-    <DirectPlayProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="ts" audioCodec="mp3,mp2" videoCodec="mpeg2video" type="Video" />
+    <DirectPlayProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" />
+    <DirectPlayProfile container="ts,mpegts" audioCodec="mp3,mp2" videoCodec="mpeg2video" type="Video" />
     <DirectPlayProfile container="mp4,m4v" audioCodec="ac3,aac,mp3,mp2" videoCodec="h264,mpeg4" type="Video" />
     <DirectPlayProfile container="avi" audioCodec="ac3,mp3" videoCodec="mpeg4" type="Video" />
     <DirectPlayProfile container="mpeg" audioCodec="mp3,mp2" videoCodec="mpeg2video,mpeg1video" type="Video" />
@@ -85,21 +85,21 @@
     </CodecProfile>
   </CodecProfiles>
   <ResponseProfiles>
-    <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts">
+    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts">
       <Conditions>
         <ProfileCondition condition="Equals" property="PacketLength" value="192" isRequired="true" />
         <ProfileCondition condition="Equals" property="VideoTimestamp" value="Valid" isRequired="true" />
       </Conditions>
     </ResponseProfile>
-    <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg">
+    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg">
       <Conditions>
         <ProfileCondition condition="Equals" property="PacketLength" value="188" isRequired="true" />
       </Conditions>
     </ResponseProfile>
-    <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts">
+    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts">
       <Conditions />
     </ResponseProfile>
-    <ResponseProfile container="ts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
+    <ResponseProfile container="ts,mpegts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
       <Conditions />
     </ResponseProfile>
     <ResponseProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg">

+ 7 - 7
Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>Sony Bravia (2013)</Name>
   <Identification>
     <FriendlyName>KDL-\d{2}[WR][5689]\d{2}A.*</FriendlyName>
@@ -39,8 +39,8 @@
     <XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
   </XmlRootAttributes>
   <DirectPlayProfiles>
-    <DirectPlayProfile container="ts" audioCodec="ac3,eac3,aac,mp3" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="ts" audioCodec="mp3,mp2" videoCodec="mpeg2video" type="Video" />
+    <DirectPlayProfile container="ts,mpegts" audioCodec="ac3,eac3,aac,mp3" videoCodec="h264" type="Video" />
+    <DirectPlayProfile container="ts,mpegts" audioCodec="mp3,mp2" videoCodec="mpeg2video" type="Video" />
     <DirectPlayProfile container="mp4,m4v" audioCodec="ac3,eac3,aac,mp3,mp2" videoCodec="h264,mpeg4" type="Video" />
     <DirectPlayProfile container="mov" audioCodec="ac3,eac3,aac,mp3,mp2" videoCodec="h264,mpeg4,mjpeg" type="Video" />
     <DirectPlayProfile container="mkv" audioCodec="ac3,eac3,aac,mp3,mp2,pcm,vorbis" videoCodec="h264,mpeg4,vp8" type="Video" />
@@ -84,21 +84,21 @@
     </CodecProfile>
   </CodecProfiles>
   <ResponseProfiles>
-    <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts">
+    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts">
       <Conditions>
         <ProfileCondition condition="Equals" property="PacketLength" value="192" isRequired="true" />
         <ProfileCondition condition="Equals" property="VideoTimestamp" value="Valid" isRequired="true" />
       </Conditions>
     </ResponseProfile>
-    <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg">
+    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg">
       <Conditions>
         <ProfileCondition condition="Equals" property="PacketLength" value="188" isRequired="true" />
       </Conditions>
     </ResponseProfile>
-    <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts">
+    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts">
       <Conditions />
     </ResponseProfile>
-    <ResponseProfile container="ts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
+    <ResponseProfile container="ts,mpegts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
       <Conditions />
     </ResponseProfile>
     <ResponseProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg">

+ 7 - 7
Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>Sony Bravia (2014)</Name>
   <Identification>
     <FriendlyName>(KDL-\d{2}W[5-9]\d{2}B|KDL-\d{2}R480|XBR-\d{2}X[89]\d{2}B|KD-\d{2}[SX][89]\d{3}B).*</FriendlyName>
@@ -39,8 +39,8 @@
     <XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
   </XmlRootAttributes>
   <DirectPlayProfiles>
-    <DirectPlayProfile container="ts" audioCodec="ac3,eac3,aac,mp3" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="ts" audioCodec="mp3,mp2" videoCodec="mpeg2video" type="Video" />
+    <DirectPlayProfile container="ts,mpegts" audioCodec="ac3,eac3,aac,mp3" videoCodec="h264" type="Video" />
+    <DirectPlayProfile container="ts,mpegts" audioCodec="mp3,mp2" videoCodec="mpeg2video" type="Video" />
     <DirectPlayProfile container="mp4,m4v" audioCodec="ac3,eac3,aac,mp3,mp2" videoCodec="h264,mpeg4" type="Video" />
     <DirectPlayProfile container="mov" audioCodec="ac3,eac3,aac,mp3,mp2" videoCodec="h264,mpeg4,mjpeg" type="Video" />
     <DirectPlayProfile container="mkv" audioCodec="ac3,eac3,aac,mp3,mp2,pcm,vorbis" videoCodec="h264,mpeg4,vp8" type="Video" />
@@ -84,21 +84,21 @@
     </CodecProfile>
   </CodecProfiles>
   <ResponseProfiles>
-    <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts">
+    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts">
       <Conditions>
         <ProfileCondition condition="Equals" property="PacketLength" value="192" isRequired="true" />
         <ProfileCondition condition="Equals" property="VideoTimestamp" value="Valid" isRequired="true" />
       </Conditions>
     </ResponseProfile>
-    <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg">
+    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg">
       <Conditions>
         <ProfileCondition condition="Equals" property="PacketLength" value="188" isRequired="true" />
       </Conditions>
     </ResponseProfile>
-    <ResponseProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts">
+    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts">
       <Conditions />
     </ResponseProfile>
-    <ResponseProfile container="ts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
+    <ResponseProfile container="ts,mpegts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
       <Conditions />
     </ResponseProfile>
     <ResponseProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg">

+ 2 - 2
Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>Sony PlayStation 3</Name>
   <Identification>
     <FriendlyName>PLAYSTATION 3</FriendlyName>
@@ -38,7 +38,7 @@
   <XmlRootAttributes />
   <DirectPlayProfiles>
     <DirectPlayProfile container="avi" audioCodec="mp2,mp3" videoCodec="mpeg4" type="Video" />
-    <DirectPlayProfile container="ts" audioCodec="ac3,mp2,mp3,aac" videoCodec="mpeg1video,mpeg2video,h264" type="Video" />
+    <DirectPlayProfile container="ts,mpegts" audioCodec="ac3,mp2,mp3,aac" videoCodec="mpeg1video,mpeg2video,h264" type="Video" />
     <DirectPlayProfile container="mpeg" audioCodec="mp2" videoCodec="mpeg1video,mpeg2video" type="Video" />
     <DirectPlayProfile container="mp4" audioCodec="aac,ac3" videoCodec="h264,mpeg4" type="Video" />
     <DirectPlayProfile container="aac,mp3,wav" type="Audio" />

+ 2 - 2
Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>Sony PlayStation 4</Name>
   <Identification>
     <FriendlyName>PLAYSTATION 4</FriendlyName>
@@ -38,7 +38,7 @@
   <XmlRootAttributes />
   <DirectPlayProfiles>
     <DirectPlayProfile container="avi" audioCodec="mp2,mp3" videoCodec="mpeg4" type="Video" />
-    <DirectPlayProfile container="ts" audioCodec="ac3,mp2,mp3,aac" videoCodec="mpeg1video,mpeg2video,h264" type="Video" />
+    <DirectPlayProfile container="ts,mpegts" audioCodec="ac3,mp2,mp3,aac" videoCodec="mpeg1video,mpeg2video,h264" type="Video" />
     <DirectPlayProfile container="mpeg" audioCodec="mp2" videoCodec="mpeg1video,mpeg2video" type="Video" />
     <DirectPlayProfile container="mp4,mkv,m4v" audioCodec="aac,ac3" videoCodec="h264,mpeg4" type="Video" />
     <DirectPlayProfile container="aac,mp3,wav" type="Audio" />

+ 3 - 3
Emby.Dlna/Profiles/Xml/WDTV Live.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>WDTV Live</Name>
   <Identification>
     <ModelName>WD TV</ModelName>
@@ -39,7 +39,7 @@
     <DirectPlayProfile container="avi" audioCodec="ac3,eac3,dca,mp2,mp3,pcm,dts" videoCodec="mpeg1video,mpeg2video,mpeg4,h264,vc1" type="Video" />
     <DirectPlayProfile container="mpeg" audioCodec="ac3,eac3,dca,mp2,mp3,pcm,dts" videoCodec="mpeg1video,mpeg2video" type="Video" />
     <DirectPlayProfile container="mkv" audioCodec="ac3,eac3,dca,aac,mp2,mp3,pcm,dts" videoCodec="mpeg1video,mpeg2video,mpeg4,h264,vc1" type="Video" />
-    <DirectPlayProfile container="ts,m2ts" audioCodec="ac3,eac3,dca,mp2,mp3,aac,dts" videoCodec="mpeg1video,mpeg2video,h264,vc1" type="Video" />
+    <DirectPlayProfile container="ts,m2ts,mpegts" audioCodec="ac3,eac3,dca,mp2,mp3,aac,dts" videoCodec="mpeg1video,mpeg2video,h264,vc1" type="Video" />
     <DirectPlayProfile container="mp4,mov,m4v" audioCodec="ac3,eac3,aac,mp2,mp3,dca,dts" videoCodec="h264,mpeg4" type="Video" />
     <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro" videoCodec="vc1" type="Video" />
     <DirectPlayProfile container="asf" audioCodec="mp2,ac3" videoCodec="mpeg2video" type="Video" />
@@ -80,7 +80,7 @@
     </CodecProfile>
   </CodecProfiles>
   <ResponseProfiles>
-    <ResponseProfile container="ts" type="Video" orgPn="MPEG_TS_SD_NA">
+    <ResponseProfile container="ts,mpegts" type="Video" orgPn="MPEG_TS_SD_NA">
       <Conditions />
     </ResponseProfile>
   </ResponseProfiles>

+ 2 - 2
Emby.Dlna/Profiles/Xml/Xbox One.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>Xbox One</Name>
   <Identification>
     <ModelName>Xbox One</ModelName>
@@ -36,7 +36,7 @@
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
   <XmlRootAttributes />
   <DirectPlayProfiles>
-    <DirectPlayProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264,mpeg2video,hevc" type="Video" />
+    <DirectPlayProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264,mpeg2video,hevc" type="Video" />
     <DirectPlayProfile container="avi" audioCodec="ac3,mp3" videoCodec="mpeg4" type="Video" />
     <DirectPlayProfile container="avi" audioCodec="aac" videoCodec="h264" type="Video" />
     <DirectPlayProfile container="mp4,mov,mkv,m4v" audioCodec="aac,ac3" videoCodec="h264,mpeg4,mpeg2video,hevc" type="Video" />

+ 1 - 1
Emby.Dlna/Profiles/Xml/foobar2000.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Name>foobar2000</Name>
   <Identification>
     <FriendlyName>foobar</FriendlyName>

+ 2 - 0
Emby.Drawing.ImageMagick/ImageMagickEncoder.cs

@@ -173,6 +173,8 @@ namespace Emby.Drawing.ImageMagick
                     originalImage.CurrentImage.CompressionQuality = quality;
                     originalImage.CurrentImage.StripImage();
 
+                    _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(outputPath));
+
                     originalImage.SaveImage(outputPath);
                 }
             }

+ 0 - 3
Emby.Drawing.Skia/Emby.Drawing.Skia.csproj

@@ -59,9 +59,6 @@
     <Compile Include="StripCollageBuilder.cs" />
     <Compile Include="UnplayedCountIndicator.cs" />
   </ItemGroup>
-  <ItemGroup>
-    <EmbeddedResource Include="fonts\robotoregular.ttf" />
-  </ItemGroup>
   <ItemGroup>
     <Reference Include="SkiaSharp, Version=1.58.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
       <HintPath>..\packages\SkiaSharp.1.58.1\lib\portable-net45+win8+wpa81+wp8\SkiaSharp.dll</HintPath>

+ 11 - 37
Emby.Drawing.Skia/PlayedIndicatorDrawer.cs

@@ -15,7 +15,6 @@ namespace Emby.Drawing.Skia
 {
     public class PlayedIndicatorDrawer
     {
-        private const int FontSize = 42;
         private const int OffsetFromTopRightCorner = 38;
 
         private readonly IApplicationPaths _appPaths;
@@ -44,48 +43,23 @@ namespace Emby.Drawing.Skia
             {
                 paint.Color = new SKColor(255, 255, 255, 255);
                 paint.Style = SKPaintStyle.Fill;
-                paint.Typeface = SKTypeface.FromFile(await DownloadFont("webdings.ttf", "https://github.com/MediaBrowser/Emby.Resources/raw/master/fonts/webdings.ttf",
-                    _appPaths, _iHttpClient, _fileSystem).ConfigureAwait(false));
-                paint.TextSize = FontSize;
-                paint.IsAntialias = true;
-
-                canvas.DrawText("a", (float)x-20, OffsetFromTopRightCorner + 12, paint);
-            }
-        }
-
-        internal static string ExtractFont(string name, IApplicationPaths paths, IFileSystem fileSystem)
-        {
-            var filePath = Path.Combine(paths.ProgramDataPath, "fonts", name);
-
-            if (fileSystem.FileExists(filePath))
-            {
-                return filePath;
-            }
 
-            var namespacePath = typeof(PlayedIndicatorDrawer).Namespace + ".fonts." + name;
-            var tempPath = Path.Combine(paths.TempDirectory, Guid.NewGuid().ToString("N") + ".ttf");
-            fileSystem.CreateDirectory(fileSystem.GetDirectoryName(tempPath));
+                paint.TextSize = 30;
+                paint.IsAntialias = true;
 
-            using (var stream = typeof(PlayedIndicatorDrawer).GetTypeInfo().Assembly.GetManifestResourceStream(namespacePath))
-            {
-                using (var fileStream = fileSystem.GetFileStream(tempPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
-                {
-                    stream.CopyTo(fileStream);
-                }
-            }
+                var text = "✔️";
+                var emojiChar = StringUtilities.GetUnicodeCharacterCode(text, SKTextEncoding.Utf32);
+                // or:
+                //var emojiChar = 0x1F680;
 
-            fileSystem.CreateDirectory(fileSystem.GetDirectoryName(filePath));
+                // ask the font manager for a font with that character
+                var fontManager = SKFontManager.Default;
+                var emojiTypeface = fontManager.MatchCharacter(emojiChar);
 
-            try
-            {
-                fileSystem.CopyFile(tempPath, filePath, false);
-            }
-            catch (IOException)
-            {
+                paint.Typeface = emojiTypeface;
 
+                canvas.DrawText(text, (float)x-20, OffsetFromTopRightCorner + 12, paint);
             }
-
-            return tempPath;
         }
 
         internal static async Task<string> DownloadFont(string name, string url, IApplicationPaths paths, IHttpClient httpClient, IFileSystem fileSystem)

+ 2 - 0
Emby.Drawing.Skia/SkiaEncoder.cs

@@ -528,6 +528,7 @@ namespace Emby.Drawing.Skia
                     // If all we're doing is resizing then we can stop now
                     if (!hasBackgroundColor && !hasForegroundColor && blur == 0 && !hasIndicator)
                     {
+                        _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(outputPath));
                         using (var outputStream = new SKFileWStream(outputPath))
                         {
                             resizedBitmap.Encode(outputStream, skiaOutputFormat, quality);
@@ -580,6 +581,7 @@ namespace Emby.Drawing.Skia
                                 DrawIndicator(canvas, width, height, options);
                             }
 
+                            _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(outputPath));
                             using (var outputStream = new SKFileWStream(outputPath))
                             {
                                 saveBitmap.Encode(outputStream, skiaOutputFormat, quality);

+ 1 - 1
Emby.Drawing.Skia/UnplayedCountIndicator.cs

@@ -40,7 +40,7 @@ namespace Emby.Drawing.Skia
             {
                 paint.Color = new SKColor(255, 255, 255, 255);
                 paint.Style = SKPaintStyle.Fill;
-                paint.Typeface = SKTypeface.FromFile(PlayedIndicatorDrawer.ExtractFont("robotoregular.ttf", _appPaths, _fileSystem));
+
                 paint.TextSize = 24;
                 paint.IsAntialias = true;
 

+ 73 - 31
Emby.Drawing/ImageProcessor.cs

@@ -220,7 +220,7 @@ namespace Emby.Drawing
                     Type = originalImage.Type,
                     Path = originalImagePath
 
-                }, requiresTransparency, item, options.ImageIndex, options.Enhancers).ConfigureAwait(false);
+                }, requiresTransparency, item, options.ImageIndex, options.Enhancers, CancellationToken.None).ConfigureAwait(false);
 
                 originalImagePath = tuple.Item1;
                 dateModified = tuple.Item2;
@@ -256,31 +256,29 @@ namespace Emby.Drawing
             var outputFormat = GetOutputFormat(options.SupportedOutputFormats, requiresTransparency);
             var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.Blur, options.BackgroundColor, options.ForegroundLayer);
 
+            CheckDisposed();
+
+            var lockInfo = GetLock(cacheFilePath);
+
+            await lockInfo.Lock.WaitAsync().ConfigureAwait(false);
+
             try
             {
-                CheckDisposed();
-
                 if (!_fileSystem.FileExists(cacheFilePath))
                 {
-                    var tmpPath = Path.ChangeExtension(Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString("N")), Path.GetExtension(cacheFilePath));
-                    _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(tmpPath));
-
                     if (options.CropWhiteSpace && !SupportsTransparency(originalImagePath))
                     {
                         options.CropWhiteSpace = false;
                     }
 
-                    var resultPath = _imageEncoder.EncodeImage(originalImagePath, dateModified, tmpPath, autoOrient, orientation, quality, options, outputFormat);
+                    var resultPath = _imageEncoder.EncodeImage(originalImagePath, dateModified, cacheFilePath, autoOrient, orientation, quality, options, outputFormat);
 
                     if (string.Equals(resultPath, originalImagePath, StringComparison.OrdinalIgnoreCase))
                     {
                         return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
                     }
 
-                    _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(cacheFilePath));
-                    CopyFile(tmpPath, cacheFilePath);
-
-                    return new Tuple<string, string, DateTime>(tmpPath, GetMimeType(outputFormat, cacheFilePath), _fileSystem.GetLastWriteTimeUtc(tmpPath));
+                    return new Tuple<string, string, DateTime>(cacheFilePath, GetMimeType(outputFormat, cacheFilePath), _fileSystem.GetLastWriteTimeUtc(cacheFilePath));
                 }
 
                 return new Tuple<string, string, DateTime>(cacheFilePath, GetMimeType(outputFormat, cacheFilePath), _fileSystem.GetLastWriteTimeUtc(cacheFilePath));
@@ -302,6 +300,10 @@ namespace Emby.Drawing
                 // Just spit out the original file if all the options are default
                 return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
             }
+            finally
+            {
+                ReleaseLock(cacheFilePath, lockInfo);
+            }
         }
 
         private ImageFormat GetOutputFormat(ImageFormat[] clientSupportedFormats, bool requiresTransparency)
@@ -667,7 +669,7 @@ namespace Emby.Drawing
 
             var inputImageSupportsTransparency = SupportsTransparency(imageInfo.Path);
 
-            var result = await GetEnhancedImage(imageInfo, inputImageSupportsTransparency, item, imageIndex, enhancers);
+            var result = await GetEnhancedImage(imageInfo, inputImageSupportsTransparency, item, imageIndex, enhancers, CancellationToken.None);
 
             return result.Item1;
         }
@@ -676,7 +678,8 @@ namespace Emby.Drawing
             bool inputImageSupportsTransparency,
             IHasMetadata item,
             int imageIndex,
-            List<IImageEnhancer> enhancers)
+            List<IImageEnhancer> enhancers,
+            CancellationToken cancellationToken)
         {
             var originalImagePath = image.Path;
             var dateModified = image.DateModified;
@@ -687,7 +690,7 @@ namespace Emby.Drawing
                 var cacheGuid = GetImageCacheTag(item, image, enhancers);
 
                 // Enhance if we have enhancers
-                var ehnancedImageInfo = await GetEnhancedImageInternal(originalImagePath, item, imageType, imageIndex, enhancers, cacheGuid).ConfigureAwait(false);
+                var ehnancedImageInfo = await GetEnhancedImageInternal(originalImagePath, item, imageType, imageIndex, enhancers, cacheGuid, cancellationToken).ConfigureAwait(false);
 
                 var ehnancedImagePath = ehnancedImageInfo.Item1;
 
@@ -727,7 +730,8 @@ namespace Emby.Drawing
             ImageType imageType,
             int imageIndex,
             List<IImageEnhancer> supportedEnhancers,
-            string cacheGuid)
+            string cacheGuid,
+            CancellationToken cancellationToken)
         {
             if (string.IsNullOrEmpty(originalImagePath))
             {
@@ -755,29 +759,28 @@ namespace Emby.Drawing
 
             var enhancedImagePath = GetCachePath(EnhancedImageCachePath, cacheGuid + cacheExtension);
 
-            // Check again in case of contention
-            if (_fileSystem.FileExists(enhancedImagePath))
-            {
-                return new Tuple<string, bool>(enhancedImagePath, treatmentRequiresTransparency);
-            }
+            var lockInfo = GetLock(enhancedImagePath);
 
-            _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(enhancedImagePath));
-
-            var tmpPath = Path.Combine(_appPaths.TempDirectory, Path.ChangeExtension(Guid.NewGuid().ToString(), Path.GetExtension(enhancedImagePath)));
-            _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(tmpPath));
-
-            await ExecuteImageEnhancers(supportedEnhancers, originalImagePath, tmpPath, item, imageType, imageIndex).ConfigureAwait(false);
+            await lockInfo.Lock.WaitAsync(cancellationToken).ConfigureAwait(false);
 
             try
             {
-                _fileSystem.CopyFile(tmpPath, enhancedImagePath, true);
+                // Check again in case of contention
+                if (_fileSystem.FileExists(enhancedImagePath))
+                {
+                    return new Tuple<string, bool>(enhancedImagePath, treatmentRequiresTransparency);
+                }
+
+                _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(enhancedImagePath));
+
+                await ExecuteImageEnhancers(supportedEnhancers, originalImagePath, enhancedImagePath, item, imageType, imageIndex).ConfigureAwait(false);
+
+                return new Tuple<string, bool>(enhancedImagePath, treatmentRequiresTransparency);
             }
-            catch
+            finally
             {
-
+                ReleaseLock(enhancedImagePath, lockInfo);
             }
-
-            return new Tuple<string, bool>(tmpPath, treatmentRequiresTransparency);
         }
 
         /// <summary>
@@ -896,6 +899,45 @@ namespace Emby.Drawing
             return list;
         }
 
+        private Dictionary<string, LockInfo> _locks = new Dictionary<string, LockInfo>();
+        private class LockInfo
+        {
+            public SemaphoreSlim Lock = new SemaphoreSlim(1, 1);
+            public int Count = 1;
+        }
+        private LockInfo GetLock(string key)
+        {
+            lock (_locks)
+            {
+                LockInfo info;
+                if (_locks.TryGetValue(key, out info))
+                {
+                    info.Count++;
+                }
+                else
+                {
+                    info = new LockInfo();
+                    _locks[key] = info;
+                }
+                return info;
+            }
+        }
+
+        private void ReleaseLock(string key, LockInfo info)
+        {
+            info.Lock.Release();
+
+            lock (_locks)
+            {
+                info.Count--;
+                if (info.Count <= 0)
+                {
+                    _locks.Remove(key);
+                    info.Lock.Dispose();
+                }
+            }
+        }
+
         private bool _disposed;
         public void Dispose()
         {

+ 96 - 29
Emby.Server.Implementations/ApplicationHost.cs

@@ -148,6 +148,34 @@ namespace Emby.Server.Implementations
             }
         }
 
+        public virtual bool CanLaunchWebBrowser
+        {
+            get
+            {
+                if (!Environment.UserInteractive)
+                {
+                    return false;
+                }
+
+                if (StartupOptions.ContainsOption("-service"))
+                {
+                    return false;
+                }
+
+                if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows)
+                {
+                    return true;
+                }
+
+                if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.OSX)
+                {
+                    return true;
+                }
+
+                return false;
+            }
+        }
+
         /// <summary>
         /// Occurs when [has pending restart changed].
         /// </summary>
@@ -361,7 +389,7 @@ namespace Emby.Server.Implementations
 
         protected IAuthService AuthService { get; private set; }
 
-        protected readonly StartupOptions StartupOptions;
+        public StartupOptions StartupOptions { get; private set; }
         protected readonly string ReleaseAssetFilename;
 
         internal IPowerManagement PowerManagement { get; private set; }
@@ -393,6 +421,7 @@ namespace Emby.Server.Implementations
             ISystemEvents systemEvents,
             INetworkManager networkManager)
         {
+
             // hack alert, until common can target .net core
             BaseExtensions.CryptographyProvider = CryptographyProvider;
 
@@ -423,6 +452,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;
@@ -1901,9 +1937,9 @@ namespace Emby.Server.Implementations
         /// Gets the system status.
         /// </summary>
         /// <returns>SystemInfo.</returns>
-        public async Task<SystemInfo> GetSystemInfo()
+        public async Task<SystemInfo> GetSystemInfo(CancellationToken cancellationToken)
         {
-            var localAddress = await GetLocalApiUrl().ConfigureAwait(false);
+            var localAddress = await GetLocalApiUrl(cancellationToken).ConfigureAwait(false);
 
             return new SystemInfo
             {
@@ -1928,6 +1964,7 @@ namespace Emby.Server.Implementations
                 OperatingSystemDisplayName = OperatingSystemDisplayName,
                 CanSelfRestart = CanSelfRestart,
                 CanSelfUpdate = CanSelfUpdate,
+                CanLaunchWebBrowser = CanLaunchWebBrowser,
                 WanAddress = ConnectManager.WanApiAddress,
                 HasUpdateAvailable = HasUpdateAvailable,
                 SupportsAutoRunAtStartup = SupportsAutoRunAtStartup,
@@ -1942,6 +1979,21 @@ namespace Emby.Server.Implementations
             };
         }
 
+        public async Task<PublicSystemInfo> GetPublicSystemInfo(CancellationToken cancellationToken)
+        {
+            var localAddress = await GetLocalApiUrl(cancellationToken).ConfigureAwait(false);
+
+            return new PublicSystemInfo
+            {
+                Version = ApplicationVersion.ToString(),
+                Id = SystemId,
+                OperatingSystem = EnvironmentInfo.OperatingSystem.ToString(),
+                WanAddress = ConnectManager.WanApiAddress,
+                ServerName = FriendlyName,
+                LocalAddress = localAddress
+            };
+        }
+
         public bool EnableHttps
         {
             get
@@ -1955,14 +2007,14 @@ namespace Emby.Server.Implementations
             get { return Certificate != null || ServerConfigurationManager.Configuration.IsBehindProxy; }
         }
 
-        public async Task<string> GetLocalApiUrl()
+        public async Task<string> GetLocalApiUrl(CancellationToken cancellationToken)
         {
             try
             {
                 // Return the first matched address, if found, or the first known local address
-                var address = (await GetLocalIpAddresses().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 +2046,12 @@ namespace Emby.Server.Implementations
                 HttpPort.ToString(CultureInfo.InvariantCulture));
         }
 
-        public async Task<List<IpAddressInfo>> GetLocalIpAddresses()
+        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 +2063,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).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,8 +2110,7 @@ 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)
+        private async Task<bool> IsIpAddressValidAsync(IpAddressInfo address, CancellationToken cancellationToken)
         {
             if (address.Equals(IpAddressInfo.Loopback) ||
                 address.Equals(IpAddressInfo.IPv6Loopback))
@@ -2054,12 +2121,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))
             {
@@ -2075,7 +2136,9 @@ namespace Emby.Server.Implementations
                     LogErrors = false,
                     LogRequest = false,
                     TimeoutMs = 30000,
-                    BufferContent = false
+                    BufferContent = false,
+
+                    CancellationToken = cancellationToken
 
                 }, "POST").ConfigureAwait(false))
                 {
@@ -2085,14 +2148,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;
@@ -2317,12 +2385,11 @@ namespace Emby.Server.Implementations
             }
         }
 
-        public void LaunchUrl(string url)
+        public virtual void LaunchUrl(string url)
         {
-            if (EnvironmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows &&
-                EnvironmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.OSX)
+            if (!CanLaunchWebBrowser)
             {
-                throw new NotImplementedException();
+                throw new NotSupportedException();
             }
 
             var process = ProcessFactory.Create(new ProcessOptions

+ 18 - 0
Emby.Server.Implementations/Archiving/ZipClient.cs

@@ -89,6 +89,24 @@ namespace Emby.Server.Implementations.Archiving
             }
         }
 
+        public void ExtractFirstFileFromGz(Stream source, string targetPath, string defaultFileName)
+        {
+            using (var reader = GZipReader.Open(source))
+            {
+                if (reader.MoveToNextEntry())
+                {
+                    var entry = reader.Entry;
+
+                    var filename = entry.Key;
+                    if (string.IsNullOrWhiteSpace(filename))
+                    {
+                        filename = defaultFileName;
+                    }
+                    reader.WriteEntryToFile(Path.Combine(targetPath, filename));
+                }
+            }
+        }
+
         /// <summary>
         /// Extracts all from7z.
         /// </summary>

+ 1 - 1
Emby.Server.Implementations/Browser/BrowserLauncher.cs

@@ -61,7 +61,7 @@ namespace Emby.Server.Implementations.Browser
             {
                 appHost.LaunchUrl(url);
             }
-            catch (NotImplementedException)
+            catch (NotSupportedException)
             {
                 
             }

+ 8 - 2
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);
@@ -5264,7 +5264,13 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
                 ItemIds = query.ItemIds,
                 TopParentIds = query.TopParentIds,
                 ParentId = query.ParentId,
-                IsPlayed = query.IsPlayed
+                IsPlayed = query.IsPlayed,
+                IsAiring = query.IsAiring,
+                IsMovie = query.IsMovie,
+                IsSports = query.IsSports,
+                IsKids = query.IsKids,
+                IsNews = query.IsNews,
+                IsSeries = query.IsSeries
             };
 
             var innerWhereClauses = GetWhereClauses(innerQuery, null);

+ 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" />

+ 2 - 1
Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs

@@ -13,6 +13,7 @@ using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Threading;
 using Mono.Nat;
 using MediaBrowser.Model.Extensions;
+using System.Threading;
 
 namespace Emby.Server.Implementations.EntryPoints
 {
@@ -158,7 +159,7 @@ namespace Emby.Server.Implementations.EntryPoints
 
                 try
                 {
-                    var localAddressString = await _appHost.GetLocalApiUrl().ConfigureAwait(false);
+                    var localAddressString = await _appHost.GetLocalApiUrl(CancellationToken.None).ConfigureAwait(false);
 
                     Uri uri;
                     if (Uri.TryCreate(localAddressString, UriKind.Absolute, out uri))

+ 33 - 3
Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs

@@ -260,7 +260,7 @@ namespace Emby.Server.Implementations.EntryPoints
                     LibraryUpdateTimer.Change(LibraryUpdateDuration, Timeout.Infinite);
                 }
 
-                var parent = e.Item.GetParent() as Folder;
+                var parent = e.Parent as Folder;
                 if (parent != null)
                 {
                     _foldersRemovedFrom.Add(parent);
@@ -363,10 +363,16 @@ namespace Emby.Server.Implementations.EntryPoints
         /// <param name="foldersRemovedFrom">The folders removed from.</param>
         /// <param name="userId">The user id.</param>
         /// <returns>LibraryUpdateInfo.</returns>
-        private LibraryUpdateInfo GetLibraryUpdateInfo(IEnumerable<BaseItem> itemsAdded, IEnumerable<BaseItem> itemsUpdated, IEnumerable<BaseItem> itemsRemoved, IEnumerable<Folder> foldersAddedTo, IEnumerable<Folder> foldersRemovedFrom, Guid userId)
+        private LibraryUpdateInfo GetLibraryUpdateInfo(List<BaseItem> itemsAdded, List<BaseItem> itemsUpdated, List<BaseItem> itemsRemoved, List<Folder> foldersAddedTo, List<Folder> foldersRemovedFrom, Guid userId)
         {
             var user = _userManager.GetUserById(userId);
 
+            var newAndRemoved = new List<BaseItem>();
+            newAndRemoved.AddRange(foldersAddedTo);
+            newAndRemoved.AddRange(foldersRemovedFrom);
+
+            var allUserRootChildren = _libraryManager.GetUserRootFolder().GetChildren(user, true).OfType<Folder>().ToList();
+
             return new LibraryUpdateInfo
             {
                 ItemsAdded = itemsAdded.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToArray(),
@@ -377,7 +383,9 @@ namespace Emby.Server.Implementations.EntryPoints
 
                 FoldersAddedTo = foldersAddedTo.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToArray(),
 
-                FoldersRemovedFrom = foldersRemovedFrom.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToArray()
+                FoldersRemovedFrom = foldersRemovedFrom.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToArray(),
+
+                CollectionFolders = GetTopParentIds(newAndRemoved, user, allUserRootChildren).ToArray()
             };
         }
 
@@ -396,6 +404,28 @@ namespace Emby.Server.Implementations.EntryPoints
             return item.SourceType == SourceType.Library;
         }
 
+        private IEnumerable<string> GetTopParentIds(List<BaseItem> items, User user, List<Folder> allUserRootChildren)
+        {
+            var list = new List<string>();
+
+            foreach (var item in items)
+            {
+                // If the physical root changed, return the user root
+                if (item is AggregateFolder)
+                {
+                    continue;
+                }
+
+                var collectionFolders = _libraryManager.GetCollectionFolders(item, allUserRootChildren);
+                foreach (var folder in allUserRootChildren)
+                {
+                    list.Add(folder.Id.ToString("N"));
+                }
+            }
+
+            return list.Distinct(StringComparer.Ordinal);
+        }
+
         /// <summary>
         /// Translates the physical item to user library.
         /// </summary>

+ 19 - 14
Emby.Server.Implementations/EntryPoints/StartupWizard.cs

@@ -3,6 +3,7 @@ using Emby.Server.Implementations.Browser;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Plugins;
 using MediaBrowser.Model.Logging;
+using MediaBrowser.Controller.Configuration;
 
 namespace Emby.Server.Implementations.EntryPoints
 {
@@ -20,15 +21,13 @@ namespace Emby.Server.Implementations.EntryPoints
         /// </summary>
         private readonly ILogger _logger;
 
-        /// <summary>
-        /// Initializes a new instance of the <see cref="StartupWizard" /> class.
-        /// </summary>
-        /// <param name="appHost">The app host.</param>
-        /// <param name="logger">The logger.</param>
-        public StartupWizard(IServerApplicationHost appHost, ILogger logger)
+        private IServerConfigurationManager _config;
+
+        public StartupWizard(IServerApplicationHost appHost, ILogger logger, IServerConfigurationManager config)
         {
             _appHost = appHost;
             _logger = logger;
+            _config = config;
         }
 
         /// <summary>
@@ -36,18 +35,24 @@ namespace Emby.Server.Implementations.EntryPoints
         /// </summary>
         public void Run()
         {
+            if (!_appHost.CanLaunchWebBrowser)
+            {
+                return;
+            }
+
             if (_appHost.IsFirstRun)
             {
-                LaunchStartupWizard();
+                BrowserLauncher.OpenDashboardPage("wizardstart.html", _appHost);
             }
-        }
+            else if (_config.Configuration.IsStartupWizardCompleted && _config.Configuration.AutoRunWebApp)
+            {
+                var options = ((ApplicationHost)_appHost).StartupOptions;
 
-        /// <summary>
-        /// Launches the startup wizard.
-        /// </summary>
-        private void LaunchStartupWizard()
-        {
-            BrowserLauncher.OpenDashboardPage("wizardstart.html", _appHost);
+                if (!options.ContainsOption("-noautorunwebapp"))
+                {
+                    BrowserLauncher.OpenDashboardPage("index.html", _appHost);
+                }
+            }
         }
 
         /// <summary>

+ 4 - 3
Emby.Server.Implementations/IO/LibraryMonitor.cs

@@ -237,7 +237,7 @@ namespace Emby.Server.Implementations.IO
         /// <param name="e">The <see cref="ItemChangeEventArgs"/> instance containing the event data.</param>
         void LibraryManager_ItemRemoved(object sender, ItemChangeEventArgs e)
         {
-            if (e.Item.GetParent() is AggregateFolder)
+            if (e.Parent is AggregateFolder)
             {
                 StopWatchingPath(e.Item.Path);
             }
@@ -250,7 +250,7 @@ namespace Emby.Server.Implementations.IO
         /// <param name="e">The <see cref="ItemChangeEventArgs"/> instance containing the event data.</param>
         void LibraryManager_ItemAdded(object sender, ItemChangeEventArgs e)
         {
-            if (e.Item.GetParent() is AggregateFolder)
+            if (e.Parent is AggregateFolder)
             {
                 StartWatching(e.Item);
             }
@@ -320,7 +320,8 @@ namespace Emby.Server.Implementations.IO
                         IncludeSubdirectories = true
                     };
 
-                    if (_environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows)
+                    if (_environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows ||
+                    _environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.OSX)
                     {
                         newWatcher.InternalBufferSize = 32767;
                     }

+ 15 - 0
Emby.Server.Implementations/IO/ManagedFileSystem.cs

@@ -473,6 +473,11 @@ namespace Emby.Server.Implementations.IO
 
         public void SetHidden(string path, bool isHidden)
         {
+            if (_environmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows)
+            {
+                return;
+            }
+
             if (_sharpCifsFileSystem.IsEnabledForPath(path))
             {
                 _sharpCifsFileSystem.SetHidden(path, isHidden);
@@ -498,6 +503,11 @@ namespace Emby.Server.Implementations.IO
 
         public void SetReadOnly(string path, bool isReadOnly)
         {
+            if (_environmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows)
+            {
+                return;
+            }
+
             if (_sharpCifsFileSystem.IsEnabledForPath(path))
             {
                 _sharpCifsFileSystem.SetReadOnly(path, isReadOnly);
@@ -523,6 +533,11 @@ namespace Emby.Server.Implementations.IO
 
         public void SetAttributes(string path, bool isHidden, bool isReadOnly)
         {
+            if (_environmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows)
+            {
+                return;
+            }
+
             if (_sharpCifsFileSystem.IsEnabledForPath(path))
             {
                 _sharpCifsFileSystem.SetAttributes(path, isHidden, isReadOnly);

+ 0 - 11
Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs

@@ -141,17 +141,6 @@ namespace Emby.Server.Implementations.Library
                         return true;
                     }
                 }
-                
-                // Ignore samples
-                var sampleFilename = " " + filename.Replace(".", " ", StringComparison.OrdinalIgnoreCase)
-                    .Replace("-", " ", StringComparison.OrdinalIgnoreCase)
-                    .Replace("_", " ", StringComparison.OrdinalIgnoreCase)
-                    .Replace("!", " ", StringComparison.OrdinalIgnoreCase);
-
-                if (sampleFilename.IndexOf(" sample ", StringComparison.OrdinalIgnoreCase) != -1)
-                {
-                    return true;
-                }
             }
 
             return false;

+ 15 - 6
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -443,7 +443,7 @@ namespace Emby.Server.Implementations.Library
             BaseItem removed;
             _libraryItemsCache.TryRemove(item.Id, out removed);
 
-            ReportItemRemoved(item);
+            ReportItemRemoved(item, parent);
         }
 
         private IEnumerable<string> GetMetadataPaths(BaseItem item, IEnumerable<BaseItem> children)
@@ -1804,7 +1804,7 @@ namespace Emby.Server.Implementations.Library
         /// <returns>Task.</returns>
         public void CreateItem(BaseItem item, CancellationToken cancellationToken)
         {
-            CreateItems(new[] { item }, cancellationToken);
+            CreateItems(new[] { item }, item.GetParent(), cancellationToken);
         }
 
         /// <summary>
@@ -1813,7 +1813,7 @@ namespace Emby.Server.Implementations.Library
         /// <param name="items">The items.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        public void CreateItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken)
+        public void CreateItems(IEnumerable<BaseItem> items, BaseItem parent, CancellationToken cancellationToken)
         {
             var list = items.ToList();
 
@@ -1830,7 +1830,11 @@ namespace Emby.Server.Implementations.Library
                 {
                     try
                     {
-                        ItemAdded(this, new ItemChangeEventArgs { Item = item });
+                        ItemAdded(this, new ItemChangeEventArgs
+                        {
+                            Item = item,
+                            Parent = parent ?? item.GetParent()
+                        });
                     }
                     catch (Exception ex)
                     {
@@ -1878,6 +1882,7 @@ namespace Emby.Server.Implementations.Library
                     ItemUpdated(this, new ItemChangeEventArgs
                     {
                         Item = item,
+                        Parent = item.GetParent(),
                         UpdateReason = updateReason
                     });
                 }
@@ -1892,13 +1897,17 @@ namespace Emby.Server.Implementations.Library
         /// Reports the item removed.
         /// </summary>
         /// <param name="item">The item.</param>
-        public void ReportItemRemoved(BaseItem item)
+        public void ReportItemRemoved(BaseItem item, BaseItem parent)
         {
             if (ItemRemoved != null)
             {
                 try
                 {
-                    ItemRemoved(this, new ItemChangeEventArgs { Item = item });
+                    ItemRemoved(this, new ItemChangeEventArgs
+                    {
+                        Item = item,
+                        Parent = parent
+                    });
                 }
                 catch (Exception ex)
                 {

+ 0 - 20
Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs

@@ -125,10 +125,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
                 if (child.IsDirectory)
                 {
                     leftOver.Add(child);
-                }
-                else if (IsIgnored(child.Name))
-                {
-
                 }
                 else
                 {
@@ -298,22 +294,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
             return item;
         }
 
-        private bool IsIgnored(string filename)
-        {
-            // Ignore samples
-            var sampleFilename = " " + filename.Replace(".", " ", StringComparison.OrdinalIgnoreCase)
-                .Replace("-", " ", StringComparison.OrdinalIgnoreCase)
-                .Replace("_", " ", StringComparison.OrdinalIgnoreCase)
-                .Replace("!", " ", StringComparison.OrdinalIgnoreCase);
-
-            if (sampleFilename.IndexOf(" sample ", StringComparison.OrdinalIgnoreCase) != -1)
-            {
-                return true;
-            }
-
-            return false;
-        }
-
         /// <summary>
         /// Sets the initial item values.
         /// </summary>

+ 25 - 2
Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs

@@ -5,6 +5,7 @@ using MediaBrowser.Controller.Library;
 using MediaBrowser.Model.Globalization;
 using Emby.Naming.Common;
 using Emby.Naming.TV;
+using MediaBrowser.Model.Logging;
 
 namespace Emby.Server.Implementations.Library.Resolvers.TV
 {
@@ -21,16 +22,18 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
         private readonly ILibraryManager _libraryManager;
         private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
         private readonly ILocalizationManager _localization;
+        private readonly ILogger _logger;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="SeasonResolver"/> class.
         /// </summary>
         /// <param name="config">The config.</param>
-        public SeasonResolver(IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization)
+        public SeasonResolver(IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization, ILogger logger)
         {
             _config = config;
             _libraryManager = libraryManager;
             _localization = localization;
+            _logger = logger;
         }
 
         /// <summary>
@@ -45,20 +48,40 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
                 var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
                 var series = ((Series)args.Parent);
 
+                var path = args.Path;
+
                 var season = new Season
                 {
-                    IndexNumber = new SeasonPathParser(namingOptions, new RegexProvider()).Parse(args.Path, true, true).SeasonNumber,
+                    IndexNumber = new SeasonPathParser(namingOptions, new RegexProvider()).Parse(path, true, true).SeasonNumber,
                     SeriesId = series.Id,
                     SeriesName = series.Name
                 };
 
                 if (season.IndexNumber.HasValue)
                 {
+                    var resolver = new Emby.Naming.TV.EpisodeResolver(namingOptions);
+
+                    var episodeInfo = resolver.Resolve(path, true);
+
+                    if (episodeInfo != null)
+                    {
+                        if (episodeInfo.EpisodeNumber.HasValue && episodeInfo.SeasonNumber.HasValue)
+                        {
+                            _logger.Info("Found folder underneath series with episode number: {0}. Season {1}. Episode {2}", 
+                                path,
+                                episodeInfo.SeasonNumber.Value,
+                                episodeInfo.EpisodeNumber.Value);
+
+                            return null;
+                        }
+                    }
+
                     var seasonNumber = season.IndexNumber.Value;
 
                     season.Name = seasonNumber == 0 ?
                         args.LibraryOptions.SeasonZeroDisplayName :
                         string.Format(_localization.GetLocalizedString("NameSeasonNumber"), seasonNumber.ToString(UsCulture), args.GetLibraryOptions().PreferredMetadataLanguage);
+
                 }
 
                 return season;

+ 2 - 1
Emby.Server.Implementations/Library/SearchEngine.cs

@@ -191,7 +191,8 @@ namespace Emby.Server.Implementations.Library
                     {
                          ItemFields.AirTime,
                          ItemFields.DateCreated,
-                         ItemFields.ChannelInfo
+                         ItemFields.ChannelInfo,
+                         ItemFields.ParentId
                     }
                 }
             };

+ 7 - 4
Emby.Server.Implementations/Library/UserManager.cs

@@ -218,7 +218,7 @@ namespace Emby.Server.Implementations.Library
             return builder.ToString();
         }
 
-        public async Task<User> AuthenticateUser(string username, string password, string hashedPassword, string passwordMd5, string remoteEndPoint)
+        public async Task<User> AuthenticateUser(string username, string password, string hashedPassword, string passwordMd5, string remoteEndPoint, bool isUserSession)
         {
             if (string.IsNullOrWhiteSpace(username))
             {
@@ -288,8 +288,11 @@ namespace Emby.Server.Implementations.Library
             // Update LastActivityDate and LastLoginDate, then save
             if (success)
             {
-                user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow;
-                UpdateUser(user);
+                if (isUserSession)
+                {
+                    user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow;
+                    UpdateUser(user);
+                }
                 UpdateInvalidLoginAttemptCount(user, 0);
             }
             else
@@ -812,7 +815,7 @@ namespace Emby.Server.Implementations.Library
 
             var text = new StringBuilder();
 
-            var localAddress = _appHost.GetLocalApiUrl().Result ?? string.Empty;
+            var localAddress = _appHost.GetLocalApiUrl(CancellationToken.None).Result ?? string.Empty;
 
             text.AppendLine("Use your web browser to visit:");
             text.AppendLine(string.Empty);

+ 32 - 3
Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -1512,6 +1512,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                 }
             }
 
+            DeleteFileIfEmpty(recordPath);
+
             TriggerRefresh(recordPath);
             _libraryMonitor.ReportFileSystemChangeComplete(recordPath, false);
 
@@ -1542,6 +1544,23 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             }
         }
 
+        private void DeleteFileIfEmpty(string path)
+        {
+            var file = _fileSystem.GetFileInfo(path);
+
+            if (file.Exists && file.Length == 0)
+            {
+                try
+                {
+                    _fileSystem.DeleteFile(path);
+                }
+                catch (Exception ex)
+                {
+                    _logger.ErrorException("Error deleting 0-byte failed recording file {0}", ex, path);
+                }
+            }
+        }
+
         private void TriggerRefresh(string path)
         {
             _logger.Info("Triggering refresh on {0}", path);
@@ -1897,7 +1916,15 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                     imageSaveFilenameWithoutExtension = "logo";
                     break;
                 case ImageType.Thumb:
-                    imageSaveFilenameWithoutExtension = "landscape";
+                    if (program.IsSeries)
+                    {
+                        imageSaveFilenameWithoutExtension = Path.GetFileNameWithoutExtension(recordingPath) + "-thumb";
+                    }
+                    else
+                    {
+                        imageSaveFilenameWithoutExtension = "landscape";
+                    }
+
                     break;
                 case ImageType.Backdrop:
                     imageSaveFilenameWithoutExtension = "fanart";
@@ -1921,9 +1948,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 
         private async Task SaveRecordingImages(string recordingPath, LiveTvProgram program)
         {
-            var image = program.GetImageInfo(ImageType.Primary, 0);
+            var image = program.IsSeries ?
+                (program.GetImageInfo(ImageType.Thumb, 0) ?? program.GetImageInfo(ImageType.Primary, 0)) :
+                (program.GetImageInfo(ImageType.Primary, 0) ?? program.GetImageInfo(ImageType.Thumb, 0));
 
-            if (image != null && program.IsMovie)
+            if (image != null)
             {
                 try
                 {

+ 53 - 17
Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs

@@ -105,31 +105,64 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 
             if (string.Equals(ext, ".gz", StringComparison.OrdinalIgnoreCase))
             {
-                using (var stream = _fileSystem.OpenRead(file))
+                try
                 {
-                    var tempFolder = Path.Combine(_config.ApplicationPaths.TempDirectory, Guid.NewGuid().ToString());
-                    _fileSystem.CreateDirectory(tempFolder);
-
-                    try
-                    {
-                        _zipClient.ExtractAllFromGz(stream, tempFolder, true);
-                    }
-                    catch
-                    {
-                        // If the extraction fails just return the original file, it could be a gz
-                        return file;
-                    }
+                    var tempFolder = ExtractGz(file);
+                    return FindXmlFile(tempFolder);
+                }
+                catch (Exception ex)
+                {
+                    //_logger.ErrorException("Error extracting from gz file {0}", ex, file);
+                }
 
-                    return _fileSystem.GetFiles(tempFolder, true)
-                        .Where(i => string.Equals(i.Extension, ".xml", StringComparison.OrdinalIgnoreCase))
-                        .Select(i => i.FullName)
-                        .FirstOrDefault();
+                try
+                {
+                    var tempFolder = ExtractFirstFileFromGz(file);
+                    return FindXmlFile(tempFolder);
+                }
+                catch (Exception ex)
+                {
+                    //_logger.ErrorException("Error extracting from zip file {0}", ex, file);
                 }
             }
 
             return file;
         }
 
+        private string ExtractFirstFileFromGz(string file)
+        {
+            using (var stream = _fileSystem.OpenRead(file))
+            {
+                var tempFolder = Path.Combine(_config.ApplicationPaths.TempDirectory, Guid.NewGuid().ToString());
+                _fileSystem.CreateDirectory(tempFolder);
+
+                _zipClient.ExtractFirstFileFromGz(stream, tempFolder, "data.xml");
+
+                return tempFolder;
+            }
+        }
+
+        private string ExtractGz(string file)
+        {
+            using (var stream = _fileSystem.OpenRead(file))
+            {
+                var tempFolder = Path.Combine(_config.ApplicationPaths.TempDirectory, Guid.NewGuid().ToString());
+                _fileSystem.CreateDirectory(tempFolder);
+
+                _zipClient.ExtractAllFromGz(stream, tempFolder, true);
+
+                return tempFolder;
+            }
+        }
+
+        private string FindXmlFile(string directory)
+        {
+            return _fileSystem.GetFiles(directory, true)
+                .Where(i => string.Equals(i.Extension, ".xml", StringComparison.OrdinalIgnoreCase))
+                .Select(i => i.FullName)
+                .FirstOrDefault();
+        }
+
         public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken)
         {
             if (string.IsNullOrWhiteSpace(channelId))
@@ -149,6 +182,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
             _logger.Debug("Getting xmltv programs for channel {0}", channelId);
 
             var path = await GetXml(info.Path, cancellationToken).ConfigureAwait(false);
+            _logger.Debug("Opening XmlTvReader for {0}", path);
             var reader = new XmlTvReader(path, GetLanguage(info));
 
             var results = reader.GetProgrammes(channelId, startDateUtc, endDateUtc, cancellationToken);
@@ -251,6 +285,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
         {
             // In theory this should never be called because there is always only one lineup
             var path = await GetXml(info.Path, CancellationToken.None).ConfigureAwait(false);
+            _logger.Debug("Opening XmlTvReader for {0}", path);
             var reader = new XmlTvReader(path, GetLanguage(info));
             var results = reader.GetChannels();
 
@@ -262,6 +297,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
         {
             // In theory this should never be called because there is always only one lineup
             var path = await GetXml(info.Path, cancellationToken).ConfigureAwait(false);
+            _logger.Debug("Opening XmlTvReader for {0}", path);
             var reader = new XmlTvReader(path, GetLanguage(info));
             var results = reader.GetChannels();
 

+ 5 - 3
Emby.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -125,7 +125,7 @@ namespace Emby.Server.Implementations.LiveTv
         public void AddParts(IEnumerable<ILiveTvService> services, IEnumerable<ITunerHost> tunerHosts, IEnumerable<IListingsProvider> listingProviders)
         {
             _services = services.ToArray();
-            _tunerHosts.AddRange(tunerHosts);
+            _tunerHosts.AddRange(tunerHosts.Where(i => i.IsSupported));
             _listingProviders.AddRange(listingProviders);
 
             foreach (var service in _services)
@@ -947,6 +947,7 @@ namespace Emby.Server.Implementations.LiveTv
                 IsKids = query.IsKids,
                 IsNews = query.IsNews,
                 Genres = query.Genres,
+                GenreIds = query.GenreIds,
                 StartIndex = query.StartIndex,
                 Limit = query.Limit,
                 OrderBy = query.OrderBy,
@@ -1020,7 +1021,8 @@ namespace Emby.Server.Implementations.LiveTv
                 EnableTotalRecordCount = query.EnableTotalRecordCount,
                 OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.StartDate, SortOrder.Ascending) },
                 TopParentIds = new[] { topFolder.Id.ToString("N") },
-                DtoOptions = options
+                DtoOptions = options,
+                GenreIds = query.GenreIds
             };
 
             if (query.Limit.HasValue)
@@ -1421,7 +1423,7 @@ namespace Emby.Server.Implementations.LiveTv
 
                     if (newPrograms.Count > 0)
                     {
-                        _libraryManager.CreateItems(newPrograms, cancellationToken);
+                        _libraryManager.CreateItems(newPrograms, null, cancellationToken);
                     }
 
                     // TODO: Do this in bulk

+ 1 - 1
Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs

@@ -95,7 +95,7 @@ namespace Emby.Server.Implementations.LiveTv
             }
 
             var list = sources.ToList();
-            var serverUrl = await _appHost.GetLocalApiUrl().ConfigureAwait(false);
+            var serverUrl = await _appHost.GetLocalApiUrl(cancellationToken).ConfigureAwait(false);
 
             foreach (var source in list)
             {

+ 8 - 0
Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs

@@ -39,6 +39,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             FileSystem = fileSystem;
         }
 
+        public virtual bool IsSupported
+        {
+            get
+            {
+                return true;
+            }
+        }
+
         protected abstract Task<List<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken);
         public abstract string Type { get; }
 

+ 19 - 2
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);
@@ -152,6 +166,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
                 isRemote = !_networkManager.IsInLocalNetwork(uri.Host);
             }
 
+            var supportsDirectPlay = !info.EnableStreamLooping && info.TunerCount == 0;
+
             var mediaSource = new MediaSourceInfo
             {
                 Path = path,
@@ -183,7 +199,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
                 IsInfiniteStream = true,
                 IsRemote = isRemote,
 
-                IgnoreDts = true
+                IgnoreDts = true,
+                SupportsDirectPlay = supportsDirectPlay
             };
 
             mediaSource.InferTotalBitrate();

+ 19 - 6
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;
             }
@@ -88,6 +89,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             SetTempFilePath(extension);
 
             var taskCompletionSource = new TaskCompletionSource<bool>();
+
+            var now = DateTime.UtcNow;
+
             StartStreaming(response, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
 
             //OpenedMediaSource.Protocol = MediaProtocol.File;
@@ -97,11 +101,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             OpenedMediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
             OpenedMediaSource.Protocol = MediaProtocol.Http;
 
-            if (OpenedMediaSource.SupportsProbing)
-            {
-                await Task.Delay(3000).ConfigureAwait(false);
-            }
-
             //OpenedMediaSource.Path = TempFilePath;
             //OpenedMediaSource.Protocol = MediaProtocol.File;
 
@@ -111,6 +110,20 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             //OpenedMediaSource.SupportsDirectStream = true;
             //OpenedMediaSource.SupportsTranscoding = true;
             await taskCompletionSource.Task.ConfigureAwait(false);
+
+            if (OpenedMediaSource.SupportsProbing)
+            {
+                var elapsed = (DateTime.UtcNow - now).TotalMilliseconds;
+
+                var delay = Convert.ToInt32(3000 - elapsed);
+
+                if (delay > 0)
+                {
+                    Logger.Info("Delaying shared stream by {0}ms to allow the buffer to build.", delay);
+
+                    await Task.Delay(delay).ConfigureAwait(false);
+                }
+            }
         }
 
         protected override void CloseInternal()

+ 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}",

+ 15 - 10
Emby.Server.Implementations/MediaEncoder/EncodingManager.cs

@@ -76,6 +76,21 @@ namespace Emby.Server.Implementations.MediaEncoder
                 return false;
             }
 
+            if (video.VideoType == VideoType.Iso)
+            {
+                return false;
+            }
+
+            if (video.VideoType == VideoType.BluRay || video.VideoType == VideoType.Dvd)
+            {
+                return false;
+            }
+
+            if (video.IsShortcut)
+            {
+                return false;
+            }
+
             if (!video.IsCompleteMedia)
             {
                 return false;
@@ -118,16 +133,6 @@ namespace Emby.Server.Implementations.MediaEncoder
                 {
                     if (extractImages)
                     {
-                        if (video.VideoType == VideoType.Iso)
-                        {
-                            continue;
-                        }
-
-                        if (video.VideoType == VideoType.BluRay || video.VideoType == VideoType.Dvd)
-                        {
-                            continue;
-                        }
-
                         try
                         {
                             // Add some time for the first chapter to make sure we don't end up with a black image

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

@@ -17,11 +17,54 @@ namespace Emby.Server.Implementations.Networking
     public class NetworkManager : INetworkManager
     {
         protected ILogger Logger { get; private set; }
-        private DateTime _lastRefresh;
+
+        public event EventHandler NetworkChanged;
 
         public NetworkManager(ILogger logger)
         {
             Logger = logger;
+
+            try
+            {
+                NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged;
+            }
+            catch (Exception ex)
+            {
+                Logger.ErrorException("Error binding to NetworkAddressChanged event", ex);
+            }
+
+            try
+            {
+                NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged;
+            }
+            catch (Exception ex)
+            {
+                Logger.ErrorException("Error binding to NetworkChange_NetworkAvailabilityChanged event", ex);
+            }
+        }
+
+        private void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
+        {
+            Logger.Debug("NetworkAvailabilityChanged");
+            OnNetworkChanged();
+        }
+
+        private void NetworkChange_NetworkAddressChanged(object sender, EventArgs e)
+        {
+            Logger.Debug("NetworkAddressChanged");
+            OnNetworkChanged();
+        }
+
+        private void OnNetworkChanged()
+        {
+            lock (_localIpAddressSyncLock)
+            {
+                _localIpAddresses = null;
+            }
+            if (NetworkChanged != null)
+            {
+                NetworkChanged(this, EventArgs.Empty);
+            }
         }
 
         private List<IpAddressInfo> _localIpAddresses;
@@ -29,34 +72,28 @@ namespace Emby.Server.Implementations.Networking
 
         public List<IpAddressInfo> GetLocalIpAddresses()
         {
-            const int cacheMinutes = 10;
-
             lock (_localIpAddressSyncLock)
             {
-                var forceRefresh = (DateTime.UtcNow - _lastRefresh).TotalMinutes >= cacheMinutes;
-
-                if (_localIpAddresses == null || forceRefresh)
+                if (_localIpAddresses == null)
                 {
-                    var addresses = GetLocalIpAddressesInternal().Select(ToIpAddressInfo).ToList();
+                    var addresses = GetLocalIpAddressesInternal().Result.Select(ToIpAddressInfo).ToList();
 
                     _localIpAddresses = addresses;
-                    _lastRefresh = DateTime.UtcNow;
 
                     return addresses;
                 }
+                return _localIpAddresses;
             }
-
-            return _localIpAddresses;
         }
 
-        private IEnumerable<IPAddress> GetLocalIpAddressesInternal()
+        private async Task<List<IPAddress>> GetLocalIpAddressesInternal()
         {
             var list = GetIPsDefault()
                 .ToList();
 
             if (list.Count == 0)
             {
-                list.AddRange(GetLocalIpAddressesFallback().Result);
+                list.AddRange(await GetLocalIpAddressesFallback().ConfigureAwait(false));
             }
 
             var listClone = list.ToList();
@@ -65,7 +102,8 @@ namespace Emby.Server.Implementations.Networking
                 .OrderBy(i => i.AddressFamily == AddressFamily.InterNetwork ? 0 : 1)
                 .ThenBy(i => listClone.IndexOf(i))
                 .Where(FilterIpAddress)
-                .DistinctBy(i => i.ToString());
+                .DistinctBy(i => i.ToString())
+                .ToList();
         }
 
         private bool FilterIpAddress(IPAddress address)

+ 5 - 1
Emby.Server.Implementations/Session/HttpSessionController.cs

@@ -117,6 +117,10 @@ namespace Emby.Server.Implementations.Session
             {
                 dict["SubtitleStreamIndex"] = command.SubtitleStreamIndex.Value.ToString(CultureInfo.InvariantCulture);
             }
+            if (command.StartIndex.HasValue)
+            {
+                dict["StartIndex"] = command.StartIndex.Value.ToString(CultureInfo.InvariantCulture);
+            }
             if (!string.IsNullOrWhiteSpace(command.MediaSourceId))
             {
                 dict["MediaSourceId"] = command.MediaSourceId;
@@ -147,7 +151,7 @@ namespace Emby.Server.Implementations.Session
             return SendMessage("LibraryChanged", info, cancellationToken);
         }
 
-        public Task SendRestartRequiredNotification(SystemInfo info, CancellationToken cancellationToken)
+        public Task SendRestartRequiredNotification(CancellationToken cancellationToken)
         {
             return SendMessage("RestartRequired", cancellationToken);
         }

+ 2 - 4
Emby.Server.Implementations/Session/SessionManager.cs

@@ -1182,13 +1182,11 @@ namespace Emby.Server.Implementations.Session
         {
             var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList();
 
-            var info = await _appHost.GetSystemInfo().ConfigureAwait(false);
-
             var tasks = sessions.Select(session => Task.Run(async () =>
             {
                 try
                 {
-                    await session.SessionController.SendRestartRequiredNotification(info, cancellationToken).ConfigureAwait(false);
+                    await session.SessionController.SendRestartRequiredNotification(cancellationToken).ConfigureAwait(false);
                 }
                 catch (Exception ex)
                 {
@@ -1423,7 +1421,7 @@ namespace Emby.Server.Implementations.Session
 
             if (enforcePassword)
             {
-                var result = await _userManager.AuthenticateUser(request.Username, request.Password, request.PasswordSha1, request.PasswordMd5, request.RemoteEndPoint).ConfigureAwait(false);
+                var result = await _userManager.AuthenticateUser(request.Username, request.Password, request.PasswordSha1, request.PasswordMd5, request.RemoteEndPoint, true).ConfigureAwait(false);
 
                 if (result == null)
                 {

+ 3 - 3
Emby.Server.Implementations/Session/WebSocketController.cs

@@ -145,12 +145,12 @@ namespace Emby.Server.Implementations.Session
         /// <param name="info">The information.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        public Task SendRestartRequiredNotification(SystemInfo info, CancellationToken cancellationToken)
+        public Task SendRestartRequiredNotification(CancellationToken cancellationToken)
         {
-            return SendMessagesInternal(new WebSocketMessage<SystemInfo>
+            return SendMessagesInternal(new WebSocketMessage<string>
             {
                 MessageType = "RestartRequired",
-                Data = info
+                Data = string.Empty
 
             }, cancellationToken);
         }

+ 3 - 2
Emby.Server.Implementations/Social/SharingManager.cs

@@ -5,6 +5,7 @@ using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Model.Social;
 using System;
+using System.Threading;
 using System.Threading.Tasks;
 
 namespace Emby.Server.Implementations.Social
@@ -42,7 +43,7 @@ namespace Emby.Server.Implementations.Social
                 throw new ResourceNotFoundException();
             }
 
-            var externalUrl = (await _appHost.GetSystemInfo().ConfigureAwait(false)).WanAddress;
+            var externalUrl = (await _appHost.GetPublicSystemInfo(CancellationToken.None).ConfigureAwait(false)).WanAddress;
 
             if (string.IsNullOrWhiteSpace(externalUrl))
             {
@@ -73,7 +74,7 @@ namespace Emby.Server.Implementations.Social
         {
             var info = _repository.GetShareInfo(id);
 
-            AddShareInfo(info, _appHost.GetSystemInfo().Result.WanAddress);
+            AddShareInfo(info, _appHost.GetPublicSystemInfo(CancellationToken.None).Result.WanAddress);
 
             return info;
         }

+ 17 - 11
Emby.Server.Implementations/Udp/UdpServer.cs

@@ -25,7 +25,7 @@ namespace Emby.Server.Implementations.Udp
 
         private bool _isDisposed;
 
-        private readonly List<Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, Task>>> _responders = new List<Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, Task>>>();
+        private readonly List<Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, CancellationToken, Task>>> _responders = new List<Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, CancellationToken, Task>>>();
 
         private readonly IServerApplicationHost _appHost;
         private readonly IJsonSerializer _json;
@@ -44,9 +44,9 @@ namespace Emby.Server.Implementations.Udp
             AddMessageResponder("who is MediaBrowserServer_v2?", false, RespondToV2Message);
         }
 
-        private void AddMessageResponder(string message, bool isSubstring, Func<string, IpEndPointInfo, Encoding, Task> responder)
+        private void AddMessageResponder(string message, bool isSubstring, Func<string, IpEndPointInfo, Encoding, CancellationToken, Task> responder)
         {
-            _responders.Add(new Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, Task>>(message, isSubstring, responder));
+            _responders.Add(new Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, CancellationToken, Task>>(message, isSubstring, responder));
         }
 
         /// <summary>
@@ -67,9 +67,15 @@ namespace Emby.Server.Implementations.Udp
 
             if (responder != null)
             {
+                var cancellationToken = CancellationToken.None;
+
                 try
                 {
-                    await responder.Item2.Item3(responder.Item1, message.RemoteEndPoint, encoding).ConfigureAwait(false);
+                    await responder.Item2.Item3(responder.Item1, message.RemoteEndPoint, encoding, cancellationToken).ConfigureAwait(false);
+                }
+                catch (OperationCanceledException)
+                {
+
                 }
                 catch (Exception ex)
                 {
@@ -78,7 +84,7 @@ namespace Emby.Server.Implementations.Udp
             }
         }
 
-        private Tuple<string, Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, Task>>> GetResponder(byte[] buffer, int bytesReceived, Encoding encoding)
+        private Tuple<string, Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, CancellationToken, Task>>> GetResponder(byte[] buffer, int bytesReceived, Encoding encoding)
         {
             var text = encoding.GetString(buffer, 0, bytesReceived);
             var responder = _responders.FirstOrDefault(i =>
@@ -94,14 +100,14 @@ namespace Emby.Server.Implementations.Udp
             {
                 return null;
             }
-            return new Tuple<string, Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, Task>>>(text, responder);
+            return new Tuple<string, Tuple<string, bool, Func<string, IpEndPointInfo, Encoding, CancellationToken, Task>>>(text, responder);
         }
 
-        private async Task RespondToV2Message(string messageText, IpEndPointInfo endpoint, Encoding encoding)
+        private async Task RespondToV2Message(string messageText, IpEndPointInfo endpoint, Encoding encoding, CancellationToken cancellationToken)
         {
             var parts = messageText.Split('|');
 
-            var localUrl = await _appHost.GetLocalApiUrl().ConfigureAwait(false);
+            var localUrl = await _appHost.GetLocalApiUrl(cancellationToken).ConfigureAwait(false);
 
             if (!string.IsNullOrEmpty(localUrl))
             {
@@ -112,7 +118,7 @@ namespace Emby.Server.Implementations.Udp
                     Name = _appHost.FriendlyName
                 };
 
-                await SendAsync(encoding.GetBytes(_json.SerializeToString(response)), endpoint).ConfigureAwait(false);
+                await SendAsync(encoding.GetBytes(_json.SerializeToString(response)), endpoint, cancellationToken).ConfigureAwait(false);
 
                 if (parts.Length > 1)
                 {
@@ -248,7 +254,7 @@ namespace Emby.Server.Implementations.Udp
             }
         }
 
-        public async Task SendAsync(byte[] bytes, IpEndPointInfo remoteEndPoint)
+        public async Task SendAsync(byte[] bytes, IpEndPointInfo remoteEndPoint, CancellationToken cancellationToken)
         {
             if (_isDisposed)
             {
@@ -267,7 +273,7 @@ namespace Emby.Server.Implementations.Udp
 
             try
             {
-                await _udpClient.SendToAsync(bytes, 0, bytes.Length, remoteEndPoint, CancellationToken.None).ConfigureAwait(false);
+                await _udpClient.SendToAsync(bytes, 0, bytes.Length, remoteEndPoint, cancellationToken).ConfigureAwait(false);
 
                 _logger.Info("Udp message sent to {0}", remoteEndPoint);
             }

+ 120 - 4
MediaBrowser.Api/FilterService.cs

@@ -2,6 +2,7 @@
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Dto;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -11,6 +12,36 @@ using MediaBrowser.Model.Services;
 namespace MediaBrowser.Api
 {
     [Route("/Items/Filters", "GET", Summary = "Gets branding configuration")]
+    public class GetQueryFiltersLegacy : IReturn<QueryFiltersLegacy>
+    {
+        /// <summary>
+        /// Gets or sets the user id.
+        /// </summary>
+        /// <value>The user id.</value>
+        [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+        public string UserId { get; set; }
+
+        [ApiMember(Name = "ParentId", Description = "Specify this to localize the search to a specific item or folder. Omit to use the root", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+        public string ParentId { get; set; }
+
+        [ApiMember(Name = "IncludeItemTypes", Description = "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+        public string IncludeItemTypes { get; set; }
+
+        [ApiMember(Name = "MediaTypes", Description = "Optional filter by MediaType. Allows multiple, comma delimited.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+        public string MediaTypes { get; set; }
+
+        public string[] GetMediaTypes()
+        {
+            return (MediaTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+        }
+
+        public string[] GetIncludeItemTypes()
+        {
+            return (IncludeItemTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+        }
+    }
+
+    [Route("/Items/Filters2", "GET", Summary = "Gets branding configuration")]
     public class GetQueryFilters : IReturn<QueryFilters>
     {
         /// <summary>
@@ -38,6 +69,13 @@ namespace MediaBrowser.Api
         {
             return (IncludeItemTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
         }
+
+        public bool? IsAiring { get; set; }
+        public bool? IsMovie { get; set; }
+        public bool? IsSports { get; set; }
+        public bool? IsKids { get; set; }
+        public bool? IsNews { get; set; }
+        public bool? IsSeries { get; set; }
     }
 
     [Authenticated]
@@ -57,18 +95,96 @@ namespace MediaBrowser.Api
             var parentItem = string.IsNullOrEmpty(request.ParentId) ? null : _libraryManager.GetItemById(request.ParentId);
             var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
 
+            if (string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase) ||
+                string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase) ||
+                string.Equals(request.IncludeItemTypes, typeof(Trailer).Name, StringComparison.OrdinalIgnoreCase) ||
+                string.Equals(request.IncludeItemTypes, "Program", StringComparison.OrdinalIgnoreCase))
+            {
+                parentItem = null;
+            }
+
+            var filters = new QueryFilters();
+
+            var genreQuery = new InternalItemsQuery(user)
+            {
+                AncestorIds = parentItem == null ? new string[] { } : new string[] { parentItem.Id.ToString("N") },
+                IncludeItemTypes = request.GetIncludeItemTypes(),
+                DtoOptions = new Controller.Dto.DtoOptions
+                {
+                    Fields = new ItemFields[] { },
+                    EnableImages = false,
+                    EnableUserData = false
+                },
+                IsAiring = request.IsAiring,
+                IsMovie = request.IsMovie,
+                IsSports = request.IsSports,
+                IsKids = request.IsKids,
+                IsNews = request.IsNews,
+                IsSeries = request.IsSeries
+            };
+
+            if (string.Equals(request.IncludeItemTypes, "MusicAlbum", StringComparison.OrdinalIgnoreCase) ||
+                string.Equals(request.IncludeItemTypes, "MusicVideo", StringComparison.OrdinalIgnoreCase) ||
+                string.Equals(request.IncludeItemTypes, "MusicArtist", StringComparison.OrdinalIgnoreCase) ||
+                string.Equals(request.IncludeItemTypes, "Audio", StringComparison.OrdinalIgnoreCase))
+            {
+                filters.Genres = _libraryManager.GetMusicGenres(genreQuery).Items.Select(i => new NameIdPair
+                {
+                    Name = i.Item1.Name,
+                    Id = i.Item1.Id.ToString("N")
+
+                }).ToArray();
+            }
+            else if (string.Equals(request.IncludeItemTypes, "Game", StringComparison.OrdinalIgnoreCase) ||
+                string.Equals(request.IncludeItemTypes, "GameSystem", StringComparison.OrdinalIgnoreCase))
+            {
+                filters.Genres = _libraryManager.GetGameGenres(genreQuery).Items.Select(i => new NameIdPair
+                {
+                    Name = i.Item1.Name,
+                    Id = i.Item1.Id.ToString("N")
+
+                }).ToArray();
+            }
+            else
+            {
+                filters.Genres = _libraryManager.GetGenres(genreQuery).Items.Select(i => new NameIdPair
+                {
+                    Name = i.Item1.Name,
+                    Id = i.Item1.Id.ToString("N")
+
+                }).ToArray();
+            }
+
+            return ToOptimizedResult(filters);
+        }
+
+        public object Get(GetQueryFiltersLegacy request)
+        {
+            var parentItem = string.IsNullOrEmpty(request.ParentId) ? null : _libraryManager.GetItemById(request.ParentId);
+            var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
+
+            if (string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase) ||
+                string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase) ||
+                string.Equals(request.IncludeItemTypes, typeof(Trailer).Name, StringComparison.OrdinalIgnoreCase) ||
+                string.Equals(request.IncludeItemTypes, "Program", StringComparison.OrdinalIgnoreCase))
+            {
+                parentItem = null;
+            }
+
             var item = string.IsNullOrEmpty(request.ParentId) ?
                user == null ? _libraryManager.RootFolder : user.RootFolder :
                parentItem;
 
             var result = ((Folder)item).GetItemList(GetItemsQuery(request, user));
 
-            return ToOptimizedResult(GetFilters(result));
+            var filters = GetFilters(result);
+
+            return ToOptimizedResult(filters);
         }
 
-        private QueryFilters GetFilters(BaseItem[] items)
+        private QueryFiltersLegacy GetFilters(BaseItem[] items)
         {
-            var result = new QueryFilters();
+            var result = new QueryFiltersLegacy();
 
             result.Years = items.Select(i => i.ProductionYear ?? -1)
                 .Where(i => i > 0)
@@ -97,7 +213,7 @@ namespace MediaBrowser.Api
             return result;
         }
 
-        private InternalItemsQuery GetItemsQuery(GetQueryFilters request, User user)
+        private InternalItemsQuery GetItemsQuery(GetQueryFiltersLegacy request, User user)
         {
             var query = new InternalItemsQuery
             {

+ 9 - 0
MediaBrowser.Api/LiveTv/LiveTvService.cs

@@ -379,6 +379,9 @@ namespace MediaBrowser.Api.LiveTv
         [ApiMember(Name = "Genres", Description = "The genres to return guide information for.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
         public string Genres { get; set; }
 
+        [ApiMember(Name = "GenreIds", Description = "The genres to return guide information for.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
+        public string GenreIds { get; set; }
+
         [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
         public bool? EnableImages { get; set; }
 
@@ -456,6 +459,9 @@ namespace MediaBrowser.Api.LiveTv
         [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string EnableImageTypes { get; set; }
 
+        [ApiMember(Name = "GenreIds", Description = "The genres to return guide information for.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
+        public string GenreIds { get; set; }
+
         /// <summary>
         /// Fields to return within the items, in addition to basic information
         /// </summary>
@@ -1003,6 +1009,7 @@ namespace MediaBrowser.Api.LiveTv
             query.IsSports = request.IsSports;
             query.SeriesTimerId = request.SeriesTimerId;
             query.Genres = (request.Genres ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+            query.GenreIds = (request.GenreIds ?? String.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
 
             if (!string.IsNullOrWhiteSpace(request.LibrarySeriesId))
             {
@@ -1036,6 +1043,8 @@ namespace MediaBrowser.Api.LiveTv
                 EnableTotalRecordCount = request.EnableTotalRecordCount
             };
 
+            query.GenreIds = (request.GenreIds ?? String.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
+
             var result = _liveTvManager.GetRecommendedPrograms(query, GetDtoOptions(_authContext, request), CancellationToken.None);
 
             return ToOptimizedResult(result);

+ 3 - 2
MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs

@@ -3,9 +3,10 @@ using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Tasks;
 using System.Collections.Generic;
 using System.Linq;
-using System.Threading.Tasks;
+using System.Threading;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.Threading;
+using System.Threading.Tasks;
 
 namespace MediaBrowser.Api.ScheduledTasks
 {
@@ -63,7 +64,7 @@ namespace MediaBrowser.Api.ScheduledTasks
         /// </summary>
         /// <param name="state">The state.</param>
         /// <returns>Task{IEnumerable{TaskInfo}}.</returns>
-        protected override Task<IEnumerable<TaskInfo>> GetDataToSend(WebSocketListenerState state)
+        protected override Task<IEnumerable<TaskInfo>> GetDataToSend(WebSocketListenerState state, CancellationToken cancellationToken)
         {
             return Task.FromResult(TaskManager.ScheduledTasks
                 .OrderBy(i => i.Name)

+ 12 - 1
MediaBrowser.Api/SearchService.cs

@@ -248,9 +248,20 @@ namespace MediaBrowser.Api
 
             if (song != null)
             {
-                result.Album = song.Album;
                 result.AlbumArtist = song.AlbumArtists.FirstOrDefault();
                 result.Artists = song.Artists;
+
+                album = song.AlbumEntity;
+
+                if (album != null)
+                {
+                    result.Album = album.Name;
+                    result.AlbumId = album.Id.ToString("N");
+                }
+                else
+                {
+                    result.Album = song.Album;
+                }
             }
 
             if (!string.IsNullOrWhiteSpace(item.ChannelId))

+ 3 - 2
MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs

@@ -5,8 +5,9 @@ using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Session;
 using System.Collections.Generic;
 using System.Linq;
-using System.Threading.Tasks;
+using System.Threading;
 using MediaBrowser.Model.Threading;
+using System.Threading.Tasks;
 
 namespace MediaBrowser.Api.Session
 {
@@ -86,7 +87,7 @@ namespace MediaBrowser.Api.Session
         /// </summary>
         /// <param name="state">The state.</param>
         /// <returns>Task{SystemInfo}.</returns>
-        protected override Task<IEnumerable<SessionInfoDto>> GetDataToSend(WebSocketListenerState state)
+        protected override Task<IEnumerable<SessionInfoDto>> GetDataToSend(WebSocketListenerState state, CancellationToken cancellationToken)
         {
             return Task.FromResult(_sessionManager.Sessions.Where(i => i.IsActive).Select(_sessionManager.GetSessionInfoDto));
         }

+ 1 - 0
MediaBrowser.Api/StartupWizardService.cs

@@ -67,6 +67,7 @@ namespace MediaBrowser.Api
         public void Post(ReportStartupWizardComplete request)
         {
             _config.Configuration.IsStartupWizardCompleted = true;
+            _config.Configuration.AutoRunWebApp = true;
             _config.SetOptimalValues();
             _config.SaveConfiguration();
 

+ 2 - 1
MediaBrowser.Api/System/ActivityLogWebSocketListener.cs

@@ -5,6 +5,7 @@ using System.Collections.Generic;
 using System.Threading.Tasks;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.Threading;
+using System.Threading;
 
 namespace MediaBrowser.Api.System
 {
@@ -43,7 +44,7 @@ namespace MediaBrowser.Api.System
         /// </summary>
         /// <param name="state">The state.</param>
         /// <returns>Task{SystemInfo}.</returns>
-        protected override Task<List<ActivityLogEntry>> GetDataToSend(WebSocketListenerState state)
+        protected override Task<List<ActivityLogEntry>> GetDataToSend(WebSocketListenerState state, CancellationToken CancellationToken)
         {
             return Task.FromResult(new List<ActivityLogEntry>());
         }

+ 3 - 2
MediaBrowser.Api/System/SystemInfoWebSocketListener.cs

@@ -4,6 +4,7 @@ using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.System;
 using System.Threading.Tasks;
 using MediaBrowser.Model.Threading;
+using System.Threading;
 
 namespace MediaBrowser.Api.System
 {
@@ -40,9 +41,9 @@ namespace MediaBrowser.Api.System
         /// </summary>
         /// <param name="state">The state.</param>
         /// <returns>Task{SystemInfo}.</returns>
-        protected override Task<SystemInfo> GetDataToSend(WebSocketListenerState state)
+        protected override Task<SystemInfo> GetDataToSend(WebSocketListenerState state, CancellationToken cancellationToken)
         {
-            return _appHost.GetSystemInfo();
+            return _appHost.GetSystemInfo(cancellationToken);
         }
     }
 }

+ 4 - 13
MediaBrowser.Api/System/SystemService.cs

@@ -14,6 +14,7 @@ using MediaBrowser.Controller.IO;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Services;
+using System.Threading;
 
 namespace MediaBrowser.Api.System
 {
@@ -164,26 +165,16 @@ namespace MediaBrowser.Api.System
         /// <returns>System.Object.</returns>
         public async Task<object> Get(GetSystemInfo request)
         {
-            var result = await _appHost.GetSystemInfo().ConfigureAwait(false);
+            var result = await _appHost.GetSystemInfo(CancellationToken.None).ConfigureAwait(false);
 
             return ToOptimizedResult(result);
         }
 
         public async Task<object> Get(GetPublicSystemInfo request)
         {
-            var result = await _appHost.GetSystemInfo().ConfigureAwait(false);
+            var result = await _appHost.GetPublicSystemInfo(CancellationToken.None).ConfigureAwait(false);
 
-            var publicInfo = new PublicSystemInfo
-            {
-                Id = result.Id,
-                ServerName = result.ServerName,
-                Version = result.Version,
-                LocalAddress = result.LocalAddress,
-                WanAddress = result.WanAddress,
-                OperatingSystem = result.OperatingSystem
-            };
-
-            return ToOptimizedResult(publicInfo);
+            return ToOptimizedResult(result);
         }
 
         /// <summary>

+ 1 - 1
MediaBrowser.Api/UserService.cs

@@ -473,7 +473,7 @@ namespace MediaBrowser.Api
             }
             else
             {
-                var success = await _userManager.AuthenticateUser(user.Name, request.CurrentPw, request.CurrentPassword, null, Request.RemoteIp).ConfigureAwait(false);
+                var success = await _userManager.AuthenticateUser(user.Name, request.CurrentPw, request.CurrentPassword, null, Request.RemoteIp, false).ConfigureAwait(false);
 
                 if (success == null)
                 {

+ 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>

部分文件因文件數量過多而無法顯示