diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs index 16e8a434a1..78aa5e1657 100644 --- a/MediaBrowser.Api/ItemUpdateService.cs +++ b/MediaBrowser.Api/ItemUpdateService.cs @@ -245,12 +245,8 @@ namespace MediaBrowser.Api item.OriginalTitle = string.IsNullOrWhiteSpace(request.OriginalTitle) ? null : request.OriginalTitle; - var hasCriticRating = item as IHasCriticRating; - if (hasCriticRating != null) - { - hasCriticRating.CriticRating = request.CriticRating; - hasCriticRating.CriticRatingSummary = request.CriticRatingSummary; - } + item.CriticRating = request.CriticRating; + item.CriticRatingSummary = request.CriticRatingSummary; item.DisplayMediaType = request.DisplayMediaType; item.CommunityRating = request.CommunityRating; @@ -279,11 +275,7 @@ namespace MediaBrowser.Api item.Tagline = request.Taglines.FirstOrDefault(); } - var hasShortOverview = item as IHasShortOverview; - if (hasShortOverview != null) - { - hasShortOverview.ShortOverview = request.ShortOverview; - } + item.ShortOverview = request.ShortOverview; item.Keywords = request.Keywords; diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index 4217cd6abe..90767b135b 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -19,6 +19,7 @@ using System.Threading.Tasks; using CommonIO; using MediaBrowser.Api.Playback.Progressive; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Server.Implementations.LiveTv.EmbyTV; namespace MediaBrowser.Api.LiveTv @@ -390,6 +391,7 @@ namespace MediaBrowser.Api.LiveTv public bool? EnableUserData { get; set; } public string SeriesTimerId { get; set; } + public string LibrarySeriesId { get; set; } /// /// Fields to return within the items, in addition to basic information @@ -990,6 +992,17 @@ namespace MediaBrowser.Api.LiveTv query.SeriesTimerId = request.SeriesTimerId; query.Genres = (request.Genres ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + if (!string.IsNullOrWhiteSpace(request.LibrarySeriesId)) + { + query.IsSeries = true; + + var series = _libraryManager.GetItemById(request.LibrarySeriesId) as Series; + if (series != null) + { + query.Name = series.Name; + } + } + var result = await _liveTvManager.GetPrograms(query, GetDtoOptions(request), CancellationToken.None).ConfigureAwait(false); return ToOptimizedResult(result); diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index eb80ae89e6..dc26218a52 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -317,13 +317,32 @@ namespace MediaBrowser.Api.Playback } if (string.Equals(hwType, "vaapi", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(encodingOptions.VaapiDevice)) { - return GetAvailableEncoder("h264_vaapi", defaultEncoder); + if (IsVaapiSupported(state)) + { + return GetAvailableEncoder("h264_vaapi", defaultEncoder); + } } } return defaultEncoder; } + private bool IsVaapiSupported(StreamState state) + { + var videoStream = state.VideoStream; + + if (videoStream != null) + { + // vaapi will throw an error with this input + // [vaapi @ 0x7faed8000960] No VAAPI support for codec mpeg4 profile -99. + if (string.Equals(videoStream.Codec, "mpeg4", StringComparison.OrdinalIgnoreCase) && videoStream.Level == -99) + { + return false; + } + } + return true; + } + private string GetAvailableEncoder(string preferredEncoder, string defaultEncoder) { if (MediaEncoder.SupportsEncoder(preferredEncoder)) diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 270b068fd1..97b386d73b 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -887,7 +887,6 @@ namespace MediaBrowser.Api.Playback.Hls var mapArgs = state.IsOutputVideo ? GetMapArgs(state) : string.Empty; var enableSplittingOnNonKeyFrames = state.VideoRequest.EnableSplittingOnNonKeyFrames && string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase); enableSplittingOnNonKeyFrames = false; - // TODO: check libavformat version for 57 50.100 and use -hls_flags split_by_time var hlsProtocolSupportsSplittingByTime = false; diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 7fe7d5a21d..0611adea5f 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -125,7 +125,7 @@ namespace MediaBrowser.Api.Playback SetDeviceSpecificData(item, result.MediaSource, profile, authInfo, request.MaxStreamingBitrate, request.StartTimeTicks ?? 0, result.MediaSource.Id, request.AudioStreamIndex, - request.SubtitleStreamIndex, request.PlaySessionId, request.UserId); + request.SubtitleStreamIndex, request.MaxAudioChannels, request.PlaySessionId, request.UserId); } else { @@ -167,7 +167,7 @@ namespace MediaBrowser.Api.Playback { var mediaSourceId = request.MediaSourceId; - SetDeviceSpecificData(request.Id, info, profile, authInfo, request.MaxStreamingBitrate ?? profile.MaxStreamingBitrate, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex, request.UserId); + SetDeviceSpecificData(request.Id, info, profile, authInfo, request.MaxStreamingBitrate ?? profile.MaxStreamingBitrate, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex, request.MaxAudioChannels, request.UserId); } return ToOptimizedResult(info); @@ -230,13 +230,14 @@ namespace MediaBrowser.Api.Playback string mediaSourceId, int? audioStreamIndex, int? subtitleStreamIndex, + int? maxAudioChannels, string userId) { var item = _libraryManager.GetItemById(itemId); foreach (var mediaSource in result.MediaSources) { - SetDeviceSpecificData(item, mediaSource, profile, auth, maxBitrate, startTimeTicks, mediaSourceId, audioStreamIndex, subtitleStreamIndex, result.PlaySessionId, userId); + SetDeviceSpecificData(item, mediaSource, profile, auth, maxBitrate, startTimeTicks, mediaSourceId, audioStreamIndex, subtitleStreamIndex, maxAudioChannels, result.PlaySessionId, userId); } SortMediaSources(result, maxBitrate); @@ -251,6 +252,7 @@ namespace MediaBrowser.Api.Playback string mediaSourceId, int? audioStreamIndex, int? subtitleStreamIndex, + int? maxAudioChannels, string playSessionId, string userId) { @@ -262,7 +264,8 @@ namespace MediaBrowser.Api.Playback Context = EncodingContext.Streaming, DeviceId = auth.DeviceId, ItemId = item.Id.ToString("N"), - Profile = profile + Profile = profile, + MaxAudioChannels = maxAudioChannels }; if (string.Equals(mediaSourceId, mediaSource.Id, StringComparison.OrdinalIgnoreCase)) diff --git a/MediaBrowser.Common.Implementations/Logging/NLogger.cs b/MediaBrowser.Common.Implementations/Logging/NLogger.cs index 97bc437a0f..11f41261a2 100644 --- a/MediaBrowser.Common.Implementations/Logging/NLogger.cs +++ b/MediaBrowser.Common.Implementations/Logging/NLogger.cs @@ -127,7 +127,9 @@ namespace MediaBrowser.Common.Implementations.Logging { for (var i = 0; i < paramList.Length; i++) { - message = message.Replace("{" + i + "}", paramList[i].ToString()); + var obj = paramList[i]; + + message = message.Replace("{" + i + "}", (obj == null ? "null" : obj.ToString())); } } diff --git a/MediaBrowser.Controller/Dlna/ISsdpHandler.cs b/MediaBrowser.Controller/Dlna/ISsdpHandler.cs deleted file mode 100644 index ec3a00aad7..0000000000 --- a/MediaBrowser.Controller/Dlna/ISsdpHandler.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; - -namespace MediaBrowser.Controller.Dlna -{ - public interface ISsdpHandler - { - } -} diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 3ebefa2170..e1a7741c94 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -33,7 +33,7 @@ namespace MediaBrowser.Controller.Entities /// /// Class BaseItem /// - public abstract class BaseItem : IHasProviderIds, ILibraryItem, IHasImages, IHasUserData, IHasMetadata, IHasLookupInfo + public abstract class BaseItem : IHasProviderIds, IHasImages, IHasUserData, IHasMetadata, IHasLookupInfo { protected BaseItem() { diff --git a/MediaBrowser.Controller/Entities/BasePluginFolder.cs b/MediaBrowser.Controller/Entities/BasePluginFolder.cs index 5a1ad6b157..bd109af7a7 100644 --- a/MediaBrowser.Controller/Entities/BasePluginFolder.cs +++ b/MediaBrowser.Controller/Entities/BasePluginFolder.cs @@ -7,7 +7,7 @@ namespace MediaBrowser.Controller.Entities /// Plugins derive from and export this class to create a folder that will appear in the root along /// with all the other actual physical folders in the system. /// - public abstract class BasePluginFolder : Folder, ICollectionFolder, IByReferenceItem + public abstract class BasePluginFolder : Folder, ICollectionFolder { public virtual string CollectionType { diff --git a/MediaBrowser.Controller/Entities/IByReferenceItem.cs b/MediaBrowser.Controller/Entities/IByReferenceItem.cs deleted file mode 100644 index b071473e11..0000000000 --- a/MediaBrowser.Controller/Entities/IByReferenceItem.cs +++ /dev/null @@ -1,12 +0,0 @@ - -namespace MediaBrowser.Controller.Entities -{ - /// - /// This is a marker class that tells us that a particular item type may be physically resolved - /// more than once within the library and we need to be sure to resolve them all to the same - /// instance of that item. - /// - public interface IByReferenceItem - { - } -} diff --git a/MediaBrowser.Controller/Entities/IHasCriticRating.cs b/MediaBrowser.Controller/Entities/IHasCriticRating.cs deleted file mode 100644 index d2b93759d8..0000000000 --- a/MediaBrowser.Controller/Entities/IHasCriticRating.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace MediaBrowser.Controller.Entities -{ - /// - /// Interface IHasCriticRating - /// - public interface IHasCriticRating - { - /// - /// Gets or sets the critic rating. - /// - /// The critic rating. - float? CriticRating { get; set; } - - /// - /// Gets or sets the critic rating summary. - /// - /// The critic rating summary. - string CriticRatingSummary { get; set; } - } -} diff --git a/MediaBrowser.Controller/Entities/IHasShortOverview.cs b/MediaBrowser.Controller/Entities/IHasShortOverview.cs deleted file mode 100644 index 437201faaf..0000000000 --- a/MediaBrowser.Controller/Entities/IHasShortOverview.cs +++ /dev/null @@ -1,12 +0,0 @@ - -namespace MediaBrowser.Controller.Entities -{ - public interface IHasShortOverview - { - /// - /// Gets or sets the short overview. - /// - /// The short overview. - string ShortOverview { get; set; } - } -} diff --git a/MediaBrowser.Controller/Entities/ILibraryItem.cs b/MediaBrowser.Controller/Entities/ILibraryItem.cs deleted file mode 100644 index b2f39608f3..0000000000 --- a/MediaBrowser.Controller/Entities/ILibraryItem.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; - -namespace MediaBrowser.Controller.Entities -{ - /// - /// Interface ILibraryItem - /// - public interface ILibraryItem - { - /// - /// Gets the name. - /// - /// The name. - string Name { get; } - - /// - /// Gets the id. - /// - /// The id. - Guid Id { get; } - - /// - /// Gets the path. - /// - /// The path. - string Path { get; } - } -} diff --git a/MediaBrowser.Controller/Entities/ImageSourceInfo.cs b/MediaBrowser.Controller/Entities/ImageSourceInfo.cs deleted file mode 100644 index 6dc072431c..0000000000 --- a/MediaBrowser.Controller/Entities/ImageSourceInfo.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace MediaBrowser.Controller.Entities -{ - public class ImageSourceInfo - { - public Guid ImagePathMD5 { get; set; } - public Guid ImageUrlMD5 { get; set; } - } -} diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs index dea42c463a..f13adb21c7 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs @@ -15,7 +15,7 @@ namespace MediaBrowser.Controller.Entities.Movies /// /// Class Movie /// - public class Movie : Video, IHasCriticRating, IHasSpecialFeatures, IHasBudget, IHasTrailers, IHasAwards, IHasMetascore, IHasLookupInfo, ISupportsBoxSetGrouping, IHasOriginalTitle + public class Movie : Video, IHasSpecialFeatures, IHasBudget, IHasTrailers, IHasAwards, IHasMetascore, IHasLookupInfo, ISupportsBoxSetGrouping, IHasOriginalTitle { public List SpecialFeatureIds { get; set; } diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 62af141593..ce13f5fc5f 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -36,7 +36,7 @@ namespace MediaBrowser.Controller.Entities.TV { get { - return true; + return false; } } diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs index 0bcd5c14e8..0780cfec5e 100644 --- a/MediaBrowser.Controller/Entities/Trailer.cs +++ b/MediaBrowser.Controller/Entities/Trailer.cs @@ -10,7 +10,7 @@ namespace MediaBrowser.Controller.Entities /// /// Class Trailer /// - public class Trailer : Video, IHasCriticRating, IHasBudget, IHasMetascore, IHasOriginalTitle, IHasLookupInfo + public class Trailer : Video, IHasBudget, IHasMetascore, IHasOriginalTitle, IHasLookupInfo { public Trailer() { diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 3b7e3c5d29..38397572e3 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -1668,16 +1668,7 @@ namespace MediaBrowser.Controller.Entities { var val = query.MinCriticRating.Value; - var hasCriticRating = item as IHasCriticRating; - - if (hasCriticRating != null) - { - if (!(hasCriticRating.CriticRating.HasValue && hasCriticRating.CriticRating >= val)) - { - return false; - } - } - else + if (!(item.CriticRating.HasValue && item.CriticRating >= val)) { return false; } diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index e87b726b23..78d7a7fdd9 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -24,7 +24,6 @@ namespace MediaBrowser.Controller.Entities IHasAspectRatio, ISupportsPlaceHolders, IHasMediaSources, - IHasShortOverview, IThemeMedia { [IgnoreDataMember] diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs index 610d4b6f8a..5e99d6fa3f 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs @@ -78,14 +78,17 @@ namespace MediaBrowser.Controller.LiveTv protected override string CreateSortName() { - double number = 0; - if (!string.IsNullOrEmpty(Number)) { - double.TryParse(Number, NumberStyles.Any, CultureInfo.InvariantCulture, out number); + double number = 0; + + if (double.TryParse(Number, NumberStyles.Any, CultureInfo.InvariantCulture, out number)) + { + return number.ToString("00000-") + (Name ?? string.Empty); + } } - return number.ToString("00000-") + (Name ?? string.Empty); + return Number + "-" + (Name ?? string.Empty); } [IgnoreDataMember] diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 11ed0f6741..36d59d3e44 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -111,7 +111,6 @@ - @@ -132,10 +131,8 @@ - - @@ -146,15 +143,12 @@ - - - diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index 13d43eee67..931af293cc 100644 --- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs @@ -185,14 +185,12 @@ namespace MediaBrowser.Controller.Providers { var text = reader.ReadElementContentAsString(); - var hasCriticRating = item as IHasCriticRating; - - if (hasCriticRating != null && !string.IsNullOrEmpty(text)) + if (!string.IsNullOrEmpty(text)) { float value; if (float.TryParse(text, NumberStyles.Any, _usCulture, out value)) { - hasCriticRating.CriticRating = value; + item.CriticRating = value; } } @@ -292,12 +290,7 @@ namespace MediaBrowser.Controller.Providers if (!string.IsNullOrWhiteSpace(val)) { - var hasShortOverview = item as IHasShortOverview; - - if (hasShortOverview != null) - { - hasShortOverview.ShortOverview = val; - } + item.ShortOverview = val; } break; @@ -309,12 +302,7 @@ namespace MediaBrowser.Controller.Providers if (!string.IsNullOrWhiteSpace(val)) { - var hasCriticRating = item as IHasCriticRating; - - if (hasCriticRating != null) - { - hasCriticRating.CriticRatingSummary = val; - } + item.CriticRatingSummary = val; } break; diff --git a/MediaBrowser.Controller/Providers/DynamicImageResponse.cs b/MediaBrowser.Controller/Providers/DynamicImageResponse.cs index fdd1891ed6..d19a28a24c 100644 --- a/MediaBrowser.Controller/Providers/DynamicImageResponse.cs +++ b/MediaBrowser.Controller/Providers/DynamicImageResponse.cs @@ -12,7 +12,6 @@ namespace MediaBrowser.Controller.Providers public Stream Stream { get; set; } public ImageFormat Format { get; set; } public bool HasImage { get; set; } - public string InternalCacheKey { get; set; } public void SetFormatFromMimeType(string mimeType) { diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs index d3e5685bb1..428651ed5b 100644 --- a/MediaBrowser.Controller/Providers/IProviderManager.cs +++ b/MediaBrowser.Controller/Providers/IProviderManager.cs @@ -69,28 +69,8 @@ namespace MediaBrowser.Controller.Providers /// /// Saves the image. /// - /// The item. - /// The source. - /// Type of the MIME. - /// The type. - /// Index of the image. - /// The internal cache key. - /// The cancellation token. /// Task. - Task SaveImage(IHasImages item, Stream source, string mimeType, ImageType type, int? imageIndex, string internalCacheKey, CancellationToken cancellationToken); - - /// - /// Saves the image. - /// - /// The item. - /// The source. - /// Type of the MIME. - /// The type. - /// Index of the image. - /// The internal cache key. - /// The cancellation token. - /// Task. - Task SaveImage(IHasImages item, string source, string mimeType, ImageType type, int? imageIndex, string internalCacheKey, CancellationToken cancellationToken); + Task SaveImage(IHasImages item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken); /// /// Adds the metadata providers. diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs index f53dec3bff..a5091bf01e 100644 --- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs +++ b/MediaBrowser.Dlna/Didl/DidlBuilder.cs @@ -585,10 +585,9 @@ namespace MediaBrowser.Dlna.Didl { var desc = item.Overview; - var hasShortOverview = item as IHasShortOverview; - if (hasShortOverview != null && !string.IsNullOrEmpty(hasShortOverview.ShortOverview)) + if (!string.IsNullOrEmpty(item.ShortOverview)) { - desc = hasShortOverview.ShortOverview; + desc = item.ShortOverview; } if (!string.IsNullOrWhiteSpace(desc)) @@ -697,16 +696,36 @@ namespace MediaBrowser.Dlna.Didl private void AddPeople(BaseItem item, XmlElement element) { - var types = new[] { PersonType.Director, PersonType.Writer, PersonType.Producer, PersonType.Composer, "Creator" }; + var types = new[] + { + PersonType.Director, + PersonType.Writer, + PersonType.Producer, + PersonType.Composer, + "Creator" + }; var people = _libraryManager.GetPeople(item); + var index = 0; + + // Seeing some LG models locking up due content with large lists of people + // The actual issue might just be due to processing a more metadata than it can handle + var limit = 10; + foreach (var actor in people) { var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase)) ?? PersonType.Actor; AddValue(element, "upnp", type.ToLower(), actor.Name, NS_UPNP); + + index++; + + if (index >= limit) + { + break; + } } } diff --git a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs index a8aedaed87..277f80b478 100644 --- a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs +++ b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs @@ -19,6 +19,7 @@ using System.Net; using System.Threading.Tasks; using MediaBrowser.Controller.MediaEncoding; using Rssdp; +using Rssdp.Infrastructure; namespace MediaBrowser.Dlna.Main { @@ -154,8 +155,14 @@ namespace MediaBrowser.Dlna.Main } } + private void LogMessage(string msg) + { + //_logger.Debug(msg); + } + private void StartPublishing() { + SsdpDevicePublisherBase.LogFunction = LogMessage; _Publisher = new SsdpDevicePublisher(); } @@ -235,48 +242,71 @@ namespace MediaBrowser.Dlna.Main var addressString = address.ToString(); - var udn = (addressString).GetMD5().ToString("N"); + var udn = CreateUuid(addressString); - var services = new List + var fullService = "urn:schemas-upnp-org:device:MediaServer:1"; + + _logger.Info("Registering publisher for {0} on {1}", fullService, addressString); + + var descriptorUri = "/dlna/" + udn + "/description.xml"; + var uri = new Uri(_appHost.GetLocalApiUrl(address) + descriptorUri); + + var device = new SsdpRootDevice + { + CacheLifetime = TimeSpan.FromSeconds(cacheLength), //How long SSDP clients can cache this info. + Location = uri, // Must point to the URL that serves your devices UPnP description document. + FriendlyName = "Emby Server", + Manufacturer = "Emby", + ModelName = "Emby Server", + Uuid = udn + // This must be a globally unique value that survives reboots etc. Get from storage or embedded hardware etc. + }; + + SetProperies(device, fullService); + _Publisher.AddDevice(device); + + var embeddedDevices = new List { - "urn:schemas-upnp-org:device:MediaServer:1", "urn:schemas-upnp-org:service:ContentDirectory:1", "urn:schemas-upnp-org:service:ConnectionManager:1", "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1" }; - foreach (var fullService in services) + foreach (var subDevice in embeddedDevices) { - _logger.Info("Registering publisher for {0} on {1}", fullService, addressString); - - var descriptorURI = "/dlna/" + udn + "/description.xml"; - var uri = new Uri(_appHost.GetLocalApiUrl(address) + descriptorURI); - - var service = fullService.Replace("urn:", string.Empty).Replace(":1", string.Empty); - - var serviceParts = service.Split(':'); - - var deviceTypeNamespace = serviceParts[0].Replace('.', '-'); - - var device = new SsdpRootDevice + var embeddedDevice = new SsdpEmbeddedDevice { - CacheLifetime = TimeSpan.FromSeconds(cacheLength), //How long SSDP clients can cache this info. - Location = uri, // Must point to the URL that serves your devices UPnP description document. - DeviceTypeNamespace = deviceTypeNamespace, - DeviceClass = serviceParts[1], - DeviceType = serviceParts[2], - FriendlyName = "Emby Server", - Manufacturer = "Emby", - ModelName = "Emby Server", + FriendlyName = device.FriendlyName, + Manufacturer = device.Manufacturer, + ModelName = device.ModelName, Uuid = udn // This must be a globally unique value that survives reboots etc. Get from storage or embedded hardware etc. }; - _Publisher.AddDevice(device); + SetProperies(embeddedDevice, subDevice); + device.AddDevice(embeddedDevice); } } } + private string CreateUuid(string text) + { + return text.GetMD5().ToString("N"); + } + + private void SetProperies(SsdpDevice device, string fullDeviceType) + { + var service = fullDeviceType.Replace("urn:", string.Empty).Replace(":1", string.Empty); + + var serviceParts = service.Split(':'); + + var deviceTypeNamespace = serviceParts[0].Replace('.', '-'); + + device.DeviceTypeNamespace = deviceTypeNamespace; + device.DeviceClass = serviceParts[1]; + device.DeviceType = serviceParts[2]; + } + private readonly object _syncLock = new object(); private void StartPlayToManager() { diff --git a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj index ef60be227f..ebffe6c575 100644 --- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj +++ b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj @@ -108,7 +108,6 @@ - @@ -130,7 +129,6 @@ - @@ -160,8 +158,6 @@ - - diff --git a/MediaBrowser.Dlna/Ssdp/Datagram.cs b/MediaBrowser.Dlna/Ssdp/Datagram.cs deleted file mode 100644 index 5901945344..0000000000 --- a/MediaBrowser.Dlna/Ssdp/Datagram.cs +++ /dev/null @@ -1,126 +0,0 @@ -using MediaBrowser.Model.Logging; -using System; -using System.Net; -using System.Net.Sockets; -using System.Text; - -namespace MediaBrowser.Dlna.Ssdp -{ - public class Datagram - { - public EndPoint ToEndPoint { get; private set; } - public EndPoint FromEndPoint { get; private set; } - public string Message { get; private set; } - public bool IsBroadcast { get; private set; } - public bool EnableDebugLogging { get; private set; } - - private readonly ILogger _logger; - - public Datagram(EndPoint toEndPoint, EndPoint fromEndPoint, ILogger logger, string message, bool isBroadcast, bool enableDebugLogging) - { - Message = message; - _logger = logger; - EnableDebugLogging = enableDebugLogging; - IsBroadcast = isBroadcast; - FromEndPoint = fromEndPoint; - ToEndPoint = toEndPoint; - } - - public void Send() - { - var msg = Encoding.ASCII.GetBytes(Message); - - var socket = CreateSocket(); - - if (socket == null) - { - return; - } - - if (FromEndPoint != null) - { - try - { - socket.Bind(FromEndPoint); - } - catch (Exception ex) - { - if (EnableDebugLogging) - { - _logger.ErrorException("Error binding datagram socket", ex); - } - - if (IsBroadcast) - { - CloseSocket(socket, false); - return; - } - } - } - - try - { - socket.BeginSendTo(msg, 0, msg.Length, SocketFlags.None, ToEndPoint, result => - { - try - { - socket.EndSend(result); - } - catch (Exception ex) - { - if (EnableDebugLogging) - { - _logger.ErrorException("Error sending Datagram to {0} from {1}: " + Message, ex, ToEndPoint, FromEndPoint == null ? "" : FromEndPoint.ToString()); - } - } - finally - { - CloseSocket(socket, true); - } - }, null); - } - catch (Exception ex) - { - _logger.ErrorException("Error sending Datagram to {0} from {1}: " + Message, ex, ToEndPoint, FromEndPoint == null ? "" : FromEndPoint.ToString()); - CloseSocket(socket, false); - } - } - - private void CloseSocket(Socket socket, bool logError) - { - try - { - socket.Close(); - } - catch (Exception ex) - { - if (logError && EnableDebugLogging) - { - _logger.ErrorException("Error closing datagram socket", ex); - } - } - } - - private Socket CreateSocket() - { - try - { - var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); - socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4); - - if (IsBroadcast) - { - socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true); - } - - return socket; - } - catch (Exception ex) - { - _logger.ErrorException("Error creating socket", ex); - return null; - } - } - } -} diff --git a/MediaBrowser.Dlna/Ssdp/DeviceDiscoveryInfo.cs b/MediaBrowser.Dlna/Ssdp/DeviceDiscoveryInfo.cs deleted file mode 100644 index e9de45522b..0000000000 --- a/MediaBrowser.Dlna/Ssdp/DeviceDiscoveryInfo.cs +++ /dev/null @@ -1,21 +0,0 @@ -using MediaBrowser.Dlna.PlayTo; -using System; -using System.Net; - -namespace MediaBrowser.Dlna.Ssdp -{ - public class DeviceDiscoveryInfo - { - public Device Device { get; set; } - - /// - /// The server's ip address that the device responded to - /// - public IPAddress LocalIpAddress { get; set; } - - public Uri Uri { get; set; } - - public string Usn { get; set; } - public string Nt { get; set; } - } -} diff --git a/MediaBrowser.Dlna/Ssdp/Extensions.cs b/MediaBrowser.Dlna/Ssdp/Extensions.cs index 12589e80fe..17ebcc7ead 100644 --- a/MediaBrowser.Dlna/Ssdp/Extensions.cs +++ b/MediaBrowser.Dlna/Ssdp/Extensions.cs @@ -9,30 +9,6 @@ namespace MediaBrowser.Dlna.Ssdp { public static class Extensions { - public static Task ReceiveAsync(this Socket socket, byte[] buffer, int offset, int size) - { - var tcs = new TaskCompletionSource(socket); - var remoteip = new IPEndPoint(IPAddress.Any, 0); - var endpoint = (EndPoint)remoteip; - - socket.BeginReceiveFrom(buffer, offset, size, SocketFlags.None, ref endpoint, iar => - { - var result = (TaskCompletionSource)iar.AsyncState; - var iarSocket = (Socket)result.Task.AsyncState; - - try - { - result.TrySetResult(iarSocket.EndReceive(iar)); - } - catch (Exception exc) - { - result.TrySetException(exc); - } - }, tcs); - - return tcs.Task; - } - public static string GetValue(this XElement container, XName name) { var node = container.Element(name); diff --git a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs deleted file mode 100644 index 0d0ca98a28..0000000000 --- a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs +++ /dev/null @@ -1,328 +0,0 @@ -using MediaBrowser.Common; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Events; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Dlna; -using MediaBrowser.Dlna.Server; -using MediaBrowser.Model.Logging; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Win32; - -namespace MediaBrowser.Dlna.Ssdp -{ - public class SsdpHandler : IDisposable, ISsdpHandler - { - private Socket _multicastSocket; - - private readonly ILogger _logger; - private readonly IServerConfigurationManager _config; - - const string SSDPAddr = "239.255.255.250"; - const int SSDPPort = 1900; - private readonly string _serverSignature; - - private readonly IPAddress _ssdpIp = IPAddress.Parse(SSDPAddr); - private readonly IPEndPoint _ssdpEndp = new IPEndPoint(IPAddress.Parse(SSDPAddr), SSDPPort); - - private Timer _notificationTimer; - - private bool _isDisposed; - private readonly Dictionary> _devices = new Dictionary>(); - - private readonly IApplicationHost _appHost; - - private readonly int _unicastPort = 1901; - private UdpClient _unicastClient; - - public SsdpHandler(ILogger logger, IServerConfigurationManager config, IApplicationHost appHost) - { - _logger = logger; - _config = config; - _appHost = appHost; - - _config.NamedConfigurationUpdated += _config_ConfigurationUpdated; - _serverSignature = GenerateServerSignature(); - } - - private string GenerateServerSignature() - { - var os = Environment.OSVersion; - var pstring = os.Platform.ToString(); - switch (os.Platform) - { - case PlatformID.Win32NT: - case PlatformID.Win32S: - case PlatformID.Win32Windows: - pstring = "WIN"; - break; - } - - return String.Format( - "{0}{1}/{2}.{3} UPnP/1.0 DLNADOC/1.5 Emby/{4}", - pstring, - IntPtr.Size * 8, - os.Version.Major, - os.Version.Minor, - _appHost.ApplicationVersion - ); - } - - void _config_ConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e) - { - if (string.Equals(e.Key, "dlna", StringComparison.OrdinalIgnoreCase)) - { - ReloadAliveNotifier(); - } - } - - public IEnumerable RegisteredDevices - { - get - { - lock (_devices) - { - var devices = _devices.ToList(); - - return devices.SelectMany(i => i.Value).ToList(); - } - } - } - - public void Start() - { - DisposeSocket(); - StopAliveNotifier(); - - RestartSocketListener(); - ReloadAliveNotifier(); - - SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged; - SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; - } - - void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e) - { - if (e.Mode == PowerModes.Resume) - { - Start(); - } - } - - public async void SendDatagram(string msg, - EndPoint endpoint, - EndPoint localAddress, - bool isBroadcast, - int sendCount = 3) - { - var enableDebugLogging = _config.GetDlnaConfiguration().EnableDebugLog; - - for (var i = 0; i < sendCount; i++) - { - if (i > 0) - { - await Task.Delay(500).ConfigureAwait(false); - } - - var dgram = new Datagram(endpoint, localAddress, _logger, msg, isBroadcast, enableDebugLogging); - dgram.Send(); - } - } - - private void RestartSocketListener() - { - if (_isDisposed) - { - return; - } - - try - { - _multicastSocket = CreateMulticastSocket(); - - _logger.Info("MultiCast socket created"); - } - catch (Exception ex) - { - _logger.ErrorException("Error creating MultiCast socket", ex); - //StartSocketRetryTimer(); - } - } - - public void Dispose() - { - _config.NamedConfigurationUpdated -= _config_ConfigurationUpdated; - SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged; - - _isDisposed = true; - - DisposeSocket(); - StopAliveNotifier(); - } - - private void DisposeSocket() - { - if (_multicastSocket != null) - { - _multicastSocket.Close(); - _multicastSocket.Dispose(); - _multicastSocket = null; - } - } - - private Socket CreateMulticastSocket() - { - var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true); - socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); - socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4); - socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(_ssdpIp, 0)); - - socket.Bind(new IPEndPoint(IPAddress.Any, SSDPPort)); - - return socket; - } - - private void NotifyAll() - { - var enableDebugLogging = _config.GetDlnaConfiguration().EnableDebugLog; - - if (enableDebugLogging) - { - _logger.Debug("Sending alive notifications"); - } - foreach (var d in RegisteredDevices) - { - NotifyDevice(d, "alive", enableDebugLogging); - } - } - - private void NotifyDevice(UpnpDevice dev, string type, bool logMessage) - { - const string header = "NOTIFY * HTTP/1.1"; - - var values = new Dictionary(StringComparer.OrdinalIgnoreCase); - - // If needed later for non-server devices, these headers will need to be dynamic - values["HOST"] = "239.255.255.250:1900"; - values["CACHE-CONTROL"] = "max-age = 600"; - values["LOCATION"] = dev.Descriptor.ToString(); - values["SERVER"] = _serverSignature; - values["NTS"] = "ssdp:" + type; - values["NT"] = dev.Type; - values["USN"] = dev.USN; - - if (logMessage) - { - _logger.Debug("{0} said {1}", dev.USN, type); - } - - var msg = new SsdpMessageBuilder().BuildMessage(header, values); - - SendDatagram(msg, _ssdpEndp, new IPEndPoint(dev.Address, 0), true, 2); - //SendUnicastRequest(msg, 1); - } - - public void RegisterNotification(string uuid, Uri descriptionUri, IPAddress address, IEnumerable services) - { - lock (_devices) - { - List list; - List dl; - if (_devices.TryGetValue(uuid, out dl)) - { - list = dl; - } - else - { - list = new List(); - _devices[uuid] = list; - } - - list.AddRange(services.Select(i => new UpnpDevice(uuid, i, descriptionUri, address))); - - NotifyAll(); - _logger.Debug("Registered mount {0} at {1}", uuid, descriptionUri); - } - } - - public void UnregisterNotification(string uuid) - { - lock (_devices) - { - List dl; - if (_devices.TryGetValue(uuid, out dl)) - { - _devices.Remove(uuid); - foreach (var d in dl.ToList()) - { - NotifyDevice(d, "byebye", true); - } - - _logger.Debug("Unregistered mount {0}", uuid); - } - } - } - - private readonly object _notificationTimerSyncLock = new object(); - private int _aliveNotifierIntervalMs; - private void ReloadAliveNotifier() - { - var config = _config.GetDlnaConfiguration(); - - if (!config.BlastAliveMessages) - { - StopAliveNotifier(); - return; - } - - var intervalMs = config.BlastAliveMessageIntervalSeconds * 1000; - - if (_notificationTimer == null || _aliveNotifierIntervalMs != intervalMs) - { - lock (_notificationTimerSyncLock) - { - if (_notificationTimer == null) - { - _logger.Debug("Starting alive notifier"); - const int initialDelayMs = 3000; - _notificationTimer = new Timer(state => NotifyAll(), null, initialDelayMs, intervalMs); - } - else - { - _logger.Debug("Updating alive notifier"); - _notificationTimer.Change(intervalMs, intervalMs); - } - - _aliveNotifierIntervalMs = intervalMs; - } - } - } - - private void StopAliveNotifier() - { - lock (_notificationTimerSyncLock) - { - if (_notificationTimer != null) - { - _logger.Debug("Stopping alive notifier"); - _notificationTimer.Dispose(); - _notificationTimer = null; - } - } - } - - public class UdpState - { - public UdpClient UdpClient; - public IPEndPoint EndPoint; - } - } -} diff --git a/MediaBrowser.Dlna/Ssdp/SsdpMessageBuilder.cs b/MediaBrowser.Dlna/Ssdp/SsdpMessageBuilder.cs deleted file mode 100644 index e479ca19a5..0000000000 --- a/MediaBrowser.Dlna/Ssdp/SsdpMessageBuilder.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Collections.Generic; -using System.Text; - -namespace MediaBrowser.Dlna.Ssdp -{ - public class SsdpMessageBuilder - { - public string BuildMessage(string header, Dictionary values) - { - var builder = new StringBuilder(); - - const string argFormat = "{0}: {1}\r\n"; - - builder.AppendFormat("{0}\r\n", header); - - foreach (var pair in values) - { - builder.AppendFormat(argFormat, pair.Key, pair.Value); - } - - builder.Append("\r\n"); - - return builder.ToString(); - } - } -} diff --git a/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs b/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs index c9810b0423..3148405583 100644 --- a/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs +++ b/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs @@ -258,18 +258,14 @@ namespace MediaBrowser.LocalMetadata.Savers builder.Append("" + SecurityElement.Escape(item.DisplayMediaType) + ""); } - var hasCriticRating = item as IHasCriticRating; - if (hasCriticRating != null) + if (item.CriticRating.HasValue) { - if (hasCriticRating.CriticRating.HasValue) - { - builder.Append("" + SecurityElement.Escape(hasCriticRating.CriticRating.Value.ToString(UsCulture)) + ""); - } + builder.Append("" + SecurityElement.Escape(item.CriticRating.Value.ToString(UsCulture)) + ""); + } - if (!string.IsNullOrEmpty(hasCriticRating.CriticRatingSummary)) - { - builder.Append(""); - } + if (!string.IsNullOrEmpty(item.CriticRatingSummary)) + { + builder.Append(""); } if (!string.IsNullOrEmpty(item.Overview)) @@ -285,14 +281,10 @@ namespace MediaBrowser.LocalMetadata.Savers builder.Append("" + SecurityElement.Escape(hasOriginalTitle.OriginalTitle) + ""); } } - - var hasShortOverview = item as IHasShortOverview; - if (hasShortOverview != null) + + if (!string.IsNullOrEmpty(item.ShortOverview)) { - if (!string.IsNullOrEmpty(hasShortOverview.ShortOverview)) - { - builder.Append(""); - } + builder.Append(""); } if (!string.IsNullOrEmpty(item.CustomRating)) diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs index b66a3ed96d..f811a8d48a 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs @@ -588,13 +588,32 @@ namespace MediaBrowser.MediaEncoding.Encoder } if (string.Equals(hwType, "vaapi", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(options.VaapiDevice)) { - return GetAvailableEncoder(mediaEncoder, "h264_vaapi", defaultEncoder); + if (IsVaapiSupported(state)) + { + return GetAvailableEncoder(mediaEncoder, "h264_vaapi", defaultEncoder); + } } } return defaultEncoder; } + private static bool IsVaapiSupported(EncodingJob state) + { + var videoStream = state.VideoStream; + + if (videoStream != null) + { + // vaapi will throw an error with this input + // [vaapi @ 0x7faed8000960] No VAAPI support for codec mpeg4 profile -99. + if (string.Equals(videoStream.Codec, "mpeg4", StringComparison.OrdinalIgnoreCase) && videoStream.Level == -99) + { + return false; + } + } + return true; + } + internal static bool CanStreamCopyVideo(EncodingJobOptions request, MediaStream videoStream) { if (videoStream.IsInterlaced) diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs index 8149e3ff5e..0b047f9e8f 100644 --- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs +++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs @@ -31,6 +31,7 @@ namespace MediaBrowser.Model.Dto public bool RequiresOpening { get; set; } public string OpenToken { get; set; } public bool RequiresClosing { get; set; } + public bool SupportsProbing { get; set; } public string LiveStreamId { get; set; } public int? BufferMs { get; set; } @@ -63,6 +64,7 @@ namespace MediaBrowser.Model.Dto SupportsTranscoding = true; SupportsDirectStream = true; SupportsDirectPlay = true; + SupportsProbing = true; } public int? DefaultAudioStreamIndex { get; set; } diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs index ee7dd8b985..e19bddeee4 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs @@ -16,6 +16,7 @@ namespace MediaBrowser.Model.LiveTv public string RecordingEncodingFormat { get; set; } public bool EnableRecordingSubfolders { get; set; } public bool EnableOriginalAudioWithEncodedRecordings { get; set; } + public bool EnableOriginalVideoWithEncodedRecordings { get; set; } public List TunerHosts { get; set; } public List ListingProviders { get; set; } diff --git a/MediaBrowser.Model/LiveTv/ProgramQuery.cs b/MediaBrowser.Model/LiveTv/ProgramQuery.cs index ad57d14732..1fd9957600 100644 --- a/MediaBrowser.Model/LiveTv/ProgramQuery.cs +++ b/MediaBrowser.Model/LiveTv/ProgramQuery.cs @@ -42,6 +42,7 @@ namespace MediaBrowser.Model.LiveTv /// The user identifier. public string UserId { get; set; } public string SeriesTimerId { get; set; } + public string Name { get; set; } /// /// The earliest date for which a program starts to return diff --git a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs index 3affbbcc34..b6e2a96aa0 100644 --- a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs +++ b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs @@ -11,6 +11,7 @@ namespace MediaBrowser.Model.MediaInfo public long? StartTimeTicks { get; set; } public int? AudioStreamIndex { get; set; } public int? SubtitleStreamIndex { get; set; } + public int? MaxAudioChannels { get; set; } public string ItemId { get; set; } public DeviceProfile DeviceProfile { get; set; } @@ -24,6 +25,7 @@ namespace MediaBrowser.Model.MediaInfo MaxStreamingBitrate = options.MaxBitrate; ItemId = options.ItemId; DeviceProfile = options.Profile; + MaxAudioChannels = options.MaxAudioChannels; VideoOptions videoOptions = options as VideoOptions; if (videoOptions != null) diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs index 124739073a..a2b85d121e 100644 --- a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs +++ b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs @@ -16,6 +16,8 @@ namespace MediaBrowser.Model.MediaInfo public int? SubtitleStreamIndex { get; set; } + public int? MaxAudioChannels { get; set; } + public string MediaSourceId { get; set; } public string LiveStreamId { get; set; } diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs index 8bf0703be9..2c059c8600 100644 --- a/MediaBrowser.Model/Net/MimeTypes.cs +++ b/MediaBrowser.Model/Net/MimeTypes.cs @@ -100,6 +100,7 @@ namespace MediaBrowser.Model.Net .ToDictionary(x => x.Key, x => x.First().Key, StringComparer.OrdinalIgnoreCase); dict["image/jpg"] = ".jpg"; + dict["image/x-png"] = ".png"; return dict; } diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs index 5203adc9d1..767c034ee6 100644 --- a/MediaBrowser.Providers/Manager/ImageSaver.cs +++ b/MediaBrowser.Providers/Manager/ImageSaver.cs @@ -72,7 +72,7 @@ namespace MediaBrowser.Providers.Manager return SaveImage(item, source, mimeType, type, imageIndex, null, cancellationToken); } - public async Task SaveImage(IHasImages item, Stream source, string mimeType, ImageType type, int? imageIndex, string internalCacheKey, CancellationToken cancellationToken) + public async Task SaveImage(IHasImages item, Stream source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(mimeType)) { @@ -109,9 +109,9 @@ namespace MediaBrowser.Providers.Manager } } } - if (!string.IsNullOrEmpty(internalCacheKey)) + if (saveLocallyWithMedia.HasValue && !saveLocallyWithMedia.Value) { - saveLocally = false; + saveLocally = saveLocallyWithMedia.Value; } if (!imageIndex.HasValue && item.AllowsMultipleImages(type)) @@ -356,6 +356,11 @@ namespace MediaBrowser.Providers.Manager var season = item as Season; var extension = MimeTypes.ToExtension(mimeType); + if (string.IsNullOrWhiteSpace(extension)) + { + throw new ArgumentException(string.Format("Unable to determine image file extension from mime type {0}", mimeType)); + } + if (type == ImageType.Thumb && saveLocally) { if (season != null && season.IndexNumber.HasValue) diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index 87da835dcc..898fa522df 100644 --- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs +++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs @@ -156,14 +156,14 @@ namespace MediaBrowser.Providers.Manager var stream = _fileSystem.GetFileStream(response.Path, FileMode.Open, FileAccess.Read, FileShare.Read, true); - await _providerManager.SaveImage(item, stream, mimeType, imageType, null, response.InternalCacheKey, cancellationToken).ConfigureAwait(false); + await _providerManager.SaveImage(item, stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false); } } else { var mimeType = "image/" + response.Format.ToString().ToLower(); - await _providerManager.SaveImage(item, response.Stream, mimeType, imageType, null, response.InternalCacheKey, cancellationToken).ConfigureAwait(false); + await _providerManager.SaveImage(item, response.Stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false); } downloadedImages.Add(imageType); diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index c470f55f2b..41cacbe0a8 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -353,19 +353,16 @@ namespace MediaBrowser.Providers.Manager { var updateType = ItemUpdateType.None; - if (isFullRefresh || currentUpdateType > ItemUpdateType.None) + var folder = item as Folder; + if (folder != null && folder.SupportsDateLastMediaAdded) { - var folder = item as Folder; - if (folder != null && folder.SupportsDateLastMediaAdded) - { - var items = folder.GetRecursiveChildren(i => !i.IsFolder).Select(i => i.DateCreated).ToList(); - var date = items.Count == 0 ? (DateTime?)null : items.Max(); + var items = folder.GetRecursiveChildren(i => !i.IsFolder).Select(i => i.DateCreated).ToList(); + var date = items.Count == 0 ? (DateTime?)null : items.Max(); - if ((!folder.DateLastMediaAdded.HasValue && date.HasValue) || folder.DateLastMediaAdded != date) - { - folder.DateLastMediaAdded = date; - updateType = ItemUpdateType.MetadataEdit; - } + if ((!folder.DateLastMediaAdded.HasValue && date.HasValue) || folder.DateLastMediaAdded != date) + { + folder.DateLastMediaAdded = date; + updateType = ItemUpdateType.MetadataImport; } } diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index ae1d60eb9f..dfeceed7d4 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -140,12 +140,7 @@ namespace MediaBrowser.Providers.Manager return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger, _memoryStreamProvider).SaveImage(item, source, mimeType, type, imageIndex, cancellationToken); } - public Task SaveImage(IHasImages item, Stream source, string mimeType, ImageType type, int? imageIndex, string internalCacheKey, CancellationToken cancellationToken) - { - return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger, _memoryStreamProvider).SaveImage(item, source, mimeType, type, imageIndex, internalCacheKey, cancellationToken); - } - - public Task SaveImage(IHasImages item, string source, string mimeType, ImageType type, int? imageIndex, string internalCacheKey, CancellationToken cancellationToken) + public Task SaveImage(IHasImages item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(source)) { @@ -154,7 +149,7 @@ namespace MediaBrowser.Providers.Manager var fileStream = _fileSystem.GetFileStream(source, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true); - return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger, _memoryStreamProvider).SaveImage(item, fileStream, mimeType, type, imageIndex, internalCacheKey, cancellationToken); + return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger, _memoryStreamProvider).SaveImage(item, fileStream, mimeType, type, imageIndex, saveLocallyWithMedia, cancellationToken); } public async Task> GetAvailableRemoteImages(IHasImages item, RemoteImageQuery query, CancellationToken cancellationToken) diff --git a/MediaBrowser.Providers/Manager/ProviderUtils.cs b/MediaBrowser.Providers/Manager/ProviderUtils.cs index fabe08f334..178e861a30 100644 --- a/MediaBrowser.Providers/Manager/ProviderUtils.cs +++ b/MediaBrowser.Providers/Manager/ProviderUtils.cs @@ -236,15 +236,9 @@ namespace MediaBrowser.Providers.Manager private static void MergeShortOverview(BaseItem source, BaseItem target, List lockedFields, bool replaceData) { - var sourceHasShortOverview = source as IHasShortOverview; - var targetHasShortOverview = target as IHasShortOverview; - - if (sourceHasShortOverview != null && targetHasShortOverview != null) + if (replaceData || string.IsNullOrEmpty(target.ShortOverview)) { - if (replaceData || string.IsNullOrEmpty(targetHasShortOverview.ShortOverview)) - { - targetHasShortOverview.ShortOverview = sourceHasShortOverview.ShortOverview; - } + target.ShortOverview = source.ShortOverview; } } @@ -311,20 +305,14 @@ namespace MediaBrowser.Providers.Manager private static void MergeCriticRating(BaseItem source, BaseItem target, List lockedFields, bool replaceData) { - var sourceCast = source as IHasCriticRating; - var targetCast = target as IHasCriticRating; - - if (sourceCast != null && targetCast != null) + if (replaceData || !target.CriticRating.HasValue) { - if (replaceData || !targetCast.CriticRating.HasValue) - { - targetCast.CriticRating = sourceCast.CriticRating; - } + target.CriticRating = source.CriticRating; + } - if (replaceData || string.IsNullOrEmpty(targetCast.CriticRatingSummary)) - { - targetCast.CriticRatingSummary = sourceCast.CriticRatingSummary; - } + if (replaceData || string.IsNullOrEmpty(target.CriticRatingSummary)) + { + target.CriticRatingSummary = source.CriticRatingSummary; } } diff --git a/MediaBrowser.Providers/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Omdb/OmdbProvider.cs index f3766f007b..8fb4d8fccf 100644 --- a/MediaBrowser.Providers/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbProvider.cs @@ -66,28 +66,24 @@ namespace MediaBrowser.Providers.Omdb item.ProductionYear = year; } - var hasCriticRating = item as IHasCriticRating; - if (hasCriticRating != null) - { - // Seeing some bogus RT data on omdb for series, so filter it out here - // RT doesn't even have tv series - int tomatoMeter; + // Seeing some bogus RT data on omdb for series, so filter it out here + // RT doesn't even have tv series + int tomatoMeter; - if (!string.IsNullOrEmpty(result.tomatoMeter) - && int.TryParse(result.tomatoMeter, NumberStyles.Integer, _usCulture, out tomatoMeter) - && tomatoMeter >= 0) - { - hasCriticRating.CriticRating = tomatoMeter; - } + if (!string.IsNullOrEmpty(result.tomatoMeter) + && int.TryParse(result.tomatoMeter, NumberStyles.Integer, _usCulture, out tomatoMeter) + && tomatoMeter >= 0) + { + item.CriticRating = tomatoMeter; + } - if (!string.IsNullOrEmpty(result.tomatoConsensus) - && !string.Equals(result.tomatoConsensus, "No consensus yet.", StringComparison.OrdinalIgnoreCase)) - { - hasCriticRating.CriticRatingSummary = WebUtility.HtmlDecode(result.tomatoConsensus); - } - } + if (!string.IsNullOrEmpty(result.tomatoConsensus) + && !string.Equals(result.tomatoConsensus, "No consensus yet.", StringComparison.OrdinalIgnoreCase)) + { + item.CriticRatingSummary = WebUtility.HtmlDecode(result.tomatoConsensus); + } - int voteCount; + int voteCount; if (!string.IsNullOrEmpty(result.imdbVotes) && int.TryParse(result.imdbVotes, NumberStyles.Number, _usCulture, out voteCount) @@ -167,25 +163,21 @@ namespace MediaBrowser.Providers.Omdb item.ProductionYear = year; } - var hasCriticRating = item as IHasCriticRating; - if (hasCriticRating != null) + // Seeing some bogus RT data on omdb for series, so filter it out here + // RT doesn't even have tv series + int tomatoMeter; + + if (!string.IsNullOrEmpty(result.tomatoMeter) + && int.TryParse(result.tomatoMeter, NumberStyles.Integer, _usCulture, out tomatoMeter) + && tomatoMeter >= 0) { - // Seeing some bogus RT data on omdb for series, so filter it out here - // RT doesn't even have tv series - int tomatoMeter; + item.CriticRating = tomatoMeter; + } - if (!string.IsNullOrEmpty(result.tomatoMeter) - && int.TryParse(result.tomatoMeter, NumberStyles.Integer, _usCulture, out tomatoMeter) - && tomatoMeter >= 0) - { - hasCriticRating.CriticRating = tomatoMeter; - } - - if (!string.IsNullOrEmpty(result.tomatoConsensus) - && !string.Equals(result.tomatoConsensus, "No consensus yet.", StringComparison.OrdinalIgnoreCase)) - { - hasCriticRating.CriticRatingSummary = WebUtility.HtmlDecode(result.tomatoConsensus); - } + if (!string.IsNullOrEmpty(result.tomatoConsensus) + && !string.Equals(result.tomatoConsensus, "No consensus yet.", StringComparison.OrdinalIgnoreCase)) + { + item.CriticRatingSummary = WebUtility.HtmlDecode(result.tomatoConsensus); } int voteCount; @@ -420,12 +412,8 @@ namespace MediaBrowser.Providers.Omdb hasAwards.AwardSummary = WebUtility.HtmlDecode(result.Awards); } - var hasShortOverview = item as IHasShortOverview; - if (hasShortOverview != null) - { - // Imdb plots are usually pretty short - hasShortOverview.ShortOverview = result.Plot; - } + // Imdb plots are usually pretty short + item.ShortOverview = result.Plot; //if (!string.IsNullOrWhiteSpace(result.Director)) //{ diff --git a/MediaBrowser.Server.Implementations/IO/FileRefresher.cs b/MediaBrowser.Server.Implementations/IO/FileRefresher.cs index 3df7a03d4c..c2c776c2bb 100644 --- a/MediaBrowser.Server.Implementations/IO/FileRefresher.cs +++ b/MediaBrowser.Server.Implementations/IO/FileRefresher.cs @@ -45,6 +45,11 @@ namespace MediaBrowser.Server.Implementations.IO private void AddAffectedPath(string path) { + if (string.IsNullOrWhiteSpace(path)) + { + throw new ArgumentNullException("path"); + } + if (!_affectedPaths.Contains(path, StringComparer.Ordinal)) { _affectedPaths.Add(path); @@ -53,6 +58,11 @@ namespace MediaBrowser.Server.Implementations.IO public void AddPath(string path) { + if (string.IsNullOrWhiteSpace(path)) + { + throw new ArgumentNullException("path"); + } + lock (_timerLock) { AddAffectedPath(path); diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index b2302cf867..64abcc0440 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -2838,9 +2838,13 @@ namespace MediaBrowser.Server.Implementations.Library private bool ValidateNetworkPath(string path) { - if (Environment.OSVersion.Platform == PlatformID.Win32NT || !path.StartsWith("\\\\", StringComparison.OrdinalIgnoreCase)) + if (Environment.OSVersion.Platform == PlatformID.Win32NT) { - return Directory.Exists(path); + // We can't validate protocol-based paths, so just allow them + if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) == -1) + { + return Directory.Exists(path); + } } // Without native support for unc, we cannot validate this when running under mono diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index 716d627a93..e7bfe56f2e 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs @@ -360,7 +360,6 @@ namespace MediaBrowser.Server.Implementations.Library public async Task OpenLiveStream(LiveStreamRequest request, bool enableAutoClose, CancellationToken cancellationToken) { - enableAutoClose = false; await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); try diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index c3d5f34412..5f41995647 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -207,14 +207,18 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies // Find movies with their own folders if (args.IsDirectory) { + var files = args.FileSystemChildren + .Where(i => !LibraryManager.IgnoreFile(i, args.Parent)) + .ToList(); + if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase)) { - return null; + return FindMovie(args.Path, args.Parent, files, args.DirectoryService, collectionType, false); } if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase)) { - return null; + return FindMovie /// - /// The path. - /// The parent. - /// The file system entries. - /// The directory service. - /// Type of the collection. /// Movie. - private T FindMovie(string path, Folder parent, List fileSystemEntries, IDirectoryService directoryService, string collectionType) + private T FindMovie(string path, Folder parent, List fileSystemEntries, IDirectoryService directoryService, string collectionType, bool allowFilesAsFolders) where T : Video, new() { var multiDiscFolders = new List(); @@ -413,23 +405,27 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies } } - var supportsMultiVersion = !string.Equals(collectionType, CollectionType.HomeVideos) && - !string.Equals(collectionType, CollectionType.Photos) && - !string.Equals(collectionType, CollectionType.MusicVideos); - - var result = ResolveVideos(parent, fileSystemEntries, directoryService, supportsMultiVersion); - - if (result.Items.Count == 1) + if (allowFilesAsFolders) { - var movie = (T)result.Items[0]; - movie.IsInMixedFolder = false; - movie.Name = Path.GetFileName(movie.ContainingFolderPath); - return movie; - } + // TODO: Allow GetMultiDiscMovie in here + var supportsMultiVersion = !string.Equals(collectionType, CollectionType.HomeVideos) && + !string.Equals(collectionType, CollectionType.Photos) && + !string.Equals(collectionType, CollectionType.MusicVideos); - if (result.Items.Count == 0 && multiDiscFolders.Count > 0) - { - return GetMultiDiscMovie(multiDiscFolders, directoryService); + var result = ResolveVideos(parent, fileSystemEntries, directoryService, supportsMultiVersion); + + if (result.Items.Count == 1) + { + var movie = (T)result.Items[0]; + movie.IsInMixedFolder = false; + movie.Name = Path.GetFileName(movie.ContainingFolderPath); + return movie; + } + + if (result.Items.Count == 0 && multiDiscFolders.Count > 0) + { + return GetMultiDiscMovie(multiDiscFolders, directoryService); + } } return null; diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 9b69b84d36..214bb87169 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -1021,7 +1021,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { var stream = new MediaSourceInfo { - Path = _appHost.GetLocalApiUrl("localhost") + "/LiveTv/LiveRecordings/" + recordingId + "/stream", + Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveRecordings/" + recordingId + "/stream", Id = recordingId, SupportsDirectPlay = false, SupportsDirectStream = true, @@ -1854,23 +1854,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV ParentIndexNumber = program.SeasonNumber.Value, IndexNumber = program.EpisodeNumber.Value, AncestorIds = seriesIds, - ExcludeLocationTypes = new[] { LocationType.Virtual } - }); - - if (result.TotalRecordCount > 0) - { - return true; - } - } - - if (!string.IsNullOrWhiteSpace(program.EpisodeTitle)) - { - var result = _libraryManager.GetItemsResult(new InternalItemsQuery - { - IncludeItemTypes = new[] { typeof(Episode).Name }, - Name = program.EpisodeTitle, - AncestorIds = seriesIds, - ExcludeLocationTypes = new[] { LocationType.Virtual } + IsVirtualItem = false }); if (result.TotalRecordCount > 0) diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 3e9d186e3f..cdf8e75974 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -52,7 +52,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { var format = _liveTvOptions.RecordingEncodingFormat; - if (string.Equals(format, "mkv", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(format, "mkv", StringComparison.OrdinalIgnoreCase) || _liveTvOptions.EnableOriginalVideoWithEncodedRecordings) { return "mkv"; } @@ -204,6 +204,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV private bool EncodeVideo(MediaSourceInfo mediaSource) { + if (_liveTvOptions.EnableOriginalAudioWithEncodedRecordings) + { + return false; + } + var mediaStreams = mediaSource.MediaStreams ?? new List(); return !mediaStreams.Any(i => i.Type == MediaStreamType.Video && string.Equals(i.Codec, "h264", StringComparison.OrdinalIgnoreCase) && !i.IsInterlaced); } diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs index 8c46b45972..c7a2d295d9 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs @@ -72,7 +72,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv { dto.ProgramInfo = _dtoService.GetBaseItemDto(program, new DtoOptions()); - dto.ProgramInfo.TimerId = dto.Id; + if (info.Status != RecordingStatus.Cancelled && info.Status != RecordingStatus.Error) + { + dto.ProgramInfo.TimerId = dto.Id; + dto.ProgramInfo.Status = info.Status.ToString(); + } dto.ProgramInfo.SeriesTimerId = dto.SeriesTimerId; } diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index aa404b37ca..902afb2003 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -877,6 +877,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv SortOrder = query.SortOrder ?? SortOrder.Ascending, EnableTotalRecordCount = query.EnableTotalRecordCount, TopParentIds = new[] { topFolder.Id.ToString("N") }, + Name = query.Name, DtoOptions = options }; @@ -1946,7 +1947,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv } else { - timers = timers.Where(i => !(i.Item1.Status == RecordingStatus.New)); + timers = timers.Where(i => i.Item1.Status != RecordingStatus.New); } } @@ -2304,7 +2305,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv var info = await service.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false); - info.RecordAnyChannel = true; info.RecordAnyTime = true; info.Days = new List { diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs index a62796036d..393708fb7d 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs @@ -140,7 +140,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv try { - if (stream.MediaStreams.Any(i => i.Index != -1)) + if (!stream.SupportsProbing || stream.MediaStreams.Any(i => i.Index != -1)) { await AddMediaInfo(stream, isAudio, cancellationToken).ConfigureAwait(false); } diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs index 60222415c0..91f0ee832f 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs @@ -60,7 +60,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun //OpenedMediaSource.Path = tempFile; //OpenedMediaSource.ReadAtNativeFramerate = true; - OpenedMediaSource.Path = _appHost.GetLocalApiUrl("localhost") + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts"; + OpenedMediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts"; OpenedMediaSource.Protocol = MediaProtocol.Http; OpenedMediaSource.SupportsDirectPlay = false; OpenedMediaSource.SupportsDirectStream = true; diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index cedaf7ad86..48117f2251 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -13,6 +13,7 @@ using System.Threading; using System.Threading.Tasks; using CommonIO; using MediaBrowser.Common.Net; +using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Serialization; @@ -24,12 +25,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts { private readonly IFileSystem _fileSystem; private readonly IHttpClient _httpClient; + private readonly IServerApplicationHost _appHost; - public M3UTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient) + public M3UTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost) : base(config, logger, jsonSerializer, mediaEncoder) { _fileSystem = fileSystem; _httpClient = httpClient; + _appHost = appHost; } public override string Type @@ -46,7 +49,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts protected override async Task> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken) { - return await new M3uParser(Logger, _fileSystem, _httpClient).Parse(info.Url, ChannelIdPrefix, info.Id, cancellationToken).ConfigureAwait(false); + return await new M3uParser(Logger, _fileSystem, _httpClient, _appHost).Parse(info.Url, ChannelIdPrefix, info.Id, cancellationToken).ConfigureAwait(false); } public Task> GetTunerInfos(CancellationToken cancellationToken) @@ -75,7 +78,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts public async Task Validate(TunerHostInfo info) { - using (var stream = await new M3uParser(Logger, _fileSystem, _httpClient).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false)) + using (var stream = await new M3uParser(Logger, _fileSystem, _httpClient, _appHost).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false)) { } diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index 38eb9bdd11..454abddddb 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using CommonIO; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; +using MediaBrowser.Controller; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.Logging; @@ -18,12 +19,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts private readonly ILogger _logger; private readonly IFileSystem _fileSystem; private readonly IHttpClient _httpClient; + private readonly IServerApplicationHost _appHost; - public M3uParser(ILogger logger, IFileSystem fileSystem, IHttpClient httpClient) + public M3uParser(ILogger logger, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost) { _logger = logger; _fileSystem = fileSystem; _httpClient = httpClient; + _appHost = appHost; } public async Task> Parse(string url, string channelIdPrefix, string tunerHostId, CancellationToken cancellationToken) @@ -41,7 +44,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts { if (url.StartsWith("http", StringComparison.OrdinalIgnoreCase)) { - return _httpClient.Get(url, cancellationToken); + return _httpClient.Get(new HttpRequestOptions + { + Url = url, + CancellationToken = cancellationToken, + // Some data providers will require a user agent + UserAgent = _appHost.FriendlyName + "/" + _appHost.ApplicationVersion + }); } return Task.FromResult(_fileSystem.OpenRead(url)); } @@ -111,15 +120,31 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts channel.Number = "0"; } - channel.ImageUrl = FindProperty("tvg-logo", extInf, null); - channel.Number = FindProperty("channel-id", extInf, channel.Number); - channel.Number = FindProperty("tvg-id", extInf, channel.Number); - channel.Name = FindProperty("tvg-id", extInf, channel.Name); - channel.Name = FindProperty("tvg-name", extInf, channel.Name); + channel.ImageUrl = FindProperty("tvg-logo", extInf); + + var name = FindProperty("tvg-name", extInf); + if (string.IsNullOrWhiteSpace(name)) + { + name = FindProperty("tvg-id", extInf); + } + + channel.Name = name; + + var numberString = FindProperty("tvg-id", extInf); + if (string.IsNullOrWhiteSpace(numberString)) + { + numberString = FindProperty("channel-id", extInf); + } + + if (!string.IsNullOrWhiteSpace(numberString)) + { + channel.Number = numberString; + } + return channel; } - private string FindProperty(string property, string properties, string defaultResult = "") + private string FindProperty(string property, string properties) { var reg = new Regex(@"([a-z0-9\-_]+)=\""([^""]+)\""", RegexOptions.IgnoreCase); var matches = reg.Matches(properties); @@ -130,7 +155,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts return match.Groups[2].Value; } } - return defaultResult; + return null; } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs index 81deb29959..1fe767e521 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs @@ -8,6 +8,7 @@ using CommonIO; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; +using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; @@ -25,12 +26,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp { private readonly IFileSystem _fileSystem; private readonly IHttpClient _httpClient; + private readonly IServerApplicationHost _appHost; - public SatIpHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient) + public SatIpHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost) : base(config, logger, jsonSerializer, mediaEncoder) { _fileSystem = fileSystem; _httpClient = httpClient; + _appHost = appHost; } private const string ChannelIdPrefix = "sat_"; @@ -39,7 +42,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp { if (!string.IsNullOrWhiteSpace(tuner.M3UUrl)) { - return await new M3uParser(Logger, _fileSystem, _httpClient).Parse(tuner.M3UUrl, ChannelIdPrefix, tuner.Id, cancellationToken).ConfigureAwait(false); + return await new M3uParser(Logger, _fileSystem, _httpClient, _appHost).Parse(tuner.M3UUrl, ChannelIdPrefix, tuner.Id, cancellationToken).ConfigureAwait(false); } var channels = await new ChannelScan(Logger).Scan(tuner, cancellationToken).ConfigureAwait(false); diff --git a/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs b/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs index abf0f34255..22d7ba3bec 100644 --- a/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs +++ b/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs @@ -144,7 +144,7 @@ namespace MediaBrowser.Server.Implementations.Photos return ItemUpdateType.None; } - await ProviderManager.SaveImage(item, outputPath, "image/png", imageType, null, Guid.NewGuid().ToString("N"), cancellationToken).ConfigureAwait(false); + await ProviderManager.SaveImage(item, outputPath, "image/png", imageType, null, false, cancellationToken).ConfigureAwait(false); return ItemUpdateType.ImageUpdate; } diff --git a/MediaBrowser.Server.Implementations/Sorting/CriticRatingComparer.cs b/MediaBrowser.Server.Implementations/Sorting/CriticRatingComparer.cs index d01f7ed1b3..9484130cbc 100644 --- a/MediaBrowser.Server.Implementations/Sorting/CriticRatingComparer.cs +++ b/MediaBrowser.Server.Implementations/Sorting/CriticRatingComparer.cs @@ -22,9 +22,7 @@ namespace MediaBrowser.Server.Implementations.Sorting private float GetValue(BaseItem x) { - var hasCriticRating = x as IHasCriticRating; - - return hasCriticRating == null ? 0 : hasCriticRating.CriticRating ?? 0; + return x.CriticRating ?? 0; } /// diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs index d1972a938b..b2c7445e23 100644 --- a/MediaBrowser.ServerApplication/MainStartup.cs +++ b/MediaBrowser.ServerApplication/MainStartup.cs @@ -71,7 +71,7 @@ namespace MediaBrowser.ServerApplication if (_isRunningAsService) { - _canRestartService = CanRestartWindowsService(); + //_canRestartService = CanRestartWindowsService(); } var currentProcess = Process.GetCurrentProcess(); diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 36dd12651c..a561a48afc 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -986,9 +986,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest @@ -1034,9 +1031,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 8c45b8001d..59f6e87220 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -294,14 +294,12 @@ namespace MediaBrowser.XbmcMetadata.Parsers { var text = reader.ReadElementContentAsString(); - var hasCriticRating = item as IHasCriticRating; - - if (hasCriticRating != null && !string.IsNullOrEmpty(text)) + if (!string.IsNullOrEmpty(text)) { float value; if (float.TryParse(text, NumberStyles.Any, _usCulture, out value)) { - hasCriticRating.CriticRating = value; + item.CriticRating = value; } } @@ -388,12 +386,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers if (!string.IsNullOrWhiteSpace(val)) { - var hasShortOverview = item as IHasShortOverview; - - if (hasShortOverview != null) - { - hasShortOverview.ShortOverview = val; - } + item.ShortOverview = val; } break; } @@ -418,12 +411,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers if (!string.IsNullOrWhiteSpace(val)) { - var hasCriticRating = item as IHasCriticRating; - - if (hasCriticRating != null) - { - hasCriticRating.CriticRatingSummary = val; - } + item.CriticRatingSummary = val; } break; diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 31d946691b..aba4913f33 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.663 + 3.0.665 MediaBrowser.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption. Copyright © Emby 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 86524c2b66..cd79701275 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.663 + 3.0.665 MediaBrowser.Common Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 860a19436b..b0dd3ae4fe 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.663 + 3.0.665 Media Browser.Server.Core Emby Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Emby Server. Copyright © Emby 2013 - +