diff options
| author | Luke <luke.pulverenti@gmail.com> | 2017-12-05 13:37:55 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-12-05 13:37:55 -0500 |
| commit | c32d8656382a0eacb301692e0084377fc433ae9b (patch) | |
| tree | 121dd382bf71a9b5c96e00771c0ba18a7d28ab87 | |
| parent | e7ffdf3fbdae7d4ec76a0a4e0e37792b079c56e5 (diff) | |
| parent | b3fbdde04305a0406b5322ec6947f8a30ddc12af (diff) | |
Merge pull request #3055 from MediaBrowser/beta
Beta
131 files changed, 1402 insertions, 679 deletions
diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index 3dd36a27b..5eeab3dec 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/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); diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs index 165f123f1..a617117f3 100644 --- a/Emby.Dlna/PlayTo/Device.cs +++ b/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; diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index bd0a9e1f4..b253cb26e 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/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); } diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs index dd30dfc3d..84094d906 100644 --- a/Emby.Dlna/PlayTo/PlayToManager.cs +++ b/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); diff --git a/Emby.Dlna/PlayTo/SsdpHttpClient.cs b/Emby.Dlna/PlayTo/SsdpHttpClient.cs index 78b688d92..d4b594367 100644 --- a/Emby.Dlna/PlayTo/SsdpHttpClient.cs +++ b/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; diff --git a/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs b/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs index 331312bec..3f779c679 100644 --- a/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs +++ b/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" } diff --git a/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs b/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs index d2f5fef76..83236c594 100644 --- a/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs +++ b/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 } }; diff --git a/Emby.Dlna/Profiles/PanasonicVieraProfile.cs b/Emby.Dlna/Profiles/PanasonicVieraProfile.cs index eb9cf9528..a267158c9 100644 --- a/Emby.Dlna/Profiles/PanasonicVieraProfile.cs +++ b/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" }, diff --git a/Emby.Dlna/Profiles/PopcornHourProfile.cs b/Emby.Dlna/Profiles/PopcornHourProfile.cs index ac8fba84c..33270e72a 100644 --- a/Emby.Dlna/Profiles/PopcornHourProfile.cs +++ b/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" diff --git a/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs b/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs index c582cb52e..cd9056632 100644 --- a/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs +++ b/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[] { diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs b/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs index d9eab28e1..ddda638ed 100644 --- a/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs +++ b/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 }, diff --git a/Emby.Dlna/Profiles/SonyBravia2010Profile.cs b/Emby.Dlna/Profiles/SonyBravia2010Profile.cs index 60a99561f..0986ebdcb 100644 --- a/Emby.Dlna/Profiles/SonyBravia2010Profile.cs +++ b/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", diff --git a/Emby.Dlna/Profiles/SonyBravia2011Profile.cs b/Emby.Dlna/Profiles/SonyBravia2011Profile.cs index 346845d9d..ff8316d9b 100644 --- a/Emby.Dlna/Profiles/SonyBravia2011Profile.cs +++ b/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", diff --git a/Emby.Dlna/Profiles/SonyBravia2012Profile.cs b/Emby.Dlna/Profiles/SonyBravia2012Profile.cs index 23a39a922..c592fae5c 100644 --- a/Emby.Dlna/Profiles/SonyBravia2012Profile.cs +++ b/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", diff --git a/Emby.Dlna/Profiles/SonyBravia2013Profile.cs b/Emby.Dlna/Profiles/SonyBravia2013Profile.cs index 2d36d3414..8580c744d 100644 --- a/Emby.Dlna/Profiles/SonyBravia2013Profile.cs +++ b/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", diff --git a/Emby.Dlna/Profiles/SonyBravia2014Profile.cs b/Emby.Dlna/Profiles/SonyBravia2014Profile.cs index 2c871a5d6..8f871dee7 100644 --- a/Emby.Dlna/Profiles/SonyBravia2014Profile.cs +++ b/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", diff --git a/Emby.Dlna/Profiles/SonyPs3Profile.cs b/Emby.Dlna/Profiles/SonyPs3Profile.cs index 2a0490f9a..69b0f81ee 100644 --- a/Emby.Dlna/Profiles/SonyPs3Profile.cs +++ b/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" diff --git a/Emby.Dlna/Profiles/SonyPs4Profile.cs b/Emby.Dlna/Profiles/SonyPs4Profile.cs index c7f70f989..4c4c1f676 100644 --- a/Emby.Dlna/Profiles/SonyPs4Profile.cs +++ b/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" diff --git a/Emby.Dlna/Profiles/WdtvLiveProfile.cs b/Emby.Dlna/Profiles/WdtvLiveProfile.cs index 61819c57d..b4ca5a3d9 100644 --- a/Emby.Dlna/Profiles/WdtvLiveProfile.cs +++ b/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 } diff --git a/Emby.Dlna/Profiles/XboxOneProfile.cs b/Emby.Dlna/Profiles/XboxOneProfile.cs index 99e52510f..d497ee161 100644 --- a/Emby.Dlna/Profiles/XboxOneProfile.cs +++ b/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 diff --git a/Emby.Dlna/Profiles/Xml/Default.xml b/Emby.Dlna/Profiles/Xml/Default.xml index 133f4abf1..77392d9ce 100644 --- a/Emby.Dlna/Profiles/Xml/Default.xml +++ b/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> diff --git a/Emby.Dlna/Profiles/Xml/Denon AVR.xml b/Emby.Dlna/Profiles/Xml/Denon AVR.xml index 9c7276f81..3ab20c330 100644 --- a/Emby.Dlna/Profiles/Xml/Denon AVR.xml +++ b/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> diff --git a/Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml b/Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml index 381a9f641..f022191a3 100644 --- a/Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml +++ b/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> diff --git a/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml b/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml index 4eb67ae81..fa8fd0e74 100644 --- a/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml +++ b/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> diff --git a/Emby.Dlna/Profiles/Xml/LG Smart TV.xml b/Emby.Dlna/Profiles/Xml/LG Smart TV.xml index ac8c8194c..845ff0415 100644 --- a/Emby.Dlna/Profiles/Xml/LG Smart TV.xml +++ b/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> diff --git a/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml b/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml index bd20112a8..410a79432 100644 --- a/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml +++ b/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" /> diff --git a/Emby.Dlna/Profiles/Xml/Marantz.xml b/Emby.Dlna/Profiles/Xml/Marantz.xml index ae843e34a..7d2d53ee4 100644 --- a/Emby.Dlna/Profiles/Xml/Marantz.xml +++ b/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> diff --git a/Emby.Dlna/Profiles/Xml/MediaMonkey.xml b/Emby.Dlna/Profiles/Xml/MediaMonkey.xml index 94606a93c..944b80f71 100644 --- a/Emby.Dlna/Profiles/Xml/MediaMonkey.xml +++ b/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> diff --git a/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml b/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml index 1c58ab0c9..0b842fe2a 100644 --- a/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml +++ b/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"> diff --git a/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml b/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml index 69bf74a98..c49184a4c 100644 --- a/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml +++ b/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" /> diff --git a/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml b/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml index 6fbed0590..361d7660b 100644 --- a/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml +++ b/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> diff --git a/Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml b/Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml index ae2e686b8..9043330ec 100644 --- a/Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml +++ b/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> diff --git a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml index 576f5ca50..2734aec9d 100644 --- a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml +++ b/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> diff --git a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml index 5a04afb3e..8c5e0a90e 100644 --- a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml +++ b/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> diff --git a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml index 97f0e3790..6d55ef980 100644 --- a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml +++ b/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> diff --git a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml index 052bc80d8..58312c1c6 100644 --- a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml +++ b/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> diff --git a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml index 084fa9df0..011ba08ea 100644 --- a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml +++ b/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"> diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2010).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2010).xml index 046282ba3..c99e21a34 100644 --- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2010).xml +++ b/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"> diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml index e30afa2af..6defa3c22 100644 --- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml +++ b/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"> diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml index cff07f04c..d90f02a55 100644 --- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml +++ b/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"> diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml index 2d5794087..ad5d1a676 100644 --- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml +++ b/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"> diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml index d61b986c2..f654982cb 100644 --- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml +++ b/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"> diff --git a/Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml b/Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml index 966ecc2c8..8da880951 100644 --- a/Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml +++ b/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" /> diff --git a/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml b/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml index 6a818193b..c192d082e 100644 --- a/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml +++ b/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" /> diff --git a/Emby.Dlna/Profiles/Xml/WDTV Live.xml b/Emby.Dlna/Profiles/Xml/WDTV Live.xml index 44d130e92..0fc8b9973 100644 --- a/Emby.Dlna/Profiles/Xml/WDTV Live.xml +++ b/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> diff --git a/Emby.Dlna/Profiles/Xml/Xbox One.xml b/Emby.Dlna/Profiles/Xml/Xbox One.xml index 635423234..0b095b2d0 100644 --- a/Emby.Dlna/Profiles/Xml/Xbox One.xml +++ b/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" /> diff --git a/Emby.Dlna/Profiles/Xml/foobar2000.xml b/Emby.Dlna/Profiles/Xml/foobar2000.xml index b22a0ee16..82ffc916c 100644 --- a/Emby.Dlna/Profiles/Xml/foobar2000.xml +++ b/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> diff --git a/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs b/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs index 38e2879ea..3b3f7cf0a 100644 --- a/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs +++ b/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); } } diff --git a/Emby.Drawing.Skia/Emby.Drawing.Skia.csproj b/Emby.Drawing.Skia/Emby.Drawing.Skia.csproj index f2b32d52c..2b61561cb 100644 --- a/Emby.Drawing.Skia/Emby.Drawing.Skia.csproj +++ b/Emby.Drawing.Skia/Emby.Drawing.Skia.csproj @@ -60,9 +60,6 @@ <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> </Reference> diff --git a/Emby.Drawing.Skia/PlayedIndicatorDrawer.cs b/Emby.Drawing.Skia/PlayedIndicatorDrawer.cs index 48f2da62b..2417043d6 100644 --- a/Emby.Drawing.Skia/PlayedIndicatorDrawer.cs +++ b/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) diff --git a/Emby.Drawing.Skia/SkiaEncoder.cs b/Emby.Drawing.Skia/SkiaEncoder.cs index a89e1f2db..9b4f1fc58 100644 --- a/Emby.Drawing.Skia/SkiaEncoder.cs +++ b/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); diff --git a/Emby.Drawing.Skia/UnplayedCountIndicator.cs b/Emby.Drawing.Skia/UnplayedCountIndicator.cs index 56a2519a3..b59bc1026 100644 --- a/Emby.Drawing.Skia/UnplayedCountIndicator.cs +++ b/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; diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index e28d22cf7..f91cb4675 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/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() { diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 745d83eb5..26450c06c 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/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 diff --git a/Emby.Server.Implementations/Archiving/ZipClient.cs b/Emby.Server.Implementations/Archiving/ZipClient.cs index d7d37bb61..32938e151 100644 --- a/Emby.Server.Implementations/Archiving/ZipClient.cs +++ b/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> diff --git a/Emby.Server.Implementations/Browser/BrowserLauncher.cs b/Emby.Server.Implementations/Browser/BrowserLauncher.cs index 05cde91e2..71497f6bf 100644 --- a/Emby.Server.Implementations/Browser/BrowserLauncher.cs +++ b/Emby.Server.Implementations/Browser/BrowserLauncher.cs @@ -61,7 +61,7 @@ namespace Emby.Server.Implementations.Browser { appHost.LaunchUrl(url); } - catch (NotImplementedException) + catch (NotSupportedException) { } diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index eb0f5150f..830d6447e 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -4286,7 +4286,7 @@ namespace Emby.Server.Implementations.Data if (query.MinParentalRating.HasValue) { - whereClauses.Add("InheritedParentalRatingValue<=@MinParentalRating"); + whereClauses.Add("InheritedParentalRatingValue>=@MinParentalRating"); if (statement != null) { statement.TryBind("@MinParentalRating", query.MinParentalRating.Value); @@ -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); diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 7ccf54d20..cef37910e 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -708,6 +708,8 @@ <EmbeddedResource Include="Localization\Core\zh-CN.json" /> <EmbeddedResource Include="Localization\Core\zh-HK.json" /> <EmbeddedResource Include="Localization\Core\en-US.json" /> + <EmbeddedResource Include="Localization\Core\el.json" /> + <EmbeddedResource Include="Localization\Core\gsw.json" /> <None Include="packages.config" /> <None Include="TextEncoding\NLangDetect\Profiles\afr" /> <None Include="TextEncoding\NLangDetect\Profiles\ara" /> diff --git a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs index 2cef46839..903bb0ff4 100644 --- a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs +++ b/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)) diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 299da0744..0e771cbec 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/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> diff --git a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs b/Emby.Server.Implementations/EntryPoints/StartupWizard.cs index 614c04fd2..103b4b321 100644 --- a/Emby.Server.Implementations/EntryPoints/StartupWizard.cs +++ b/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> diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs index c99b601c9..a2abb2a5c 100644 --- a/Emby.Server.Implementations/IO/LibraryMonitor.cs +++ b/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; } diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 125d9e980..c8e4031a9 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/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); diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs index 961f9886c..5c3e1dab1 100644 --- a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs +++ b/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; diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index f71e2714a..2934a5147 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/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) { diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 667616414..d74235ec7 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -126,10 +126,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies { leftOver.Add(child); } - else if (IsIgnored(child.Name)) - { - - } else { files.Add(child); @@ -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> diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs index a0ff29482..3bad69b56 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs +++ b/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; diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs index df21c1409..8021399bd 100644 --- a/Emby.Server.Implementations/Library/SearchEngine.cs +++ b/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 } } }; diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 0f48ff46b..71c953b2c 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/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); diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 9992c71ec..35d2d3c0a 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/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 { diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index 95ec1dee0..7c251e303 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/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(); diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 7e72d1b1a..211e0de4b 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/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 diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs index ed8b3074b..29b7c41ef 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs +++ b/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) { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs index e0fd32aee..45e96c36d 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/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; } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index c96d1f359..04c5303f1 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -17,6 +17,7 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.System; +using System.IO; namespace Emby.Server.Implementations.LiveTv.TunerHosts { @@ -75,6 +76,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts return Task.FromResult(list); } + private string[] _disallowedSharedStreamExtensions = new string[] + { + ".mkv", + ".mp4", + ".m3u8", + ".mpd" + }; + protected override async Task<ILiveStream> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken) { var tunerCount = info.TunerCount; @@ -95,7 +104,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts if (mediaSource.Protocol == MediaProtocol.Http && !mediaSource.RequiresLooping) { - return new SharedHttpStream(mediaSource, info, streamId, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _environment); + var extension = Path.GetExtension(mediaSource.Path) ?? string.Empty; + + if (!_disallowedSharedStreamExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) + { + return new SharedHttpStream(mediaSource, info, streamId, FileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _environment); + } } return new LiveStream(mediaSource, info, _environment, FileSystem, Logger, Config.ApplicationPaths); @@ -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(); diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs index cc2cb3e5e..af7491e86 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs @@ -71,7 +71,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts } else if (contentType.IndexOf("mp4", StringComparison.OrdinalIgnoreCase) != -1 || contentType.IndexOf("dash", StringComparison.OrdinalIgnoreCase) != -1 || - contentType.IndexOf("mpegURL", StringComparison.OrdinalIgnoreCase) != -1) + contentType.IndexOf("mpegURL", StringComparison.OrdinalIgnoreCase) != -1 || + contentType.IndexOf("text/", StringComparison.OrdinalIgnoreCase) != -1) { requiresRemux = true; } @@ -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() diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json index f21a0b295..7c55540e5 100644 --- a/Emby.Server.Implementations/Localization/Core/ca.json +++ b/Emby.Server.Implementations/Localization/Core/ca.json @@ -1,74 +1,74 @@ { "Latest": "Darreres", "ValueSpecialEpisodeName": "Especial - {0}", - "Inherit": "Inherit", - "Books": "Books", - "Music": "Music", - "Games": "Games", - "Photos": "Photos", - "MixedContent": "Mixed content", - "MusicVideos": "Music videos", - "HomeVideos": "Home videos", - "Playlists": "Playlists", - "HeaderRecordingGroups": "Recording Groups", + "Inherit": "Heretat", + "Books": "Llibres", + "Music": "M\u00fasica", + "Games": "Jocs", + "Photos": "Fotos", + "MixedContent": "Contingut mesclat", + "MusicVideos": "V\u00eddeos musicals", + "HomeVideos": "V\u00eddeos dom\u00e8stics", + "Playlists": "Llistes de reproducci\u00f3", + "HeaderRecordingGroups": "Grups d'Enregistrament", "HeaderContinueWatching": "Continua Veient", - "HeaderFavoriteArtists": "Favorite Artists", - "HeaderFavoriteSongs": "Favorite Songs", + "HeaderFavoriteArtists": "Artistes Preferits", + "HeaderFavoriteSongs": "Can\u00e7ons Preferides", "HeaderAlbumArtists": "Album Artists", - "HeaderFavoriteAlbums": "Favorite Albums", - "HeaderFavoriteEpisodes": "Favorite Episodes", + "HeaderFavoriteAlbums": "\u00c0lbums Preferits", + "HeaderFavoriteEpisodes": "Episodis Preferits", "HeaderFavoriteShows": "Programes Preferits", "HeaderNextUp": "A continuaci\u00f3", - "Favorites": "Favorites", - "Collections": "Collections", - "Channels": "Channels", - "Movies": "Movies", - "Albums": "Albums", - "Artists": "Artists", - "Folders": "Folders", - "Songs": "Songs", - "TvShows": "TV Shows", - "Shows": "Shows", + "Favorites": "Preferits", + "Collections": "Col\u00b7leccions", + "Channels": "Canals", + "Movies": "Pel\u00b7l\u00edcules", + "Albums": "\u00c0lbums", + "Artists": "Artistes", + "Folders": "Directoris", + "Songs": "Can\u00e7ons", + "TvShows": "Espectacles de TV", + "Shows": "Espectacles", "Genres": "G\u00e8neres", - "NameSeasonNumber": "Season {0}", - "AppDeviceValues": "App: {0}, Device: {1}", - "UserDownloadingItemWithValues": "{0} is downloading {1}", - "HeaderLiveTV": "Live TV", - "ChapterNameValue": "Chapter {0}", - "ScheduledTaskFailedWithName": "{0} failed", - "LabelRunningTimeValue": "Running time: {0}", - "ScheduledTaskStartedWithName": "{0} started", - "VersionNumber": "Version {0}", - "PluginInstalledWithName": "{0} was installed", - "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly.", - "PluginUpdatedWithName": "{0} was updated", - "PluginUninstalledWithName": "{0} was uninstalled", - "ItemAddedWithName": "{0} was added to the library", - "ItemRemovedWithName": "{0} was removed from the library", - "LabelIpAddressValue": "Ip address: {0}", - "DeviceOnlineWithName": "{0} is connected", - "UserOnlineFromDevice": "{0} is online from {1}", - "ProviderValue": "Provider: {0}", - "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}", - "UserCreatedWithName": "User {0} has been created", - "UserPasswordChangedWithName": "Password has been changed for user {0}", - "UserDeletedWithName": "User {0} has been deleted", - "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}", - "MessageServerConfigurationUpdated": "Server configuration has been updated", - "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated", - "MessageApplicationUpdated": "Emby Server has been updated", - "FailedLoginAttemptWithUserName": "Failed login attempt from {0}", - "AuthenticationSucceededWithUserName": "{0} successfully authenticated", - "UserOfflineFromDevice": "{0} has disconnected from {1}", - "DeviceOfflineWithName": "{0} has disconnected", - "UserStartedPlayingItemWithValues": "{0} has started playing {1}", - "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}", - "NotificationOptionPluginError": "Plugin failure", - "NotificationOptionApplicationUpdateAvailable": "Application update available", - "NotificationOptionApplicationUpdateInstalled": "Application update installed", - "NotificationOptionPluginUpdateInstalled": "Plugin update installed", - "NotificationOptionPluginInstalled": "Plugin installed", - "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NameSeasonNumber": "Temporada {0}", + "AppDeviceValues": "App: {0}, Dispositiu: {1}", + "UserDownloadingItemWithValues": "{0} est\u00e0 descarregant {1}", + "HeaderLiveTV": "TV en Directe", + "ChapterNameValue": "Episodi {0}", + "ScheduledTaskFailedWithName": "{0} ha fallat", + "LabelRunningTimeValue": "Temps en marxa: {0}", + "ScheduledTaskStartedWithName": "{0} iniciat", + "VersionNumber": "Versi\u00f3 {0}", + "PluginInstalledWithName": "{0} ha estat instal\u00b7lat", + "StartupEmbyServerIsLoading": "El Servidor d'Emby està carregant. Si et plau, prova de nou en breus.", + "PluginUpdatedWithName": "{0} ha estat actualitzat", + "PluginUninstalledWithName": "{0} ha estat desinstal\u00b7lat", + "ItemAddedWithName": "{0} afegit a la biblioteca", + "ItemRemovedWithName": "{0} eliminat de la biblioteca", + "LabelIpAddressValue": "Adre\u00e7a IP: {0}", + "DeviceOnlineWithName": "{0} est\u00e0 connectat", + "UserOnlineFromDevice": "{0} est\u00e0 connectat des de {1}", + "ProviderValue": "Prove\u00efdor: {0}", + "SubtitlesDownloadedForItem": "Subt\u00edtols descarregats per a {0}", + "UserCreatedWithName": "S'ha creat l'usuari {0}", + "UserPasswordChangedWithName": "La contrasenya ha estat canviada per a l'usuari {0}", + "UserDeletedWithName": "L'usuari {0} ha estat eliminat", + "UserConfigurationUpdatedWithName": "La configuraci\u00f3 d'usuari ha estat actualitzada per a {0}", + "MessageServerConfigurationUpdated": "S'ha actualitzat la configuraci\u00f3 del servidor", + "MessageNamedServerConfigurationUpdatedWithValue": "La secci\u00f3 de configuraci\u00f3 {0} ha estat actualitzada", + "MessageApplicationUpdated": "El Servidor d'Emby ha estat actualitzat", + "FailedLoginAttemptWithUserName": "Intent de connexi\u00f3 fallit des de {0}", + "AuthenticationSucceededWithUserName": "{0} s'ha autenticat correctament", + "UserOfflineFromDevice": "{0} s'ha desconnectat de {1}", + "DeviceOfflineWithName": "{0} s'ha desconnectat", + "UserStartedPlayingItemWithValues": "{0} ha comen\u00e7at a reproduir {1}", + "UserStoppedPlayingItemWithValues": "{0} ha parat de reproduir {1}", + "NotificationOptionPluginError": "Un component ha fallat", + "NotificationOptionApplicationUpdateAvailable": "Actualitzaci\u00f3 d'aplicaci\u00f3 disponible", + "NotificationOptionApplicationUpdateInstalled": "Actualitzaci\u00f3 d'aplicaci\u00f3 instal\u00b7lada", + "NotificationOptionPluginUpdateInstalled": "Actualitzaci\u00f3 de complement instal\u00b7lada", + "NotificationOptionPluginInstalled": "Complement instal\u00b7lat", + "NotificationOptionPluginUninstalled": "Complement desinstal\u00b7lat", "NotificationOptionVideoPlayback": "Video playback started", "NotificationOptionAudioPlayback": "Audio playback started", "NotificationOptionGamePlayback": "Game playback started", diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json index 7bab689eb..bcfadb61c 100644 --- a/Emby.Server.Implementations/Localization/Core/de.json +++ b/Emby.Server.Implementations/Localization/Core/de.json @@ -7,7 +7,7 @@ "Games": "Spiele", "Photos": "Fotos", "MixedContent": "Gemischte Inhalte", - "MusicVideos": "Musik-Videos", + "MusicVideos": "Musikvideos", "HomeVideos": "Heimvideos", "Playlists": "Wiedergabelisten", "HeaderRecordingGroups": "Aufnahme-Gruppen", @@ -27,7 +27,7 @@ "Artists": "Interpreten", "Folders": "Verzeichnisse", "Songs": "Songs", - "TvShows": "TV Shows", + "TvShows": "TV Sendungen", "Shows": "Serien", "Genres": "Genres", "NameSeasonNumber": "Staffel {0}", diff --git a/Emby.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json new file mode 100644 index 000000000..ab229e111 --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/el.json @@ -0,0 +1,91 @@ +{ + "Latest": "\u03a4\u03b5\u03bb\u03b5\u03c5\u03c4\u03b1\u03af\u03b1", + "ValueSpecialEpisodeName": "\u0395\u03b9\u03b4\u03b9\u03ba\u03ac - {0} ", + "Inherit": "Inherit", + "Books": "\u0392\u03b9\u03b2\u03bb\u03af\u03b1", + "Music": "\u039c\u03bf\u03c5\u03c3\u03b9\u03ba\u03ae", + "Games": "\u03a0\u03b1\u03b9\u03c7\u03bd\u03af\u03b4\u03b9\u03b1", + "Photos": "\u03a6\u03c9\u03c4\u03bf\u03b3\u03c1\u03b1\u03c6\u03af\u03b5\u03c2", + "MixedContent": "\u0391\u03bd\u03ac\u03bc\u03b5\u03b9\u03ba\u03c4\u03bf \u03a0\u03b5\u03c1\u03b9\u03b5\u03c7\u03cc\u03bc\u03b5\u03bd\u03bf", + "MusicVideos": "\u039c\u03bf\u03c5\u03c3\u03b9\u03ba\u03ac \u03b2\u03af\u03bd\u03c4\u03b5\u03bf", + "HomeVideos": "\u03a0\u03c1\u03bf\u03c3\u03c9\u03c0\u03b9\u03ba\u03ac \u0392\u03af\u03bd\u03c4\u03b5\u03bf", + "Playlists": "\u039b\u03af\u03c3\u03c4\u03b5\u03c2 \u03b1\u03bd\u03b1\u03c0\u03b1\u03c1\u03b1\u03b3\u03c9\u03b3\u03ae\u03c2", + "HeaderRecordingGroups": "\u0393\u03ba\u03c1\u03bf\u03c5\u03c0 \u0395\u03b3\u03b3\u03c1\u03b1\u03c6\u03ce\u03bd", + "HeaderContinueWatching": "\u03a3\u03c5\u03bd\u03b5\u03c7\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03c0\u03b1\u03c1\u03b1\u03ba\u03bf\u03bb\u03bf\u03cd\u03b8\u03b7\u03c3\u03b7", + "HeaderFavoriteArtists": "\u0391\u03b3\u03b1\u03c0\u03b7\u03bc\u03ad\u03bd\u03bf\u03b9 \u039a\u03b1\u03bb\u03bb\u03b9\u03c4\u03ad\u03c7\u03bd\u03b5\u03c2", + "HeaderFavoriteSongs": "\u0391\u03b3\u03b1\u03c0\u03b7\u03bc\u03ad\u03bd\u03b1 \u03a4\u03c1\u03b1\u03b3\u03bf\u03cd\u03b4\u03b9\u03b1", + "HeaderAlbumArtists": "\u0386\u03bb\u03bc\u03c0\u03bf\u03c5\u03bc \u039a\u03b1\u03bb\u03bb\u03b9\u03c4\u03b5\u03c7\u03bd\u03ce\u03bd", + "HeaderFavoriteAlbums": "\u0391\u03b3\u03b1\u03c0\u03b7\u03bc\u03ad\u03bd\u03b1 \u0386\u03bb\u03bc\u03c0\u03bf\u03c5\u03bc", + "HeaderFavoriteEpisodes": "\u0391\u03b3\u03b1\u03c0\u03b7\u03bc\u03ad\u03bd\u03b1 \u03b5\u03c0\u03b5\u03b9\u03c3\u03cc\u03b4\u03b9\u03b1", + "HeaderFavoriteShows": "\u0391\u03b3\u03b1\u03c0\u03b7\u03bc\u03ad\u03bd\u03b5\u03c2 \u03a3\u03b5\u03b9\u03c1\u03ad\u03c2", + "HeaderNextUp": "\u0395\u03c0\u03cc\u03bc\u03b5\u03bd\u03bf", + "Favorites": "\u0391\u03b3\u03b1\u03c0\u03b7\u03bc\u03ad\u03bd\u03b1", + "Collections": "\u03a3\u03c5\u03bb\u03bb\u03bf\u03b3\u03ad\u03c2", + "Channels": "\u039a\u03b1\u03bd\u03ac\u03bb\u03b9\u03b1", + "Movies": "\u03a4\u03b1\u03b9\u03bd\u03af\u03b5\u03c2", + "Albums": "\u0386\u03bb\u03bc\u03c0\u03bf\u03c5\u03bc", + "Artists": "\u039a\u03b1\u03bb\u03bb\u03b9\u03c4\u03ad\u03c7\u03bd\u03b5\u03c2", + "Folders": "\u03a6\u03ac\u03ba\u03b5\u03bb\u03bf\u03b9", + "Songs": "\u03a4\u03c1\u03b1\u03b3\u03bf\u03cd\u03b4\u03b9\u03b1", + "TvShows": "\u03a4\u03b7\u03bb\u03b5\u03bf\u03c0\u03c4\u03b9\u03ba\u03ac \u03c0\u03c1\u03bf\u03b3\u03c1\u03ac\u03bc\u03bc\u03b1\u03c4\u03b1", + "Shows": "\u03a3\u03b5\u03b9\u03c1\u03ad\u03c2", + "Genres": "\u0395\u03af\u03b4\u03b7", + "NameSeasonNumber": "\u039a\u03cd\u03ba\u03bb\u03bf\u03c2 {0}", + "AppDeviceValues": "\u0395\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae: {0}, \u03a3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae: {1}", + "UserDownloadingItemWithValues": "{0} \u03ba\u03b1\u03c4\u03b5\u03b2\u03ac\u03b6\u03b5\u03b9 {1}", + "HeaderLiveTV": "\u0396\u03c9\u03bd\u03c4\u03b1\u03bd\u03ae \u03a4\u03b7\u03bb\u03b5\u03cc\u03c1\u03b1\u03c3\u03b7", + "ChapterNameValue": "\u039a\u03b5\u03c6\u03ac\u03bb\u03b1\u03b9\u03bf {0}", + "ScheduledTaskFailedWithName": "{0} \u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1", + "LabelRunningTimeValue": "\u0394\u03b9\u03ac\u03c1\u03ba\u03b5\u03b9\u03b1: {0}", + "ScheduledTaskStartedWithName": "{0} \u03ad\u03bd\u03b1\u03c1\u03be\u03b7", + "VersionNumber": "\u0388\u03ba\u03b4\u03bf\u03c3\u03b7 {0}", + "PluginInstalledWithName": "{0} \u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ae\u03b8\u03b7\u03ba\u03b5", + "StartupEmbyServerIsLoading": "\u039f \u03a3\u03ad\u03c1\u03b2\u03b5\u03c1 \u03c6\u03bf\u03c1\u03c4\u03ce\u03bd\u03b5\u03b9. \u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03ce \u03b4\u03bf\u03ba\u03b9\u03bc\u03ac\u03c3\u03c4\u03b5 \u03c3\u03b5 \u03bb\u03af\u03b3\u03bf", + "PluginUpdatedWithName": "{0} \u03ad\u03c7\u03b5\u03b9 \u03b1\u03bd\u03b1\u03b2\u03b1\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af", + "PluginUninstalledWithName": "{0} \u03ad\u03c7\u03b5\u03b9 \u03b1\u03c0\u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03b1\u03b8\u03b5\u03af", + "ItemAddedWithName": "{0} \u03c0\u03c1\u03bf\u03c3\u03c4\u03ad\u03b8\u03b7\u03ba\u03b5 \u03c3\u03c4\u03b7 \u03b2\u03b9\u03b2\u03bb\u03b9\u03bf\u03b8\u03ae\u03ba\u03b7", + "ItemRemovedWithName": "{0} \u03b4\u03b9\u03b1\u03b3\u03c1\u03ac\u03c6\u03b7\u03ba\u03b5 \u03b1\u03c0\u03cc \u03c4\u03b7 \u03b2\u03b9\u03b2\u03bb\u03b9\u03bf\u03b8\u03ae\u03ba\u03b7", + "LabelIpAddressValue": "\u0394\u03b9\u03b5\u03cd\u03b8\u03c5\u03bd\u03c3\u03b7 IP: {0}", + "DeviceOnlineWithName": "{0} \u03c3\u03c5\u03bd\u03b4\u03ad\u03b8\u03b7\u03ba\u03b5", + "UserOnlineFromDevice": "{0} \u03b5\u03af\u03bd\u03b1\u03b9 \u03c3\u03c5\u03bd\u03b4\u03b5\u03b4\u03b5\u03bc\u03ad\u03bd\u03bf\u03c2 \u03b1\u03c0\u03bf {1}", + "ProviderValue": "\u03a0\u03ac\u03c1\u03bf\u03c7\u03bf\u03c2: {0}", + "SubtitlesDownloadedForItem": "\u03a5\u03c0\u03cc\u03c4\u03b9\u03c4\u03bb\u03bf\u03b9 \u03bb\u03ae\u03c6\u03b8\u03b7\u03ba\u03b1\u03bd \u03b1\u03c0\u03cc {0}", + "UserCreatedWithName": "\u0394\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03ae\u03b8\u03b7\u03ba\u03b5 \u03bf \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7\u03c2 {0}", + "UserPasswordChangedWithName": "\u039f \u03ba\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03c4\u03bf\u03c5 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7 {0} \u03b1\u03bb\u03bb\u03ac\u03c7\u03b8\u03b7\u03ba\u03b5", + "UserDeletedWithName": "\u039f \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7\u03c2 {0} \u03b4\u03b9\u03b5\u03b3\u03c1\u03ac\u03c6\u03b5\u03b9", + "UserConfigurationUpdatedWithName": "\u039f\u03b9 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03c4\u03bf\u03c5 \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7 {0} \u03ad\u03c7\u03bf\u03c5\u03bd \u03b1\u03bb\u03bb\u03ac\u03be\u03b5\u03b9", + "MessageServerConfigurationUpdated": "Server configuration has been updated", + "MessageNamedServerConfigurationUpdatedWithValue": "\u039f\u03b9 \u03c1\u03c5\u03b8\u03bc\u03af\u03c3\u03b5\u03b9\u03c2 \u03c4\u03bf\u03bc\u03ad\u03b1 \u03c4\u03bf\u03c5 \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae {0} \u03ad\u03c7\u03bf\u03c5\u03bd \u03b1\u03bb\u03bb\u03ac\u03be\u03b5\u03b9", + "MessageApplicationUpdated": "\u039f \u03a3\u03ad\u03c1\u03b2\u03b5\u03c1 \u03ad\u03c7\u03b5\u03b9 \u03b1\u03bd\u03b1\u03b2\u03b1\u03b8\u03bc\u03b9\u03c3\u03c4\u03b5\u03af", + "FailedLoginAttemptWithUserName": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c0\u03c1\u03bf\u03c3\u03c0\u03ac\u03b8\u03b5\u03b9\u03b1 \u03b5\u03b9\u03c3\u03cc\u03b4\u03bf\u03c5 \u03b1\u03c0\u03cc {0}", + "AuthenticationSucceededWithUserName": "{0} \u03b5\u03c0\u03b9\u03c4\u03c5\u03c7\u03b5\u03af\u03c2 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7", + "UserOfflineFromDevice": "{0} \u03b1\u03c0\u03bf\u03c3\u03c5\u03bd\u03b4\u03ad\u03b8\u03b7\u03ba\u03b5 \u03b1\u03c0\u03cc {1}", + "DeviceOfflineWithName": "{0} \u03b1\u03c0\u03bf\u03c3\u03c5\u03bd\u03b4\u03ad\u03b8\u03b7\u03ba\u03b5", + "UserStartedPlayingItemWithValues": "{0} \u03be\u03b5\u03ba\u03af\u03bd\u03b7\u03c3\u03b5 \u03bd\u03b1 \u03c0\u03b1\u03af\u03b6\u03b5\u03b9 {1}", + "UserStoppedPlayingItemWithValues": "{0} \u03c3\u03c4\u03b1\u03bc\u03ac\u03c4\u03b7\u03c3\u03b5 \u03bd\u03b1 \u03c0\u03b1\u03af\u03b6\u03b5\u03b9 {1}", + "NotificationOptionPluginError": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c0\u03c1\u03bf\u03c3\u03b8\u03ad\u03c4\u03bf\u03c5", + "NotificationOptionApplicationUpdateAvailable": "\u03a5\u03c0\u03ac\u03c1\u03c7\u03b5\u03b9 \u03b1\u03bd\u03b1\u03b2\u03ac\u03b8\u03bc\u03b9\u03c3\u03b7", + "NotificationOptionApplicationUpdateInstalled": "\u0397 \u03b1\u03bd\u03b1\u03b2\u03ac\u03b8\u03bc\u03b9\u03c3\u03b7 \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03ce\u03b8\u03b7\u03ba\u03b5", + "NotificationOptionPluginUpdateInstalled": "\u0397 \u03b1\u03bd\u03b1\u03b2\u03ac\u03b8\u03bc\u03b9\u03c3\u03b7 \u03c4\u03bf\u03c5 plugin \u03bf\u03bb\u03bf\u03ba\u03bb\u03b7\u03c1\u03ce\u03b8\u03b7\u03ba\u03b5", + "NotificationOptionPluginInstalled": "\u03a4\u03bf plugin \u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03b8\u03b7\u03ba\u03b5", + "NotificationOptionPluginUninstalled": "\u03a4\u03bf plugin \u03b1\u03c0\u03b5\u03b3\u03ba\u03b1\u03c4\u03b1\u03c3\u03c4\u03ac\u03b8\u03b7\u03ba\u03b5", + "NotificationOptionVideoPlayback": "\u03a4\u03bf \u03b2\u03af\u03bd\u03c4\u03b5\u03bf \u03c0\u03c1\u03bf\u03b2\u03ac\u03bb\u03bb\u03b5\u03c4\u03b1\u03b9", + "NotificationOptionAudioPlayback": "\u0397 \u03bc\u03bf\u03c5\u03c3\u03b9\u03ba\u03ae \u03c0\u03b1\u03af\u03b6\u03b5\u03b9", + "NotificationOptionGamePlayback": "\u03a4\u03bf \u03c0\u03b1\u03b9\u03c7\u03bd\u03af\u03b4\u03b9 \u03be\u03b5\u03ba\u03af\u03bd\u03b7\u03c3\u03b5", + "NotificationOptionVideoPlaybackStopped": "\u03a4\u03bf \u03b2\u03af\u03bd\u03c4\u03b5\u03bf \u03c3\u03c4\u03b1\u03bc\u03ac\u03c4\u03b7\u03c3\u03b5", + "NotificationOptionAudioPlaybackStopped": "\u0397 \u03bc\u03bf\u03c5\u03c3\u03b9\u03ba\u03ae \u03c3\u03c4\u03b1\u03bc\u03ac\u03c4\u03b7\u03c3\u03b5", + "NotificationOptionGamePlaybackStopped": "\u03a4\u03bf \u03c0\u03b1\u03b9\u03c7\u03bd\u03af\u03b4\u03b9 \u03c3\u03c4\u03b1\u03bc\u03ac\u03c4\u03b7\u03c3\u03b5", + "NotificationOptionTaskFailed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c0\u03c1\u03bf\u03b3\u03c1\u03b1\u03bc\u03bc\u03b1\u03c4\u03b9\u03c3\u03bc\u03ad\u03bd\u03b7\u03c2 \u03b5\u03c1\u03b3\u03b1\u03c3\u03af\u03b1\u03c2", + "NotificationOptionInstallationFailed": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03b5\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2", + "NotificationOptionNewLibraryContent": "\u03a0\u03c1\u03bf\u03c3\u03c4\u03ad\u03b8\u03b7\u03ba\u03b5 \u03bd\u03ad\u03bf \u03c0\u03b5\u03c1\u03b9\u03b5\u03c7\u03cc\u03bc\u03b5\u03bd\u03bf", + "NotificationOptionCameraImageUploaded": "Camera image uploaded", + "NotificationOptionUserLockedOut": "\u039f \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7\u03c2 \u03b1\u03c0\u03bf\u03ba\u03bb\u03b5\u03af\u03c3\u03c4\u03b7\u03ba\u03b5", + "NotificationOptionServerRestartRequired": "\u0391\u03c0\u03b1\u03b9\u03c4\u03b5\u03af\u03c4\u03b1\u03b9 \u03b5\u03c0\u03b1\u03bd\u03b5\u03ba\u03ba\u03af\u03bd\u03b7\u03c3\u03b7 \u03c4\u03bf\u03c5 \u03b4\u03b9\u03b1\u03ba\u03bf\u03bc\u03b9\u03c3\u03c4\u03ae", + "UserLockedOutWithName": "\u039f \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7\u03c2 {0} \u03b1\u03c0\u03bf\u03ba\u03bb\u03b5\u03af\u03c3\u03c4\u03b7\u03ba\u03b5", + "SubtitleDownloadFailureForItem": "\u0391\u03b4\u03c5\u03bd\u03b1\u03bc\u03af\u03b1 \u03bb\u03ae\u03c8\u03b7\u03c2 \u03c5\u03c0\u03bf\u03c4\u03af\u03c4\u03bb\u03c9\u03bd \u03b1\u03c0\u03cc {0}", + "Sync": "\u03a3\u03c5\u03b3\u03c7\u03c1\u03bf\u03bd\u03b9\u03c3\u03bc\u03cc\u03c2", + "User": "\u03a7\u03c1\u03ae\u03c3\u03c4\u03b7\u03c2", + "System": "\u03a3\u03cd\u03c3\u03c4\u03b7\u03bc\u03b1", + "Application": "\u0395\u03c6\u03b1\u03c1\u03bc\u03bf\u03b3\u03ae", + "Plugin": "\u03a0\u03c1\u03cc\u03c3\u03b8\u03b5\u03c4\u03bf" +}
\ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/es-MX.json b/Emby.Server.Implementations/Localization/Core/es-MX.json index 2cc1dd759..8bfaffec8 100644 --- a/Emby.Server.Implementations/Localization/Core/es-MX.json +++ b/Emby.Server.Implementations/Localization/Core/es-MX.json @@ -83,7 +83,7 @@ "NotificationOptionServerRestartRequired": "Se necesita reiniciar el Servidor", "UserLockedOutWithName": "El usuario {0} ha sido bloqueado", "SubtitleDownloadFailureForItem": "Fall\u00f3 la descarga de subt\u00edtulos para {0}", - "Sync": "Sinc.", + "Sync": "Sincronizar", "User": "Usuario", "System": "Sistema", "Application": "Aplicaci\u00f3n", diff --git a/Emby.Server.Implementations/Localization/Core/gsw.json b/Emby.Server.Implementations/Localization/Core/gsw.json new file mode 100644 index 000000000..5c7ff5d6d --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/gsw.json @@ -0,0 +1,91 @@ +{ + "Latest": "Letschte", + "ValueSpecialEpisodeName": "Spezial - {0}", + "Inherit": "Hinzuef\u00fcege", + "Books": "B\u00fcecher", + "Music": "Musig", + "Games": "Spiel", + "Photos": "Fotis", + "MixedContent": "Gmischte Inhalt", + "MusicVideos": "Musigfilm", + "HomeVideos": "Heimfilmli", + "Playlists": "Abspielliste", + "HeaderRecordingGroups": "Ufnahmegruppe", + "HeaderContinueWatching": "Wiiterluege", + "HeaderFavoriteArtists": "Besti Interpret", + "HeaderFavoriteSongs": "Besti Lieder", + "HeaderAlbumArtists": "Albuminterprete", + "HeaderFavoriteAlbums": "Favorite Albums", + "HeaderFavoriteEpisodes": "Favorite Episodes", + "HeaderFavoriteShows": "Favorite Shows", + "HeaderNextUp": "Next Up", + "Favorites": "Favorites", + "Collections": "Collections", + "Channels": "Channels", + "Movies": "Movies", + "Albums": "Albums", + "Artists": "Artists", + "Folders": "Folders", + "Songs": "Songs", + "TvShows": "TV Shows", + "Shows": "Shows", + "Genres": "Genres", + "NameSeasonNumber": "Season {0}", + "AppDeviceValues": "App: {0}, Device: {1}", + "UserDownloadingItemWithValues": "{0} is downloading {1}", + "HeaderLiveTV": "Live TV", + "ChapterNameValue": "Chapter {0}", + "ScheduledTaskFailedWithName": "{0} failed", + "LabelRunningTimeValue": "Running time: {0}", + "ScheduledTaskStartedWithName": "{0} started", + "VersionNumber": "Version {0}", + "PluginInstalledWithName": "{0} was installed", + "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly.", + "PluginUpdatedWithName": "{0} was updated", + "PluginUninstalledWithName": "{0} was uninstalled", + "ItemAddedWithName": "{0} was added to the library", + "ItemRemovedWithName": "{0} was removed from the library", + "LabelIpAddressValue": "Ip address: {0}", + "DeviceOnlineWithName": "{0} is connected", + "UserOnlineFromDevice": "{0} is online from {1}", + "ProviderValue": "Provider: {0}", + "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}", + "UserCreatedWithName": "User {0} has been created", + "UserPasswordChangedWithName": "Password has been changed for user {0}", + "UserDeletedWithName": "User {0} has been deleted", + "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}", + "MessageServerConfigurationUpdated": "Server configuration has been updated", + "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated", + "MessageApplicationUpdated": "Emby Server has been updated", + "FailedLoginAttemptWithUserName": "Failed login attempt from {0}", + "AuthenticationSucceededWithUserName": "{0} successfully authenticated", + "UserOfflineFromDevice": "{0} has disconnected from {1}", + "DeviceOfflineWithName": "{0} has disconnected", + "UserStartedPlayingItemWithValues": "{0} has started playing {1}", + "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}", + "NotificationOptionPluginError": "Plugin failure", + "NotificationOptionApplicationUpdateAvailable": "Application update available", + "NotificationOptionApplicationUpdateInstalled": "Application update installed", + "NotificationOptionPluginUpdateInstalled": "Plugin update installed", + "NotificationOptionPluginInstalled": "Plugin installed", + "NotificationOptionPluginUninstalled": "Plugin uninstalled", + "NotificationOptionVideoPlayback": "Video playback started", + "NotificationOptionAudioPlayback": "Audio playback started", + "NotificationOptionGamePlayback": "Game playback started", + "NotificationOptionVideoPlaybackStopped": "Video playback stopped", + "NotificationOptionAudioPlaybackStopped": "Audio playback stopped", + "NotificationOptionGamePlaybackStopped": "Game playback stopped", + "NotificationOptionTaskFailed": "Scheduled task failure", + "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionNewLibraryContent": "New content added", + "NotificationOptionCameraImageUploaded": "Camera image uploaded", + "NotificationOptionUserLockedOut": "User locked out", + "NotificationOptionServerRestartRequired": "Server restart required", + "UserLockedOutWithName": "User {0} has been locked out", + "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", + "Sync": "Sync", + "User": "User", + "System": "System", + "Application": "Application", + "Plugin": "Plugin" +}
\ No newline at end of file diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json index fa4eac1c4..e0a375170 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-BR.json +++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json @@ -27,7 +27,7 @@ "Artists": "Artistas", "Folders": "Pastas", "Songs": "M\u00fasicas", - "TvShows": "TV Shows", + "TvShows": "S\u00e9ries de TV", "Shows": "S\u00e9ries", "Genres": "G\u00eaneros", "NameSeasonNumber": "Temporada {0}", diff --git a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs index 6e0e55bef..d790f4ab8 100644 --- a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs +++ b/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 diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs index fbdb5c128..60da8a012 100644 --- a/Emby.Server.Implementations/Networking/NetworkManager.cs +++ b/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) diff --git a/Emby.Server.Implementations/Session/HttpSessionController.cs b/Emby.Server.Implementations/Session/HttpSessionController.cs index e1c1bbe2b..6725cd7af 100644 --- a/Emby.Server.Implementations/Session/HttpSessionController.cs +++ b/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); } diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 30f6e6521..6b70f2cda 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/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) { diff --git a/Emby.Server.Implementations/Session/WebSocketController.cs b/Emby.Server.Implementations/Session/WebSocketController.cs index ee9ee8969..b13eb6116 100644 --- a/Emby.Server.Implementations/Session/WebSocketController.cs +++ b/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); } diff --git a/Emby.Server.Implementations/Social/SharingManager.cs b/Emby.Server.Implementations/Social/SharingManager.cs index 57cf93948..23ce7492a 100644 --- a/Emby.Server.Implementations/Social/SharingManager.cs +++ b/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; } diff --git a/Emby.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs index 8dc1fae4b..28de80da1 100644 --- a/Emby.Server.Implementations/Udp/UdpServer.cs +++ b/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); } diff --git a/MediaBrowser.Api/FilterService.cs b/MediaBrowser.Api/FilterService.cs index 52b274653..585e9c49b 100644 --- a/MediaBrowser.Api/FilterService.cs +++ b/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 { diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index 703c96e0c..fee52ea5e 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/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); diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs index 69ce6a385..2d30625a9 100644 --- a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs +++ b/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) diff --git a/MediaBrowser.Api/SearchService.cs b/MediaBrowser.Api/SearchService.cs index ad79ea57b..50033eee8 100644 --- a/MediaBrowser.Api/SearchService.cs +++ b/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)) diff --git a/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs b/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs index e133a88a1..65c69fc7d 100644 --- a/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs +++ b/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)); } diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index 54e4657c1..c6345c17f 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/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(); diff --git a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs index 793f74571..f9cac7389 100644 --- a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs +++ b/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>()); } diff --git a/MediaBrowser.Api/System/SystemInfoWebSocketListener.cs b/MediaBrowser.Api/System/SystemInfoWebSocketListener.cs index 8d74cc66c..63847f2b5 100644 --- a/MediaBrowser.Api/System/SystemInfoWebSocketListener.cs +++ b/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); } } } diff --git a/MediaBrowser.Api/System/SystemService.cs b/MediaBrowser.Api/System/SystemService.cs index d850b2ce7..c0bbf70ea 100644 --- a/MediaBrowser.Api/System/SystemService.cs +++ b/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> diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index ddefb08df..66d6a024e 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/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) { diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs index e2ab47322..6ddc2e799 100644 --- a/MediaBrowser.Common/Net/INetworkManager.cs +++ b/MediaBrowser.Common/Net/INetworkManager.cs @@ -1,13 +1,15 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; using System.Collections.Generic; -using System.Net; +using System; using System.Threading.Tasks; namespace MediaBrowser.Common.Net { public interface INetworkManager { + event EventHandler NetworkChanged; + /// <summary> /// Gets a random port number that is currently available /// </summary> diff --git a/MediaBrowser.Common/Updates/GithubUpdater.cs b/MediaBrowser.Common/Updates/GithubUpdater.cs index 30abdc5da..4275799a9 100644 --- a/MediaBrowser.Common/Updates/GithubUpdater.cs +++ b/MediaBrowser.Common/Updates/GithubUpdater.cs @@ -184,20 +184,9 @@ namespace MediaBrowser.Common.Updates private bool IsAsset(Asset asset, string assetFilename, string version) { - var downloadFilename = Path.GetFileNameWithoutExtension(asset.browser_download_url) ?? string.Empty; - var assetExtension = Path.GetExtension(assetFilename); + var downloadFilename = Path.GetFileName(asset.browser_download_url) ?? string.Empty; assetFilename = assetFilename.Replace("{version}", version); - assetFilename = Path.GetFileNameWithoutExtension(assetFilename); - - var zipExtensions = new[] { ".zip", ".7z" }; - var extensionMatch = zipExtensions.Contains(Path.GetExtension(asset.browser_download_url) ?? string.Empty, StringComparer.OrdinalIgnoreCase) && - zipExtensions.Contains(assetExtension ?? string.Empty, StringComparer.OrdinalIgnoreCase); - - if (!extensionMatch) - { - return false; - } if (downloadFilename.IndexOf(assetFilename, StringComparison.OrdinalIgnoreCase) != -1) { diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index f6a8f1d5a..98899253e 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -2236,7 +2236,7 @@ namespace MediaBrowser.Controller.Entities } var filename = System.IO.Path.GetFileNameWithoutExtension(Path); - var extensions = new List<string> { ".nfo", ".xml", ".srt" }; + var extensions = new List<string> { ".nfo", ".xml", ".srt", ".vtt", ".sub", ".idx", ".txt", ".edl" }; extensions.AddRange(SupportedImageExtensions); return FileSystem.GetFiles(FileSystem.GetDirectoryName(Path), extensions.ToArray(extensions.Count), false, false) diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 504d03a27..08b6a123d 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -466,11 +466,11 @@ namespace MediaBrowser.Controller.Entities item.SetParent(null); await LibraryManager.DeleteItem(item, new DeleteOptions { DeleteFileLocation = false }).ConfigureAwait(false); - LibraryManager.ReportItemRemoved(item); + LibraryManager.ReportItemRemoved(item, this); } } - LibraryManager.CreateItems(newItems, cancellationToken); + LibraryManager.CreateItems(newItems, this, cancellationToken); } } else diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 1d09783d1..ccd0a7636 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -215,23 +215,6 @@ namespace MediaBrowser.Controller.Entities.TV return list; } - [IgnoreDataMember] - public bool ContainsEpisodesWithoutSeasonFolders - { - get - { - var children = Children; - foreach (var child in children) - { - if (child is Video) - { - return true; - } - } - return false; - } - } - public override List<BaseItem> GetChildren(User user, bool includeLinkedChildren) { return GetSeasons(user, new DtoOptions(true)); diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index e9f7d5932..3f7f8248b 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Net; using System.Threading.Tasks; using MediaBrowser.Model.Net; +using System.Threading; namespace MediaBrowser.Controller { @@ -19,14 +20,18 @@ namespace MediaBrowser.Controller /// Gets the system info. /// </summary> /// <returns>SystemInfo.</returns> - Task<SystemInfo> GetSystemInfo(); + Task<SystemInfo> GetSystemInfo(CancellationToken cancellationToken); + + Task<PublicSystemInfo> GetPublicSystemInfo(CancellationToken cancellationToken); /// <summary> /// Gets a value indicating whether [supports automatic run at startup]. /// </summary> /// <value><c>true</c> if [supports automatic run at startup]; otherwise, <c>false</c>.</value> bool SupportsAutoRunAtStartup { get; } - + + bool CanLaunchWebBrowser { get; } + /// <summary> /// Gets the HTTP server port. /// </summary> @@ -61,13 +66,13 @@ namespace MediaBrowser.Controller /// Gets the local ip address. /// </summary> /// <value>The local ip address.</value> - Task<List<IpAddressInfo>> GetLocalIpAddresses(); + Task<List<IpAddressInfo>> GetLocalIpAddresses(CancellationToken cancellationToken); /// <summary> /// Gets the local API URL. /// </summary> /// <value>The local API URL.</value> - Task<string> GetLocalApiUrl(); + Task<string> GetLocalApiUrl(CancellationToken cancellationToken); /// <summary> /// Gets the local API URL. diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 37e0d5661..9ef372eb6 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -195,16 +195,12 @@ namespace MediaBrowser.Controller.Library /// <summary> /// Creates the item. /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> void CreateItem(BaseItem item, CancellationToken cancellationToken); /// <summary> /// Creates the items. /// </summary> - /// <param name="items">The items.</param> - /// <param name="cancellationToken">The cancellation token.</param> - void CreateItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken); + void CreateItems(IEnumerable<BaseItem> items, BaseItem parent, CancellationToken cancellationToken); /// <summary> /// Updates the item. @@ -237,8 +233,7 @@ namespace MediaBrowser.Controller.Library /// <summary> /// Reports the item removed. /// </summary> - /// <param name="item">The item.</param> - void ReportItemRemoved(BaseItem item); + void ReportItemRemoved(BaseItem item, BaseItem parent); /// <summary> /// Finds the type of the collection. diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index d4232c77e..03e1d352e 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -143,7 +143,7 @@ namespace MediaBrowser.Controller.Library /// <summary> /// Authenticates the user. /// </summary> - Task<User> AuthenticateUser(string username, string password, string passwordSha1, string passwordMd5, string remoteEndPoint); + Task<User> AuthenticateUser(string username, string password, string passwordSha1, string passwordMd5, string remoteEndPoint, bool isUserSession); /// <summary> /// Starts the forgot password process. diff --git a/MediaBrowser.Controller/Library/ItemChangeEventArgs.cs b/MediaBrowser.Controller/Library/ItemChangeEventArgs.cs index a2951f548..e671490d3 100644 --- a/MediaBrowser.Controller/Library/ItemChangeEventArgs.cs +++ b/MediaBrowser.Controller/Library/ItemChangeEventArgs.cs @@ -13,6 +13,8 @@ namespace MediaBrowser.Controller.Library /// <value>The item.</value> public BaseItem Item { get; set; } + public BaseItem Parent { get; set; } + /// <summary> /// Gets or sets the item. /// </summary> diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs index 242011db0..80c40f8bd 100644 --- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs +++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs @@ -46,6 +46,10 @@ namespace MediaBrowser.Controller.LiveTv Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken); Task<List<TunerHostInfo>> DiscoverDevices(int discoveryDurationMs, CancellationToken cancellationToken); + bool IsSupported + { + get; + } } public interface IConfigurableTunerHost { diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 8f8791922..1fab6defc 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -393,6 +393,10 @@ namespace MediaBrowser.Controller.MediaEncoding { return "wmav2"; } + if (string.Equals(codec, "opus", StringComparison.OrdinalIgnoreCase)) + { + return "libopus"; + } return codec.ToLower(); } diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs index 6be94e7e6..17b82b3ca 100644 --- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs +++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs @@ -36,7 +36,7 @@ namespace MediaBrowser.Controller.Net /// </summary> /// <param name="state">The state.</param> /// <returns>Task{`1}.</returns> - protected abstract Task<TReturnDataType> GetDataToSend(TStateType state); + protected abstract Task<TReturnDataType> GetDataToSend(TStateType state, CancellationToken cancellationToken); /// <summary> /// The logger @@ -209,7 +209,9 @@ namespace MediaBrowser.Controller.Net { var state = tuple.Item4; - var data = await GetDataToSend(state).ConfigureAwait(false); + var cancellationToken = tuple.Item2.Token; + + var data = await GetDataToSend(state, cancellationToken).ConfigureAwait(false); if (data != null) { @@ -218,7 +220,7 @@ namespace MediaBrowser.Controller.Net MessageType = Name, Data = data - }, tuple.Item2.Token).ConfigureAwait(false); + }, cancellationToken).ConfigureAwait(false); state.DateLastSendUtc = DateTime.UtcNow; } diff --git a/MediaBrowser.Controller/Session/ISessionController.cs b/MediaBrowser.Controller/Session/ISessionController.cs index f8a6ed1fc..0d8c207b6 100644 --- a/MediaBrowser.Controller/Session/ISessionController.cs +++ b/MediaBrowser.Controller/Session/ISessionController.cs @@ -55,10 +55,7 @@ namespace MediaBrowser.Controller.Session /// <summary> /// Sends the restart required message. /// </summary> - /// <param name="info">The information.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - Task SendRestartRequiredNotification(SystemInfo info, CancellationToken cancellationToken); + Task SendRestartRequiredNotification(CancellationToken cancellationToken); /// <summary> /// Sends the user data change info. diff --git a/MediaBrowser.Controller/Sync/ISyncManager.cs b/MediaBrowser.Controller/Sync/ISyncManager.cs index 0bf4b914f..5e9085a40 100644 --- a/MediaBrowser.Controller/Sync/ISyncManager.cs +++ b/MediaBrowser.Controller/Sync/ISyncManager.cs @@ -29,7 +29,7 @@ namespace MediaBrowser.Controller.Sync /// Gets the jobs. /// </summary> /// <returns>QueryResult<SyncJob>.</returns> - Task<QueryResult<SyncJob>> GetJobs(SyncJobQuery query); + QueryResult<SyncJob> GetJobs(SyncJobQuery query); /// <summary> /// Gets the job items. diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index f2c3b7cc8..41ed0648a 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -61,6 +61,8 @@ namespace MediaBrowser.Model.Configuration /// <value><c>true</c> if this instance is port authorized; otherwise, <c>false</c>.</value> public bool IsPortAuthorized { get; set; } + public bool AutoRunWebApp { get; set; } + /// <summary> /// Gets or sets a value indicating whether [enable case sensitive item ids]. /// </summary> diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 93a46aaf4..6ded1f6dd 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -186,6 +186,11 @@ namespace MediaBrowser.Model.Dlna return MediaSource.Path; } + if (string.IsNullOrWhiteSpace(PlaySessionId)) + { + PlaySessionId = Guid.NewGuid().ToString("N"); + } + string dlnaCommand = BuildDlnaParam(this, accessToken); return GetUrl(baseUrl, dlnaCommand); } diff --git a/MediaBrowser.Model/Entities/LibraryUpdateInfo.cs b/MediaBrowser.Model/Entities/LibraryUpdateInfo.cs index b3d3be70e..8995e0888 100644 --- a/MediaBrowser.Model/Entities/LibraryUpdateInfo.cs +++ b/MediaBrowser.Model/Entities/LibraryUpdateInfo.cs @@ -35,6 +35,8 @@ namespace MediaBrowser.Model.Entities /// <value>The items updated.</value> public string[] ItemsUpdated { get; set; } + public string[] CollectionFolders { get; set; } + /// <summary> /// Initializes a new instance of the <see cref="LibraryUpdateInfo"/> class. /// </summary> @@ -45,6 +47,7 @@ namespace MediaBrowser.Model.Entities ItemsAdded = new string[] { }; ItemsRemoved = new string[] { }; ItemsUpdated = new string[] { }; + CollectionFolders = new string[] { }; } } } diff --git a/MediaBrowser.Model/IO/IZipClient.cs b/MediaBrowser.Model/IO/IZipClient.cs index 2dc4880c2..4ebcba9d8 100644 --- a/MediaBrowser.Model/IO/IZipClient.cs +++ b/MediaBrowser.Model/IO/IZipClient.cs @@ -24,6 +24,7 @@ namespace MediaBrowser.Model.IO void ExtractAll(Stream source, string targetPath, bool overwriteExistingFiles); void ExtractAllFromGz(Stream source, string targetPath, bool overwriteExistingFiles); + void ExtractFirstFileFromGz(Stream source, string targetPath, string defaultFileName); /// <summary> /// Extracts all from zip. diff --git a/MediaBrowser.Model/LiveTv/ProgramQuery.cs b/MediaBrowser.Model/LiveTv/ProgramQuery.cs index c0959635f..ec3f8ad67 100644 --- a/MediaBrowser.Model/LiveTv/ProgramQuery.cs +++ b/MediaBrowser.Model/LiveTv/ProgramQuery.cs @@ -14,6 +14,7 @@ namespace MediaBrowser.Model.LiveTv ChannelIds = new string[] { }; OrderBy = new Tuple<string, SortOrder>[] { }; Genres = new string[] { }; + GenreIds = new string[] { }; EnableTotalRecordCount = true; EnableUserData = true; } @@ -110,6 +111,7 @@ namespace MediaBrowser.Model.LiveTv /// Limit results to items containing specific genres /// </summary> /// <value>The genres.</value> + public string[] GenreIds { get; set; } public string[] Genres { get; set; } } }
\ No newline at end of file diff --git a/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs b/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs index 4bc506bf6..3d137256e 100644 --- a/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs +++ b/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs @@ -13,12 +13,14 @@ namespace MediaBrowser.Model.LiveTv public bool? EnableImages { get; set; } public int? ImageTypeLimit { get; set; } public ImageType[] EnableImageTypes { get; set; } + public string[] GenreIds { get; set; } public bool EnableTotalRecordCount { get; set; } public RecommendedProgramQuery() { EnableTotalRecordCount = true; + GenreIds = new string[] { }; } /// <summary> diff --git a/MediaBrowser.Model/Querying/QueryFilters.cs b/MediaBrowser.Model/Querying/QueryFilters.cs index dd575c2a8..3a261857b 100644 --- a/MediaBrowser.Model/Querying/QueryFilters.cs +++ b/MediaBrowser.Model/Querying/QueryFilters.cs @@ -1,14 +1,15 @@ - +using MediaBrowser.Model.Dto; + namespace MediaBrowser.Model.Querying { - public class QueryFilters + public class QueryFiltersLegacy { public string[] Genres { get; set; } public string[] Tags { get; set; } public string[] OfficialRatings { get; set; } public int[] Years { get; set; } - public QueryFilters() + public QueryFiltersLegacy() { Genres = new string[] { }; Tags = new string[] { }; @@ -16,4 +17,15 @@ namespace MediaBrowser.Model.Querying Years = new int[] { }; } } + public class QueryFilters + { + public NameIdPair[] Genres { get; set; } + public string[] Tags { get; set; } + + public QueryFilters() + { + Tags = new string[] { }; + Genres = new NameIdPair[] { }; + } + } } diff --git a/MediaBrowser.Model/Search/SearchHint.cs b/MediaBrowser.Model/Search/SearchHint.cs index 5c5637481..f2617c909 100644 --- a/MediaBrowser.Model/Search/SearchHint.cs +++ b/MediaBrowser.Model/Search/SearchHint.cs @@ -107,6 +107,7 @@ namespace MediaBrowser.Model.Search /// </summary> /// <value>The album.</value> public string Album { get; set; } + public string AlbumId { get; set; } /// <summary> /// Gets or sets the album artist. diff --git a/MediaBrowser.Model/Session/PlayRequest.cs b/MediaBrowser.Model/Session/PlayRequest.cs index d50cb5953..3477f2e57 100644 --- a/MediaBrowser.Model/Session/PlayRequest.cs +++ b/MediaBrowser.Model/Session/PlayRequest.cs @@ -37,5 +37,6 @@ namespace MediaBrowser.Model.Session public int? SubtitleStreamIndex { get; set; } public int? AudioStreamIndex { get; set; } public string MediaSourceId { get; set; } + public int? StartIndex { get; set; } } }
\ No newline at end of file diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs index b61d63729..9ed0f904f 100644 --- a/MediaBrowser.Model/System/SystemInfo.cs +++ b/MediaBrowser.Model/System/SystemInfo.cs @@ -68,6 +68,8 @@ namespace MediaBrowser.Model.System /// <value><c>true</c> if this instance can self update; otherwise, <c>false</c>.</value> public bool CanSelfUpdate { get; set; } + public bool CanLaunchWebBrowser { get; set; } + /// <summary> /// Gets or sets plugin assemblies that failed to load. /// </summary> diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs index e79aec33c..6a2677b43 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs @@ -153,7 +153,6 @@ namespace MediaBrowser.Providers.MediaInfo if (item.IsShortcut) { FetchShortcutInfo(item); - return Task.FromResult(ItemUpdateType.MetadataImport); } var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem, _config, _subtitleManager, _chapterManager, _libraryManager); diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 158238557..443eb6eda 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -75,49 +75,54 @@ namespace MediaBrowser.Providers.MediaInfo try { - string[] streamFileNames = null; + Model.MediaInfo.MediaInfo mediaInfoResult = null; - if (item.VideoType == VideoType.Iso) + if (!item.IsShortcut) { - item.IsoType = DetermineIsoType(isoMount); - } + string[] streamFileNames = null; - if (item.VideoType == VideoType.Dvd || (item.IsoType.HasValue && item.IsoType == IsoType.Dvd)) - { - streamFileNames = FetchFromDvdLib(item, isoMount); + if (item.VideoType == VideoType.Iso) + { + item.IsoType = DetermineIsoType(isoMount); + } - if (streamFileNames.Length == 0) + if (item.VideoType == VideoType.Dvd || (item.IsoType.HasValue && item.IsoType == IsoType.Dvd)) { - _logger.Error("No playable vobs found in dvd structure, skipping ffprobe."); - return ItemUpdateType.MetadataImport; + streamFileNames = FetchFromDvdLib(item, isoMount); + + if (streamFileNames.Length == 0) + { + _logger.Error("No playable vobs found in dvd structure, skipping ffprobe."); + return ItemUpdateType.MetadataImport; + } } - } - else if (item.VideoType == VideoType.BluRay || (item.IsoType.HasValue && item.IsoType == IsoType.BluRay)) - { - var inputPath = isoMount != null ? isoMount.MountedPath : item.Path; + else if (item.VideoType == VideoType.BluRay || (item.IsoType.HasValue && item.IsoType == IsoType.BluRay)) + { + var inputPath = isoMount != null ? isoMount.MountedPath : item.Path; - blurayDiscInfo = GetBDInfo(inputPath); + blurayDiscInfo = GetBDInfo(inputPath); - streamFileNames = blurayDiscInfo.Files; + streamFileNames = blurayDiscInfo.Files; - if (streamFileNames.Length == 0) - { - _logger.Error("No playable vobs found in bluray structure, skipping ffprobe."); - return ItemUpdateType.MetadataImport; + if (streamFileNames.Length == 0) + { + _logger.Error("No playable vobs found in bluray structure, skipping ffprobe."); + return ItemUpdateType.MetadataImport; + } } - } - if (streamFileNames == null) - { - streamFileNames = new string[] { }; - } + if (streamFileNames == null) + { + streamFileNames = new string[] { }; + } - var result = await GetMediaInfo(item, isoMount, streamFileNames, cancellationToken).ConfigureAwait(false); + mediaInfoResult = await GetMediaInfo(item, isoMount, streamFileNames, cancellationToken).ConfigureAwait(false); - cancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); + } - await Fetch(item, cancellationToken, result, isoMount, blurayDiscInfo, options).ConfigureAwait(false); + await Fetch(item, cancellationToken, mediaInfoResult, isoMount, blurayDiscInfo, options).ConfigureAwait(false); } finally @@ -162,43 +167,60 @@ namespace MediaBrowser.Providers.MediaInfo BlurayDiscInfo blurayInfo, MetadataRefreshOptions options) { - var mediaStreams = mediaInfo.MediaStreams; + List<MediaStream> mediaStreams; + List<ChapterInfo> chapters; - video.TotalBitrate = mediaInfo.Bitrate; - //video.FormatName = (mediaInfo.Container ?? string.Empty) - // .Replace("matroska", "mkv", StringComparison.OrdinalIgnoreCase); + if (mediaInfo != null) + { + mediaStreams = mediaInfo.MediaStreams; - // For dvd's this may not always be accurate, so don't set the runtime if the item already has one - var needToSetRuntime = video.VideoType != VideoType.Dvd || video.RunTimeTicks == null || video.RunTimeTicks.Value == 0; + video.TotalBitrate = mediaInfo.Bitrate; + //video.FormatName = (mediaInfo.Container ?? string.Empty) + // .Replace("matroska", "mkv", StringComparison.OrdinalIgnoreCase); - if (needToSetRuntime) - { - video.RunTimeTicks = mediaInfo.RunTimeTicks; - } + // For dvd's this may not always be accurate, so don't set the runtime if the item already has one + var needToSetRuntime = video.VideoType != VideoType.Dvd || video.RunTimeTicks == null || video.RunTimeTicks.Value == 0; - if (video.VideoType == VideoType.VideoFile) - { - var extension = (Path.GetExtension(video.Path) ?? string.Empty).TrimStart('.'); + if (needToSetRuntime) + { + video.RunTimeTicks = mediaInfo.RunTimeTicks; + } + + if (video.VideoType == VideoType.VideoFile) + { + var extension = (Path.GetExtension(video.Path) ?? string.Empty).TrimStart('.'); + + video.Container = extension; + } + else + { + video.Container = null; + } + video.Container = mediaInfo.Container; - video.Container = extension; + chapters = mediaInfo.Chapters == null ? new List<ChapterInfo>() : mediaInfo.Chapters.ToList(); + if (blurayInfo != null) + { + FetchBdInfo(video, chapters, mediaStreams, blurayInfo); + } } else { - video.Container = null; - } - video.Container = mediaInfo.Container; - - var chapters = mediaInfo.Chapters == null ? new List<ChapterInfo>() : mediaInfo.Chapters.ToList(); - if (blurayInfo != null) - { - FetchBdInfo(video, chapters, mediaStreams, blurayInfo); + mediaStreams = new List<MediaStream>(); + chapters = new List<ChapterInfo>(); } await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); + var libraryOptions = _libraryManager.GetLibraryOptions(video); - FetchEmbeddedInfo(video, mediaInfo, options, libraryOptions); - FetchPeople(video, mediaInfo, options); + if (mediaInfo != null) + { + FetchEmbeddedInfo(video, mediaInfo, options, libraryOptions); + FetchPeople(video, mediaInfo, options); + video.Timestamp = mediaInfo.Timestamp; + video.Video3DFormat = video.Video3DFormat ?? mediaInfo.Video3DFormat; + } video.IsHD = mediaStreams.Any(i => i.Type == MediaStreamType.Video && i.Width.HasValue && i.Width.Value >= 1260); @@ -207,9 +229,6 @@ namespace MediaBrowser.Providers.MediaInfo video.DefaultVideoStreamIndex = videoStream == null ? (int?)null : videoStream.Index; video.HasSubtitles = mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle); - video.Timestamp = mediaInfo.Timestamp; - - video.Video3DFormat = video.Video3DFormat ?? mediaInfo.Video3DFormat; _itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken); diff --git a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs index 65742f6e6..a000ed36e 100644 --- a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs +++ b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs @@ -262,7 +262,7 @@ namespace MediaBrowser.Providers.Movies var keepTypes = new[] { PersonType.Director, - PersonType.Writer, + //PersonType.Writer, //PersonType.Producer }; diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs index f55d95a2e..5fd94e0d7 100644 --- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs +++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs @@ -200,7 +200,16 @@ namespace MediaBrowser.WebDashboard.Api sb.Append("<link rel=\"manifest\" href=\"manifest.json\">"); sb.Append("<meta name=\"format-detection\" content=\"telephone=no\">"); sb.Append("<meta name=\"msapplication-tap-highlight\" content=\"no\">"); - sb.Append("<meta name=\"viewport\" content=\"user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width\">"); + + if (string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase)) + { + sb.Append("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no\">"); + } + else + { + sb.Append("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, minimum-scale=1\">"); + } + sb.Append("<meta name=\"apple-mobile-web-app-capable\" content=\"yes\">"); sb.Append("<meta name=\"mobile-web-app-capable\" content=\"yes\">"); sb.Append("<meta name=\"application-name\" content=\"Emby\">"); diff --git a/RSSDP/HttpRequestParser.cs b/RSSDP/HttpRequestParser.cs index 0923f291f..bedbbe675 100644 --- a/RSSDP/HttpRequestParser.cs +++ b/RSSDP/HttpRequestParser.cs @@ -64,7 +64,7 @@ namespace Rssdp.Infrastructure if (message == null) throw new ArgumentNullException("message"); var parts = data.Split(' '); - if (parts.Length < 3) throw new ArgumentException("Status line is invalid. Insufficient status parts.", "data"); + if (parts.Length < 2) throw new ArgumentException("Status line is invalid. Insufficient status parts.", "data"); message.Method = new HttpMethod(parts[0].Trim()); Uri requestUri; @@ -73,8 +73,11 @@ namespace Rssdp.Infrastructure else System.Diagnostics.Debug.WriteLine(parts[1]); - message.Version = ParseHttpVersion(parts[2].Trim()); - } + if (parts.Length >= 3) + { + message.Version = ParseHttpVersion(parts[2].Trim()); + } + } /// <summary> /// Returns a boolean indicating whether the specified HTTP header name represents a content header (true), or a message header (false). diff --git a/RSSDP/HttpResponseParser.cs b/RSSDP/HttpResponseParser.cs index ba85a1657..5f297ca9a 100644 --- a/RSSDP/HttpResponseParser.cs +++ b/RSSDP/HttpResponseParser.cs @@ -75,7 +75,7 @@ namespace Rssdp.Infrastructure if (message == null) throw new ArgumentNullException("message"); var parts = data.Split(' '); - if (parts.Length < 3) throw new ArgumentException("data status line is invalid. Insufficient status parts.", "data"); + if (parts.Length < 2) throw new ArgumentException("data status line is invalid. Insufficient status parts.", "data"); message.Version = ParseHttpVersion(parts[0].Trim()); @@ -84,8 +84,12 @@ namespace Rssdp.Infrastructure throw new ArgumentException("data status line is invalid. Status code is not a valid integer.", "data"); message.StatusCode = (HttpStatusCode)statusCode; - message.ReasonPhrase = parts[2].Trim(); - } + + if (parts.Length >= 3) + { + message.ReasonPhrase = parts[2].Trim(); + } + } #endregion diff --git a/RSSDP/SsdpDevice.cs b/RSSDP/SsdpDevice.cs index cda11f0a4..65d9be139 100644 --- a/RSSDP/SsdpDevice.cs +++ b/RSSDP/SsdpDevice.cs @@ -743,6 +743,14 @@ namespace Rssdp private static void AddCustomProperty(XmlReader reader, SsdpDevice device) { + // If the property is an empty element, there is no value to read + // Advance the reader and return + if (reader.IsEmptyElement) + { + reader.Read(); + return; + } + var newProp = new SsdpDeviceProperty() { Namespace = reader.Prefix, Name = reader.LocalName }; int depth = reader.Depth; reader.Read(); diff --git a/SharedVersion.cs b/SharedVersion.cs index 580bc7001..c683d0245 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.40.0")] +[assembly: AssemblyVersion("3.2.50.0")] |
