From 2bdaba633cab470b25470a5bb05a34c4dfe8aec1 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 29 Aug 2016 17:06:24 -0400 Subject: make chapter images a per-library setting --- MediaBrowser.Controller/Entities/CollectionFolder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MediaBrowser.Controller/Entities') diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 597ecf973..30ea26eb6 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -106,7 +106,7 @@ namespace MediaBrowser.Controller.Entities { LibraryOptions[path] = options; - options.SchemaVersion = 1; + options.SchemaVersion = 2; XmlSerializer.SerializeToFile(options, GetLibraryOptionsPath(path)); } } -- cgit v1.2.3 From f646c0f29d97fbd3f43fbbb58739d3378a40edb8 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 3 Sep 2016 14:18:59 -0400 Subject: update season cleanup --- MediaBrowser.Api/TvShowsService.cs | 2 +- MediaBrowser.Controller/Entities/TV/Season.cs | 26 +----------- MediaBrowser.Controller/Entities/TV/Series.cs | 50 +++++++++--------------- MediaBrowser.Providers/TV/DummySeasonProvider.cs | 18 +++------ 4 files changed, 27 insertions(+), 69 deletions(-) (limited to 'MediaBrowser.Controller/Entities') diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index daaa6343d..a0d69317c 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -478,7 +478,7 @@ namespace MediaBrowser.Api } else { - episodes = series.GetSeasonEpisodes(user, season); + episodes = series.GetSeasonEpisodes(season, user); } } else diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 65b7c9955..5b50ad54e 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -185,34 +185,12 @@ namespace MediaBrowser.Controller.Entities.TV public IEnumerable GetEpisodes(Series series, User user, IEnumerable allSeriesEpisodes) { - return series.GetSeasonEpisodes(user, this, allSeriesEpisodes); + return series.GetSeasonEpisodes(this, user, allSeriesEpisodes); } public IEnumerable GetEpisodes() { - var episodes = GetRecursiveChildren().OfType(); - var series = Series; - - if (series != null && series.ContainsEpisodesWithoutSeasonFolders) - { - var seasonNumber = IndexNumber; - var list = episodes.ToList(); - - if (seasonNumber.HasValue) - { - list.AddRange(series.GetRecursiveChildren().OfType() - .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value)); - } - else - { - list.AddRange(series.GetRecursiveChildren().OfType() - .Where(i => !i.ParentIndexNumber.HasValue)); - } - - episodes = list.DistinctBy(i => i.Id); - } - - return episodes; + return Series.GetSeasonEpisodes(this, null, null); } public override IEnumerable GetChildren(User user, bool includeLinkedChildren) diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 4915cfedc..253566293 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -373,24 +373,7 @@ namespace MediaBrowser.Controller.Entities.TV progress.Report(100); } - private IEnumerable GetAllEpisodes(User user) - { - Logger.Debug("Series.GetAllEpisodes entering GetItemList"); - - var result = LibraryManager.GetItemList(new InternalItemsQuery(user) - { - AncestorWithPresentationUniqueKey = GetUniqueSeriesKey(this), - IncludeItemTypes = new[] { typeof(Episode).Name }, - SortBy = new[] { ItemSortBy.SortName } - - }).Cast().ToList(); - - Logger.Debug("Series.GetAllEpisodes returning {0} episodes", result.Count); - - return result; - } - - public IEnumerable GetSeasonEpisodes(User user, Season parentSeason) + public IEnumerable GetSeasonEpisodes(Season parentSeason, User user) { var seriesKey = GetUniqueSeriesKey(this); Logger.Debug("GetSeasonEpisodes seriesKey: {0}", seriesKey); @@ -401,31 +384,34 @@ namespace MediaBrowser.Controller.Entities.TV IncludeItemTypes = new[] { typeof(Episode).Name }, SortBy = new[] { ItemSortBy.SortName } }; - var config = user.Configuration; - if (!config.DisplayMissingEpisodes && !config.DisplayUnairedEpisodes) - { - query.IsVirtualItem = false; - } - else if (!config.DisplayMissingEpisodes) - { - query.IsMissing = false; - } - else if (!config.DisplayUnairedEpisodes) + if (user != null) { - query.IsVirtualUnaired = false; + var config = user.Configuration; + if (!config.DisplayMissingEpisodes && !config.DisplayUnairedEpisodes) + { + query.IsVirtualItem = false; + } + else if (!config.DisplayMissingEpisodes) + { + query.IsMissing = false; + } + else if (!config.DisplayUnairedEpisodes) + { + query.IsVirtualUnaired = false; + } } var allItems = LibraryManager.GetItemList(query).OfType(); - return GetSeasonEpisodes(user, parentSeason, allItems); + return GetSeasonEpisodes(parentSeason, user, allItems); } - public IEnumerable GetSeasonEpisodes(User user, Season parentSeason, IEnumerable allSeriesEpisodes) + public IEnumerable GetSeasonEpisodes(Season parentSeason, User user, IEnumerable allSeriesEpisodes) { if (allSeriesEpisodes == null) { Logger.Debug("GetSeasonEpisodes allSeriesEpisodes is null"); - return GetSeasonEpisodes(user, parentSeason); + return GetSeasonEpisodes(parentSeason, user); } Logger.Debug("GetSeasonEpisodes FilterEpisodesBySeason"); diff --git a/MediaBrowser.Providers/TV/DummySeasonProvider.cs b/MediaBrowser.Providers/TV/DummySeasonProvider.cs index fe0ad78be..b74eac219 100644 --- a/MediaBrowser.Providers/TV/DummySeasonProvider.cs +++ b/MediaBrowser.Providers/TV/DummySeasonProvider.cs @@ -138,8 +138,6 @@ namespace MediaBrowser.Providers.TV .Where(i => i.LocationType == LocationType.Virtual) .ToList(); - var episodes = series.GetRecursiveChildren().OfType().ToList(); - var seasonsToRemove = virtualSeasons .Where(i => { @@ -152,19 +150,15 @@ namespace MediaBrowser.Providers.TV { return true; } + } - // If there are no episodes with this season number, delete it - if (episodes.All(e => !e.ParentIndexNumber.HasValue || e.ParentIndexNumber.Value != seasonNumber)) - { - return true; - } - - return false; + // If there are no episodes with this season number, delete it + if (!i.GetEpisodes().Any()) + { + return true; } - // Season does not have a number - // Remove if there are no episodes directly in series without a season number - return episodes.All(s => s.ParentIndexNumber.HasValue || s.IsInSeasonFolder); + return false; }) .ToList(); -- cgit v1.2.3 From aa56fab9b0f75af008baed4b93b08bc95701f040 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 3 Sep 2016 14:26:48 -0400 Subject: trim excess logging --- MediaBrowser.Controller/Entities/TV/Season.cs | 5 ----- MediaBrowser.Controller/Entities/TV/Series.cs | 7 ------- 2 files changed, 12 deletions(-) (limited to 'MediaBrowser.Controller/Entities') diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 5b50ad54e..ff3e53b69 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -85,9 +85,7 @@ namespace MediaBrowser.Controller.Entities.TV public override int GetChildCount(User user) { - Logger.Debug("Season {0} getting child cound", (Path ?? Name)); var result = GetChildren(user, true).Count(); - Logger.Debug("Season {0} child cound: ", result); return result; } @@ -158,13 +156,10 @@ namespace MediaBrowser.Controller.Entities.TV var id = Guid.NewGuid().ToString("N"); - Logger.Debug("Season.GetItemsInternal entering GetEpisodes. Request id: " + id); var items = GetEpisodes(user).Where(filter); - Logger.Debug("Season.GetItemsInternal entering PostFilterAndSort. Request id: " + id); var result = PostFilterAndSort(items, query, false, false); - Logger.Debug("Season.GetItemsInternal complete. Request id: " + id); return Task.FromResult(result); } diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 253566293..7e8ba0516 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -209,7 +209,6 @@ namespace MediaBrowser.Controller.Entities.TV var seriesKey = GetUniqueSeriesKey(this); - Logger.Debug("GetSeasons SeriesKey: {0}", seriesKey); var query = new InternalItemsQuery(user) { AncestorWithPresentationUniqueKey = seriesKey, @@ -267,7 +266,6 @@ namespace MediaBrowser.Controller.Entities.TV public IEnumerable GetEpisodes(User user) { var seriesKey = GetUniqueSeriesKey(this); - Logger.Debug("GetEpisodes seriesKey: {0}", seriesKey); var query = new InternalItemsQuery(user) { @@ -291,8 +289,6 @@ namespace MediaBrowser.Controller.Entities.TV var allItems = LibraryManager.GetItemList(query).ToList(); - Logger.Debug("GetEpisodes return {0} items from database", allItems.Count); - var allSeriesEpisodes = allItems.OfType().ToList(); var allEpisodes = allItems.OfType() @@ -376,7 +372,6 @@ namespace MediaBrowser.Controller.Entities.TV public IEnumerable GetSeasonEpisodes(Season parentSeason, User user) { var seriesKey = GetUniqueSeriesKey(this); - Logger.Debug("GetSeasonEpisodes seriesKey: {0}", seriesKey); var query = new InternalItemsQuery(user) { @@ -410,11 +405,9 @@ namespace MediaBrowser.Controller.Entities.TV { if (allSeriesEpisodes == null) { - Logger.Debug("GetSeasonEpisodes allSeriesEpisodes is null"); return GetSeasonEpisodes(parentSeason, user); } - Logger.Debug("GetSeasonEpisodes FilterEpisodesBySeason"); var episodes = FilterEpisodesBySeason(allSeriesEpisodes, parentSeason, ConfigurationManager.Configuration.DisplaySpecialsWithinSeasons); var sortBy = (parentSeason.IndexNumber ?? -1) == 0 ? ItemSortBy.SortName : ItemSortBy.AiredEpisodeOrder; -- cgit v1.2.3 From daeedb98ea1ebc59c768ef16ccfdc19b752eea4d Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 3 Sep 2016 15:10:52 -0400 Subject: update now playing tabs --- MediaBrowser.Controller/Entities/Trailer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'MediaBrowser.Controller/Entities') diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs index 306ce35ec..7a987a68e 100644 --- a/MediaBrowser.Controller/Entities/Trailer.cs +++ b/MediaBrowser.Controller/Entities/Trailer.cs @@ -64,7 +64,7 @@ namespace MediaBrowser.Controller.Entities info.IsLocalTrailer = TrailerTypes.Contains(TrailerType.LocalTrailer); - if (!IsInMixedFolder) + if (!IsInMixedFolder && LocationType == LocationType.FileSystem) { info.Name = System.IO.Path.GetFileName(ContainingFolderPath); } -- cgit v1.2.3 From 9c7eef891b808c60181704046a48dd9cdbe96bae Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 4 Sep 2016 11:01:31 -0400 Subject: add configurable encoding params --- MediaBrowser.Api/Playback/BaseStreamingService.cs | 20 ++++++++++++++++++-- MediaBrowser.Controller/Entities/UserView.cs | 3 ++- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 7 ++++++- MediaBrowser.Model/Configuration/EncodingOptions.cs | 4 ++++ .../Configuration/ServerConfiguration.cs | 2 ++ .../UserViews/DynamicImageProvider.cs | 16 ++++++++++++++-- 6 files changed, 46 insertions(+), 6 deletions(-) (limited to 'MediaBrowser.Controller/Entities') diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 4af564a5a..89ad02170 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -346,11 +346,27 @@ namespace MediaBrowser.Api.Playback var isVc1 = state.VideoStream != null && string.Equals(state.VideoStream.Codec, "vc1", StringComparison.OrdinalIgnoreCase); + var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions(); + if (string.Equals(videoCodec, "libx264", StringComparison.OrdinalIgnoreCase)) { - param = "-preset superfast"; + if (!string.IsNullOrWhiteSpace(encodingOptions.H264Preset)) + { + param = "-preset " + encodingOptions.H264Preset; + } + else + { + param = "-preset superfast"; + } - param += " -crf 23"; + if (encodingOptions.H264Crf >= 0 && encodingOptions.H264Crf <= 51) + { + param = " -crf " + encodingOptions.H264Crf.ToString(CultureInfo.InvariantCulture); + } + else + { + param += " -crf 23"; + } } else if (string.Equals(videoCodec, "libx265", StringComparison.OrdinalIgnoreCase)) diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index 35375e7e6..194ba0ee4 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -113,7 +113,8 @@ namespace MediaBrowser.Controller.Entities { var standaloneTypes = new List { - CollectionType.Playlists + CollectionType.Playlists, + CollectionType.BoxSets }; var collectionFolder = folder as ICollectionFolder; diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index ad84ffee8..75d85cd32 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -928,7 +928,12 @@ namespace MediaBrowser.MediaEncoding.Encoder { StartProcess(processWrapper); - ranToCompletion = process.WaitForExit(10000); + var timeoutMs = ConfigurationManager.Configuration.ImageExtractionTimeoutMs; + if (timeoutMs <= 0) + { + timeoutMs = 10000; + } + ranToCompletion = process.WaitForExit(timeoutMs); if (!ranToCompletion) { diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index 3c03dc12a..d49816803 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -11,6 +11,8 @@ namespace MediaBrowser.Model.Configuration public string HardwareAccelerationType { get; set; } public string EncoderAppPath { get; set; } public string VaapiDevice { get; set; } + public int H264Crf { get; set; } + public string H264Preset { get; set; } public EncodingOptions() { @@ -19,6 +21,8 @@ namespace MediaBrowser.Model.Configuration ThrottleDelaySeconds = 180; EncodingThreadCount = -1; VaapiDevice = "/dev/dri/card0"; + H264Crf = 23; + H264Preset = "superfast"; } } } diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index b0595b558..44a2ae954 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -207,6 +207,7 @@ namespace MediaBrowser.Model.Configuration public bool EnableChannelView { get; set; } public bool EnableExternalContentInSuggestions { get; set; } + public int ImageExtractionTimeoutMs { get; set; } /// /// Initializes a new instance of the class. /// @@ -216,6 +217,7 @@ namespace MediaBrowser.Model.Configuration Migrations = new string[] { }; CodecsUsed = new string[] { }; SqliteCacheSize = 0; + ImageExtractionTimeoutMs = 10000; EnableLocalizedGuids = true; DisplaySpecialsWithinSeasons = true; diff --git a/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs b/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs index 3c75c8a48..f40072897 100644 --- a/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs +++ b/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs @@ -14,17 +14,20 @@ using System.IO; using System.Linq; using System.Threading.Tasks; using CommonIO; +using MediaBrowser.Controller.LiveTv; namespace MediaBrowser.Server.Implementations.UserViews { public class DynamicImageProvider : BaseDynamicImageProvider { private readonly IUserManager _userManager; + private readonly ILibraryManager _libraryManager; - public DynamicImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, IUserManager userManager) + public DynamicImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, IUserManager userManager, ILibraryManager libraryManager) : base(fileSystem, providerManager, applicationPaths, imageProcessor) { _userManager = userManager; + _libraryManager = libraryManager; } public override IEnumerable GetSupportedImages(IHasImages item) @@ -50,7 +53,15 @@ namespace MediaBrowser.Server.Implementations.UserViews if (string.Equals(view.ViewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase)) { - return new List(); + var programs = _libraryManager.GetItemList(new InternalItemsQuery + { + IncludeItemTypes = new[] { typeof(LiveTvProgram).Name }, + ImageTypes = new[] { ImageType.Primary }, + Limit = 30, + IsMovie = true + }).ToList(); + + return GetFinalItems(programs).ToList(); } if (string.Equals(view.ViewType, SpecialFolder.MovieGenre, StringComparison.OrdinalIgnoreCase) || @@ -147,6 +158,7 @@ namespace MediaBrowser.Server.Implementations.UserViews CollectionType.MusicVideos, CollectionType.HomeVideos, CollectionType.BoxSets, + CollectionType.LiveTv, CollectionType.Playlists, CollectionType.Photos, string.Empty -- cgit v1.2.3 From d4324b7e893725c1fc42eb482d54184420b9a5d9 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 5 Sep 2016 16:07:36 -0400 Subject: add chapter image error handling --- MediaBrowser.Api/Playback/BaseStreamingService.cs | 3 +- .../Collections/ManualCollectionsFolder.cs | 36 +++++++++++++ MediaBrowser.Controller/Entities/UserView.cs | 3 +- .../MediaBrowser.Controller.csproj | 1 + .../Providers/BaseItemXmlParser.cs | 27 ++++++---- .../Folders/CollectionFolderMetadataService.cs | 14 +++++ .../Collections/CollectionsDynamicFolder.cs | 1 + .../Collections/ManualCollectionsFolder.cs | 36 ------------- .../LiveTv/EmbyTV/EncodedRecorder.cs | 22 +++++++- .../MediaBrowser.Server.Implementations.csproj | 1 - .../MediaEncoder/EncodingManager.cs | 16 +++--- .../UserViews/CollectionFolderImageProvider.cs | 62 ++++++++++++++++++++++ .../MediaBrowser.WebDashboard.csproj | 6 --- 13 files changed, 160 insertions(+), 68 deletions(-) create mode 100644 MediaBrowser.Controller/Collections/ManualCollectionsFolder.cs delete mode 100644 MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs (limited to 'MediaBrowser.Controller/Entities') diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index b419250f7..a979848e2 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1791,8 +1791,7 @@ namespace MediaBrowser.Api.Playback if (!string.IsNullOrWhiteSpace(state.VideoRequest.VideoCodec)) { state.SupportedVideoCodecs = state.VideoRequest.VideoCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); - state.VideoRequest.VideoCodec = state.SupportedVideoCodecs.FirstOrDefault(i => MediaEncoder.CanEncodeToAudioCodec(i)) - ?? state.SupportedVideoCodecs.FirstOrDefault(); + state.VideoRequest.VideoCodec = state.SupportedVideoCodecs.FirstOrDefault(); } } diff --git a/MediaBrowser.Controller/Collections/ManualCollectionsFolder.cs b/MediaBrowser.Controller/Collections/ManualCollectionsFolder.cs new file mode 100644 index 000000000..d2d28e504 --- /dev/null +++ b/MediaBrowser.Controller/Collections/ManualCollectionsFolder.cs @@ -0,0 +1,36 @@ +using MediaBrowser.Controller.Entities; + +namespace MediaBrowser.Controller.Collections +{ + public class ManualCollectionsFolder : BasePluginFolder, IHiddenFromDisplay + { + public ManualCollectionsFolder() + { + Name = "Collections"; + DisplayMediaType = "CollectionFolder"; + } + + public override bool IsHidden + { + get + { + return true; + } + } + + public bool IsHiddenFromUser(User user) + { + return !ConfigurationManager.Configuration.DisplayCollectionsView; + } + + public override string CollectionType + { + get { return Model.Entities.CollectionType.BoxSets; } + } + + public override string GetClientTypeName() + { + return typeof(CollectionFolder).Name; + } + } +} \ No newline at end of file diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index 194ba0ee4..35375e7e6 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -113,8 +113,7 @@ namespace MediaBrowser.Controller.Entities { var standaloneTypes = new List { - CollectionType.Playlists, - CollectionType.BoxSets + CollectionType.Playlists }; var collectionFolder = folder as ICollectionFolder; diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index e7eaa1dc0..5e74a3999 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -98,6 +98,7 @@ + diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index 4484adb1d..fccbd9211 100644 --- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs @@ -790,20 +790,25 @@ namespace MediaBrowser.Controller.Providers } default: - if (_validProviderIds.ContainsKey(reader.Name)) - { - var id = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(id)) + { + string readerName = reader.Name; + string providerIdValue; + if (_validProviderIds.TryGetValue(readerName, out providerIdValue)) { - item.SetProviderId(_validProviderIds[reader.Name], id); + var id = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(id)) + { + item.SetProviderId(providerIdValue, id); + } + } + else + { + reader.Skip(); } - } - else - { - reader.Skip(); - } - break; + break; + + } } } diff --git a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs index cdaa38366..2f534c12e 100644 --- a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs +++ b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using CommonIO; +using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -21,4 +22,17 @@ namespace MediaBrowser.Providers.Folders { } } + + public class ManualCollectionsFolderMetadataService : MetadataService + { + protected override void MergeData(MetadataResult source, MetadataResult target, List lockedFields, bool replaceData, bool mergeMetadataSettings) + { + ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); + } + + public ManualCollectionsFolderMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager) + { + } + } + } diff --git a/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs b/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs index 6cd9e9620..50bb6c559 100644 --- a/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs +++ b/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs @@ -2,6 +2,7 @@ using MediaBrowser.Controller.Entities; using System.IO; using CommonIO; +using MediaBrowser.Controller.Collections; namespace MediaBrowser.Server.Implementations.Collections { diff --git a/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs b/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs deleted file mode 100644 index 3e33066ae..000000000 --- a/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs +++ /dev/null @@ -1,36 +0,0 @@ -using MediaBrowser.Controller.Entities; - -namespace MediaBrowser.Server.Implementations.Collections -{ - public class ManualCollectionsFolder : BasePluginFolder, IHiddenFromDisplay - { - public ManualCollectionsFolder() - { - Name = "Collections"; - DisplayMediaType = "CollectionFolder"; - } - - public override bool IsHidden - { - get - { - return true; - } - } - - public bool IsHiddenFromUser(User user) - { - return !ConfigurationManager.Configuration.DisplayCollectionsView; - } - - public override string CollectionType - { - get { return Model.Entities.CollectionType.BoxSets; } - } - - public override string GetClientTypeName() - { - return typeof(CollectionFolder).Name; - } - } -} \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index fc3a507d1..5e7e3a94f 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -53,11 +53,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV public async Task Record(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken) { + if (mediaSource.Path.IndexOf("m3u8", StringComparison.OrdinalIgnoreCase) != -1) + { + await RecordWithoutTempFile(mediaSource, targetFile, duration, onStarted, cancellationToken) + .ConfigureAwait(false); + + return; + } + var tempfile = Path.Combine(_appPaths.TranscodingTempPath, Guid.NewGuid().ToString("N") + ".ts"); try { - await RecordInternal(mediaSource, tempfile, targetFile, duration, onStarted, cancellationToken) + await RecordWithTempFile(mediaSource, tempfile, targetFile, duration, onStarted, cancellationToken) .ConfigureAwait(false); } finally @@ -73,7 +81,17 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } - public async Task RecordInternal(MediaSourceInfo mediaSource, string tempFile, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken) + private async Task RecordWithoutTempFile(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken) + { + var durationToken = new CancellationTokenSource(duration); + cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; + + await RecordFromFile(mediaSource, mediaSource.Path, targetFile, duration, onStarted, cancellationToken).ConfigureAwait(false); + + _logger.Info("Recording completed to file {0}", targetFile); + } + + private async Task RecordWithTempFile(MediaSourceInfo mediaSource, string tempFile, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken) { var httpRequestOptions = new HttpRequestOptions() { diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 9a92cf896..8850f3d35 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -124,7 +124,6 @@ - diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs index 11338df6d..7d0841fa6 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs +++ b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs @@ -149,16 +149,16 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder } } - // Add some time for the first chapter to make sure we don't end up with a black image - var time = chapter.StartPositionTicks == 0 ? TimeSpan.FromTicks(Math.Min(FirstChapterTicks, video.RunTimeTicks ?? 0)) : TimeSpan.FromTicks(chapter.StartPositionTicks); + try + { + // Add some time for the first chapter to make sure we don't end up with a black image + var time = chapter.StartPositionTicks == 0 ? TimeSpan.FromTicks(Math.Min(FirstChapterTicks, video.RunTimeTicks ?? 0)) : TimeSpan.FromTicks(chapter.StartPositionTicks); - var protocol = MediaProtocol.File; + var protocol = MediaProtocol.File; - var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, video.Path, protocol, null, video.PlayableStreamFileNames); + var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, video.Path, protocol, null, video.PlayableStreamFileNames); - try - { - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); + _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); var tempFile = await _encoder.ExtractVideoImage(inputPath, protocol, video.Video3DFormat, time, cancellationToken).ConfigureAwait(false); File.Copy(tempFile, path, true); @@ -178,7 +178,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder } catch (Exception ex) { - _logger.ErrorException("Error extracting chapter images for {0}", ex, string.Join(",", inputPath)); + _logger.ErrorException("Error extracting chapter images for {0}", ex, string.Join(",", video.Path)); success = false; break; } diff --git a/MediaBrowser.Server.Implementations/UserViews/CollectionFolderImageProvider.cs b/MediaBrowser.Server.Implementations/UserViews/CollectionFolderImageProvider.cs index 29716d33e..2cff4a14f 100644 --- a/MediaBrowser.Server.Implementations/UserViews/CollectionFolderImageProvider.cs +++ b/MediaBrowser.Server.Implementations/UserViews/CollectionFolderImageProvider.cs @@ -13,6 +13,10 @@ using System.IO; using System.Linq; using System.Threading.Tasks; using CommonIO; +using MediaBrowser.Controller.Collections; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Querying; namespace MediaBrowser.Server.Implementations.UserViews { @@ -109,4 +113,62 @@ namespace MediaBrowser.Server.Implementations.UserViews return await base.CreateImage(item, itemsWithImages, outputPath, imageType, imageIndex).ConfigureAwait(false); } } + + public class ManualCollectionFolderImageProvider : BaseDynamicImageProvider + { + private readonly ILibraryManager _libraryManager; + + public ManualCollectionFolderImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, ILibraryManager libraryManager) : base(fileSystem, providerManager, applicationPaths, imageProcessor) + { + _libraryManager = libraryManager; + } + + public override IEnumerable GetSupportedImages(IHasImages item) + { + return new List + { + ImageType.Primary + }; + } + + protected override async Task> GetItemsWithImages(IHasImages item) + { + var view = (ManualCollectionsFolder)item; + + var recursive = !new[] { CollectionType.Playlists, CollectionType.Channels }.Contains(view.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase); + + var items = _libraryManager.GetItemList(new InternalItemsQuery + { + Recursive = recursive, + IncludeItemTypes = new[] { typeof(BoxSet).Name }, + Limit = 20, + SortBy = new[] { ItemSortBy.Random } + }); + + return GetFinalItems(items.Where(i => i.HasImage(ImageType.Primary) || i.HasImage(ImageType.Thumb)).ToList(), 8); + } + + protected override bool Supports(IHasImages item) + { + return item is ManualCollectionsFolder; + } + + protected override async Task CreateImage(IHasImages item, List itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex) + { + var outputPath = Path.ChangeExtension(outputPathWithoutExtension, ".png"); + + if (imageType == ImageType.Primary) + { + if (itemsWithImages.Count == 0) + { + return null; + } + + return await CreateThumbCollage(item, itemsWithImages, outputPath, 960, 540).ConfigureAwait(false); + } + + return await base.CreateImage(item, itemsWithImages, outputPath, imageType, imageIndex).ConfigureAwait(false); + } + } + } diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index d1308a501..2b828b8fc 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -755,9 +755,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest @@ -944,9 +941,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest -- cgit v1.2.3 From d68a8268331ab2c3f75b1f3edd8b786e48b0ba57 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 7 Sep 2016 13:17:26 -0400 Subject: update dialogs --- MediaBrowser.Api/Playback/BaseStreamingService.cs | 10 +++++++--- MediaBrowser.Controller/Entities/Folder.cs | 12 +++++++++++- MediaBrowser.Dlna/PlayTo/Device.cs | 2 ++ .../Library/LibraryManager.cs | 2 +- MediaBrowser.Server.Startup.Common/ApplicationHost.cs | 8 +++++--- 5 files changed, 26 insertions(+), 8 deletions(-) (limited to 'MediaBrowser.Controller/Entities') diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index a979848e2..5d7f01ad3 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1176,17 +1176,21 @@ namespace MediaBrowser.Api.Playback await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false); } - if (state.IsInputVideo && transcodingJob.Type == TranscodingJobType.Progressive) + if (state.IsInputVideo && transcodingJob.Type == TranscodingJobType.Progressive && !transcodingJob.HasExited) { await Task.Delay(1000, cancellationTokenSource.Token).ConfigureAwait(false); - if (state.ReadInputAtNativeFramerate) + if (state.ReadInputAtNativeFramerate && !transcodingJob.HasExited) { await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false); } } - StartThrottler(state, transcodingJob); + if (!transcodingJob.HasExited) + { + StartThrottler(state, transcodingJob); + } + ReportUsage(state); return transcodingJob; diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index bf47ada0d..f1d8def4b 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -1057,10 +1057,20 @@ namespace MediaBrowser.Controller.Entities /// IList{BaseItem}. public IList GetRecursiveChildren() { - return GetRecursiveChildren(i => true); + return GetRecursiveChildren(true); + } + + public IList GetRecursiveChildren(bool includeLinkedChildren) + { + return GetRecursiveChildren(i => true, includeLinkedChildren); } public IList GetRecursiveChildren(Func filter) + { + return GetRecursiveChildren(filter, true); + } + + public IList GetRecursiveChildren(Func filter, bool includeLinkedChildren) { var result = new Dictionary(); diff --git a/MediaBrowser.Dlna/PlayTo/Device.cs b/MediaBrowser.Dlna/PlayTo/Device.cs index 174ca871a..d1802b3ad 100644 --- a/MediaBrowser.Dlna/PlayTo/Device.cs +++ b/MediaBrowser.Dlna/PlayTo/Device.cs @@ -483,7 +483,9 @@ namespace MediaBrowser.Dlna.PlayTo { if (OnDeviceUnavailable != null) { + _logger.Debug("Disposing device due to loss of connection"); OnDeviceUnavailable(); + return; } } if (_successiveStopCount >= maxSuccessiveStopReturns) diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 7758d690a..442e2ebe5 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -401,7 +401,7 @@ namespace MediaBrowser.Server.Implementations.Library var locationType = item.LocationType; var children = item.IsFolder - ? ((Folder)item).GetRecursiveChildren().ToList() + ? ((Folder)item).GetRecursiveChildren(false).ToList() : new List(); foreach (var metadataPath in GetMetadataPaths(item, children)) diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index 2417c5b11..07659fdfc 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -363,7 +363,10 @@ namespace MediaBrowser.Server.Startup.Common private void PerformPreInitMigrations() { - var migrations = new List(); + var migrations = new List + { + new UpdateLevelMigration(ServerConfigurationManager, this, HttpClient, JsonSerializer, _releaseAssetFilename) + }; foreach (var task in migrations) { @@ -383,8 +386,7 @@ namespace MediaBrowser.Server.Startup.Common var migrations = new List { new MovieDbEpisodeProviderMigration(ServerConfigurationManager), - new DbMigration(ServerConfigurationManager, TaskManager), - new UpdateLevelMigration(ServerConfigurationManager, this, HttpClient, JsonSerializer, _releaseAssetFilename) + new DbMigration(ServerConfigurationManager, TaskManager) }; foreach (var task in migrations) -- cgit v1.2.3 From 151d88f20db2d6fbb8ea901d84d25d26ccbb136c Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 12 Sep 2016 14:10:09 -0400 Subject: encapsulate path substitution --- MediaBrowser.Controller/Entities/BaseItem.cs | 5 +--- MediaBrowser.Controller/Library/ILibraryManager.cs | 2 ++ MediaBrowser.Dlna/Main/DlnaEntryPoint.cs | 16 +++++++++++-- .../Dto/DtoService.cs | 5 +--- .../Library/LibraryManager.cs | 10 ++++++++ MediaBrowser.ServerApplication/MainStartup.cs | 27 ++++++++++++++++++++-- MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs | 7 +----- 7 files changed, 54 insertions(+), 18 deletions(-) (limited to 'MediaBrowser.Controller/Entities') diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 55aaf04ff..2a49168ed 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -2111,10 +2111,7 @@ namespace MediaBrowser.Controller.Entities { if (locationType == LocationType.FileSystem || locationType == LocationType.Offline) { - foreach (var map in ConfigurationManager.Configuration.PathSubstitutions) - { - path = LibraryManager.SubstitutePath(path, map.From, map.To); - } + return LibraryManager.GetPathAfterNetworkSubstitution(path); } return path; diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 04268bcea..d5c2fcd20 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -506,6 +506,8 @@ namespace MediaBrowser.Controller.Library /// QueryResult<BaseItem>. QueryResult QueryItems(InternalItemsQuery query); + string GetPathAfterNetworkSubstitution(string path); + /// /// Substitutes the path. /// diff --git a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs index af03f325f..0ab41020e 100644 --- a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs +++ b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs @@ -343,7 +343,8 @@ namespace MediaBrowser.Dlna.Main if (_Publisher != null) { var devices = _Publisher.Devices.ToList(); - foreach (var device in devices) + + Parallel.ForEach(devices, device => { try { @@ -353,7 +354,18 @@ namespace MediaBrowser.Dlna.Main { _logger.ErrorException("Error sending bye bye", ex); } - } + }); + //foreach (var device in devices) + //{ + // try + // { + // _Publisher.RemoveDevice(device); + // } + // catch (Exception ex) + // { + // _logger.ErrorException("Error sending bye bye", ex); + // } + //} _Publisher.Dispose(); } diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 9284f4fc7..3236c38d5 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -1516,10 +1516,7 @@ namespace MediaBrowser.Server.Implementations.Dto if (locationType == LocationType.FileSystem || locationType == LocationType.Offline) { - foreach (var map in _config.Configuration.PathSubstitutions) - { - path = _libraryManager.SubstitutePath(path, map.From, map.To); - } + path = _libraryManager.GetPathAfterNetworkSubstitution(path); } return path; diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 442e2ebe5..bd408c9d3 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -2527,6 +2527,16 @@ namespace MediaBrowser.Server.Implementations.Library }).OrderBy(i => i.Path).ToList(); } + public string GetPathAfterNetworkSubstitution(string path) + { + foreach (var map in ConfigurationManager.Configuration.PathSubstitutions) + { + path = SubstitutePath(path, map.From, map.To); + } + + return path; + } + public string SubstitutePath(string path, string from, string to) { if (string.IsNullOrWhiteSpace(path)) diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs index cdacdc81f..5d4fba32d 100644 --- a/MediaBrowser.ServerApplication/MainStartup.cs +++ b/MediaBrowser.ServerApplication/MainStartup.cs @@ -15,6 +15,7 @@ using System.Linq; using System.Management; using System.Runtime.InteropServices; using System.ServiceProcess; +using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; @@ -37,9 +38,31 @@ namespace MediaBrowser.ServerApplication [DllImport("kernel32.dll", SetLastError = true)] static extern bool SetDllDirectory(string lpPathName); + public static bool TryGetLocalFromUncDirectory(string local, out string unc) + { + if ((local == null) || (local == "")) + { + unc = ""; + throw new ArgumentNullException("local"); + } + + ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Name FROM Win32_share WHERE path ='" + local.Replace("\\", "\\\\") + "'"); + ManagementObjectCollection coll = searcher.Get(); + if (coll.Count == 1) + { + foreach (ManagementObject share in searcher.Get()) + { + unc = share["Name"] as String; + unc = "\\\\" + SystemInformation.ComputerName + "\\" + unc; + return true; + } + } + unc = ""; + return false; + } /// - /// Defines the entry point of the application. - /// + /// Defines the entry point of the application. + /// public static void Main() { var options = new StartupOptions(); diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs index 290ea588e..dc208d495 100644 --- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs @@ -1040,12 +1040,7 @@ namespace MediaBrowser.XbmcMetadata.Savers private static string GetPathToSave(string path, ILibraryManager libraryManager, IServerConfigurationManager config) { - foreach (var map in config.Configuration.PathSubstitutions) - { - path = libraryManager.SubstitutePath(path, map.From, map.To); - } - - return path; + return libraryManager.GetPathAfterNetworkSubstitution(path); } private static bool IsPersonType(PersonInfo person, string type) -- cgit v1.2.3 From 6798a8f92c3ef35b496b8446add497d6d074a2d2 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 19 Sep 2016 11:41:35 -0400 Subject: always supply SeriesTimerId --- MediaBrowser.Api/Playback/Hls/BaseHlsService.cs | 3 +- MediaBrowser.Api/Playback/StreamState.cs | 12 ++++-- MediaBrowser.Controller/Entities/BaseItem.cs | 10 +++++ .../LiveTv/EmbyTV/EmbyTV.cs | 6 +-- .../LiveTv/LiveTvManager.cs | 44 +++++++++++++++++++--- 5 files changed, 62 insertions(+), 13 deletions(-) (limited to 'MediaBrowser.Controller/Entities') diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 1e056f670..761b1eb4e 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -150,9 +150,10 @@ namespace MediaBrowser.Api.Playback.Hls { var text = reader.ReadToEnd(); + text = text.Replace("#EXTM3U", "#EXTM3U\n#EXT-X-PLAYLIST-TYPE:EVENT"); + var newDuration = "#EXT-X-TARGETDURATION:" + segmentLength.ToString(UsCulture); - // ffmpeg pads the reported length by a full second text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength + 1).ToString(UsCulture), newDuration, StringComparison.OrdinalIgnoreCase); return text; diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index d6ccdd1fd..109aa85de 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -73,6 +73,10 @@ namespace MediaBrowser.Api.Playback { get { + if (!RunTimeTicks.HasValue) + { + return 6; + } if (string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { var userAgent = UserAgent ?? string.Empty; @@ -88,13 +92,13 @@ namespace MediaBrowser.Api.Playback return 10; } - if (!RunTimeTicks.HasValue) - { - return 10; - } return 6; } + if (!RunTimeTicks.HasValue) + { + return 6; + } return 3; } } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 2a49168ed..6f358dfb4 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -254,6 +254,16 @@ namespace MediaBrowser.Controller.Entities } } + [IgnoreDataMember] + public string ExternalSeriesId + { + get { return this.GetProviderId("ProviderExternalSeriesId"); } + set + { + this.SetProviderId("ProviderExternalSeriesId", value); + } + } + /// /// Gets or sets the etag. /// diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 3d7b4abef..b508110cf 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -763,7 +763,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV throw new ApplicationException("Tuner not found."); } - private async Task> GetChannelStreamInternal(string channelId, CancellationToken cancellationToken) + private async Task> GetChannelStreamInternal(string channelId, string streamId, CancellationToken cancellationToken) { _logger.Info("Streaming Channel " + channelId); @@ -771,7 +771,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { try { - var result = await hostInstance.GetChannelStream(channelId, null, cancellationToken).ConfigureAwait(false); + var result = await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false); return new Tuple(result.Item1, hostInstance, result.Item2); } @@ -994,7 +994,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV try { - var result = await GetChannelStreamInternal(timer.ChannelId, CancellationToken.None).ConfigureAwait(false); + var result = await GetChannelStreamInternal(timer.ChannelId, null, CancellationToken.None).ConfigureAwait(false); isResourceOpen = true; semaphore = result.Item3; var mediaStreamInfo = result.Item1; diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 65d023e88..df1ef3962 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -668,6 +668,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv item.EpisodeTitle = info.EpisodeTitle; item.ExternalId = info.Id; + item.ExternalSeriesId = info.SeriesId; item.Genres = info.Genres; item.IsHD = info.IsHD; item.IsKids = info.IsKids; @@ -903,8 +904,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv var dto = _dtoService.GetBaseItemDto(program, new DtoOptions(), user); - var list = new List>(); - list.Add(new Tuple(dto, program.ServiceName, program.ExternalId)); + var list = new List>(); + list.Add(new Tuple(dto, program.ServiceName, program.ExternalId, program.ExternalSeriesId)); await AddRecordingInfo(list, cancellationToken).ConfigureAwait(false); @@ -1092,15 +1093,17 @@ namespace MediaBrowser.Server.Implementations.LiveTv return score; } - private async Task AddRecordingInfo(IEnumerable> programs, CancellationToken cancellationToken) + private async Task AddRecordingInfo(IEnumerable> programs, CancellationToken cancellationToken) { var timers = new Dictionary>(); + var seriesTimers = new Dictionary>(); foreach (var programTuple in programs) { var program = programTuple.Item1; var serviceName = programTuple.Item2; var externalProgramId = programTuple.Item3; + string externalSeriesId = programTuple.Item4; if (string.IsNullOrWhiteSpace(serviceName)) { @@ -1123,6 +1126,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv } var timer = timerList.FirstOrDefault(i => string.Equals(i.ProgramId, externalProgramId, StringComparison.OrdinalIgnoreCase)); + var foundSeriesTimer = false; if (timer != null) { @@ -1133,8 +1137,38 @@ namespace MediaBrowser.Server.Implementations.LiveTv { program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(serviceName, timer.SeriesTimerId) .ToString("N"); + + foundSeriesTimer = true; } } + + if (foundSeriesTimer || string.IsNullOrWhiteSpace(externalSeriesId)) + { + continue; + } + + List seriesTimerList; + if (!seriesTimers.TryGetValue(serviceName, out seriesTimerList)) + { + try + { + var tempTimers = await GetService(serviceName).GetSeriesTimersAsync(cancellationToken).ConfigureAwait(false); + seriesTimers[serviceName] = seriesTimerList = tempTimers.ToList(); + } + catch (Exception ex) + { + _logger.ErrorException("Error getting series timer infos", ex); + seriesTimers[serviceName] = seriesTimerList = new List(); + } + } + + var seriesTimer = seriesTimerList.FirstOrDefault(i => string.Equals(i.SeriesId, externalSeriesId, StringComparison.OrdinalIgnoreCase)); + + if (seriesTimer != null) + { + program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(serviceName, seriesTimer.Id) + .ToString("N"); + } } } @@ -1659,7 +1693,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv public async Task AddInfoToProgramDto(List> tuples, List fields, User user = null) { - var recordingTuples = new List>(); + var recordingTuples = new List>(); foreach (var tuple in tuples) { @@ -1727,7 +1761,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv dto.ServiceName = serviceName; } - recordingTuples.Add(new Tuple(dto, serviceName, program.ExternalId)); + recordingTuples.Add(new Tuple(dto, serviceName, program.ExternalId, program.ExternalSeriesId)); } await AddRecordingInfo(recordingTuples, CancellationToken.None).ConfigureAwait(false); -- cgit v1.2.3 From d94598a75e303c0b4f76392e1e26b819d73e87c1 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 21 Sep 2016 13:07:18 -0400 Subject: update recording screens --- MediaBrowser.Api/Playback/BaseStreamingService.cs | 4 +++- MediaBrowser.Controller/Entities/IHasMetadata.cs | 2 ++ MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 21 ------------------- .../LiveTv/EmbyTV/EmbyTV.cs | 18 ++++++++++++++++ .../LiveTv/Listings/SchedulesDirect.cs | 24 ++++++++++++++-------- .../ApplicationHost.cs | 17 +++++++++++++++ 6 files changed, 56 insertions(+), 30 deletions(-) (limited to 'MediaBrowser.Controller/Entities') diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 9a3659f07..7ef9a1d5b 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -367,6 +367,8 @@ namespace MediaBrowser.Api.Playback { param += " -crf 23"; } + + param += " -tune zerolatency"; } else if (string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase)) @@ -1232,7 +1234,7 @@ namespace MediaBrowser.Api.Playback private void StartThrottler(StreamState state, TranscodingJob transcodingJob) { - if (EnableThrottling(state) && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (EnableThrottling(state)) { transcodingJob.TranscodingThrottler = state.TranscodingThrottler = new TranscodingThrottler(transcodingJob, Logger, ServerConfigurationManager); state.TranscodingThrottler.Start(); diff --git a/MediaBrowser.Controller/Entities/IHasMetadata.cs b/MediaBrowser.Controller/Entities/IHasMetadata.cs index cf2f7db64..ae73dbeed 100644 --- a/MediaBrowser.Controller/Entities/IHasMetadata.cs +++ b/MediaBrowser.Controller/Entities/IHasMetadata.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace MediaBrowser.Controller.Entities { @@ -62,5 +63,6 @@ namespace MediaBrowser.Controller.Entities int? GetInheritedParentalRatingValue(); int InheritedParentalRatingValue { get; set; } + List GetInheritedTags(); } } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index a2707aace..b2c2b71f8 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -108,11 +108,6 @@ namespace MediaBrowser.MediaEncoding.Encoder { get { - if (_hasExternalEncoder) - { - return "External"; - } - if (string.IsNullOrWhiteSpace(FFMpegPath)) { return null; @@ -177,12 +172,6 @@ namespace MediaBrowser.MediaEncoding.Encoder { ConfigureEncoderPaths(); - if (_hasExternalEncoder) - { - LogPaths(); - return; - } - // If the path was passed in, save it into config now. var encodingOptions = GetEncodingOptions(); var appPath = encodingOptions.EncoderAppPath; @@ -207,11 +196,6 @@ namespace MediaBrowser.MediaEncoding.Encoder public async Task UpdateEncoderPath(string path, string pathType) { - if (_hasExternalEncoder) - { - return; - } - Tuple newPaths; if (string.Equals(pathType, "system", StringComparison.OrdinalIgnoreCase)) @@ -256,11 +240,6 @@ namespace MediaBrowser.MediaEncoding.Encoder private void ConfigureEncoderPaths() { - if (_hasExternalEncoder) - { - return; - } - var appPath = GetEncodingOptions().EncoderAppPath; if (string.IsNullOrWhiteSpace(appPath)) diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 2dd23857a..d4b21102d 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -843,6 +843,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV if (recordingEndDate <= DateTime.UtcNow) { _logger.Warn("Recording timer fired for timer {0}, Id: {1}, but the program has already ended.", timer.Name, timer.Id); + _timerProvider.Delete(timer); return; } @@ -1273,6 +1274,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV writer.WriteElementString("rating", timer.CommunityRating.Value.ToString(CultureInfo.InvariantCulture)); } + if (timer.IsSports) + { + AddGenre(timer.Genres, "Sports"); + } + if (timer.IsKids) + { + AddGenre(timer.Genres, "Kids"); + } + foreach (var genre in timer.Genres) { writer.WriteElementString("genre", genre); @@ -1294,6 +1304,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } + private void AddGenre(List genres, string genre) + { + if (!genres.Contains(genre, StringComparer.OrdinalIgnoreCase)) + { + genres.Add(genre); + } + } + private ProgramInfo GetProgramInfoFromCache(string channelId, string programId) { var epgData = GetEpgDataForChannel(channelId); diff --git a/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 0bc84a2e5..c3907c045 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -339,13 +339,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings channelNumber = channelNumber.TrimStart('0'); _logger.Debug("Found channel: " + channelNumber + " in Schedules Direct"); - if (root.stations != null) + + var schChannel = (root.stations ?? new List()).FirstOrDefault(item => string.Equals(item.stationID, map.stationID, StringComparison.OrdinalIgnoreCase)); + if (schChannel != null) { - var schChannel = root.stations.FirstOrDefault(item => string.Equals(item.stationID, map.stationID, StringComparison.OrdinalIgnoreCase)); - if (schChannel != null) + AddToChannelPairCache(listingsId, channelNumber, schChannel); + } + else + { + AddToChannelPairCache(listingsId, channelNumber, new ScheduleDirect.Station { - AddToChannelPairCache(listingsId, channelNumber, schChannel); - } + stationID = map.stationID + }); } } _logger.Info("Added " + GetChannelPairCacheCount(listingsId) + " channels to the dictionary"); @@ -361,8 +366,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings channel.ImageUrl = station.logo.URL; channel.HasImage = true; } - string channelName = station.name; - channel.Name = channelName; + + if (!string.IsNullOrWhiteSpace(station.name)) + { + channel.Name = station.name; + } } else { @@ -993,7 +1001,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings var name = channelNumber; var station = GetStation(listingsId, channelNumber, null); - if (station != null) + if (station != null && !string.IsNullOrWhiteSpace(station.name)) { name = station.name; } diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index d8d3614e6..9312e1501 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -870,6 +870,23 @@ namespace MediaBrowser.Server.Startup.Common { CertificatePath = GetCertificatePath(true); + try + { + ServerManager.Start(GetUrlPrefixes(), CertificatePath); + return; + } + catch (Exception ex) + { + Logger.ErrorException("Error starting http server", ex); + + if (HttpPort == 8096) + { + throw; + } + } + + HttpPort = 8096; + try { ServerManager.Start(GetUrlPrefixes(), CertificatePath); -- cgit v1.2.3 From 6999017bc9823fac9f9ff0f0b6f5eab39b2519e2 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 21 Sep 2016 17:09:14 -0400 Subject: update recording dialogs --- MediaBrowser.Controller/Entities/BaseItem.cs | 4 ++++ MediaBrowser.Controller/Entities/IHasMetadata.cs | 1 + MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs | 5 +++++ MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs | 4 ++++ MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs | 4 +++- MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs | 4 ++++ 6 files changed, 21 insertions(+), 1 deletion(-) (limited to 'MediaBrowser.Controller/Entities') diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 6f358dfb4..6e0a33620 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -44,6 +44,7 @@ namespace MediaBrowser.Controller.Entities ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); LockedFields = new List(); ImageInfos = new List(); + InheritedTags = new List(); } public static readonly char[] SlugReplaceChars = { '?', '/', '&' }; @@ -794,6 +795,9 @@ namespace MediaBrowser.Controller.Entities [IgnoreDataMember] public int InheritedParentalRatingValue { get; set; } + [IgnoreDataMember] + public List InheritedTags { get; set; } + /// /// Gets or sets the critic rating. /// diff --git a/MediaBrowser.Controller/Entities/IHasMetadata.cs b/MediaBrowser.Controller/Entities/IHasMetadata.cs index ae73dbeed..aee58b445 100644 --- a/MediaBrowser.Controller/Entities/IHasMetadata.cs +++ b/MediaBrowser.Controller/Entities/IHasMetadata.cs @@ -64,5 +64,6 @@ namespace MediaBrowser.Controller.Entities int? GetInheritedParentalRatingValue(); int InheritedParentalRatingValue { get; set; } List GetInheritedTags(); + List InheritedTags { get; set; } } } diff --git a/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs b/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs index 2d79473f0..12308adda 100644 --- a/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs +++ b/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs @@ -53,6 +53,10 @@ namespace MediaBrowser.Controller.LiveTv /// true if [record any channel]; otherwise, false. public bool RecordAnyChannel { get; set; } + public int KeepUpTo { get; set; } + + public bool SkipEpisodesInLibrary { get; set; } + /// /// Gets or sets a value indicating whether [record new only]. /// @@ -104,6 +108,7 @@ namespace MediaBrowser.Controller.LiveTv public SeriesTimerInfo() { Days = new List(); + SkipEpisodesInLibrary = true; } } } diff --git a/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs b/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs index 4b88e42f3..9d4bab40c 100644 --- a/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs +++ b/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs @@ -18,12 +18,16 @@ namespace MediaBrowser.Model.LiveTv /// true if [record any time]; otherwise, false. public bool RecordAnyTime { get; set; } + public bool SkipEpisodesInLibrary { get; set; } + /// /// Gets or sets a value indicating whether [record any channel]. /// /// true if [record any channel]; otherwise, false. public bool RecordAnyChannel { get; set; } + public int KeepUpTo { get; set; } + /// /// Gets or sets a value indicating whether [record new only]. /// diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index d4b21102d..a3ca54b4a 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -541,6 +541,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV instance.RecordAnyChannel = info.RecordAnyChannel; instance.RecordAnyTime = info.RecordAnyTime; instance.RecordNewOnly = info.RecordNewOnly; + instance.SkipEpisodesInLibrary = info.SkipEpisodesInLibrary; + instance.KeepUpTo = info.KeepUpTo; instance.StartDate = info.StartDate; _seriesTimerProvider.Update(instance); @@ -1381,7 +1383,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV allPrograms = GetProgramsForSeries(seriesTimer, allPrograms); - if (filterByCurrentRecordings) + if (filterByCurrentRecordings && seriesTimer.SkipEpisodesInLibrary) { allPrograms = allPrograms.Where(i => !IsProgramAlreadyInLibrary(i)); } diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs index 683377c61..4681d0a7b 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs @@ -100,6 +100,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv Priority = info.Priority, RecordAnyChannel = info.RecordAnyChannel, RecordAnyTime = info.RecordAnyTime, + SkipEpisodesInLibrary = info.SkipEpisodesInLibrary, + KeepUpTo = info.KeepUpTo, RecordNewOnly = info.RecordNewOnly, ExternalChannelId = info.ChannelId, ExternalProgramId = info.ProgramId, @@ -308,6 +310,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv Priority = dto.Priority, RecordAnyChannel = dto.RecordAnyChannel, RecordAnyTime = dto.RecordAnyTime, + SkipEpisodesInLibrary = dto.SkipEpisodesInLibrary, + KeepUpTo = dto.KeepUpTo, RecordNewOnly = dto.RecordNewOnly, ProgramId = dto.ExternalProgramId, ChannelId = dto.ExternalChannelId, -- cgit v1.2.3 From 8b096ccc0e87883655bcf9ea1f9e22e9961450d3 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 23 Sep 2016 02:21:54 -0400 Subject: stub out storage for new path substitution --- .../Library/LibraryStructureService.cs | 20 ++- MediaBrowser.Controller/Entities/Audio/Audio.cs | 2 +- MediaBrowser.Controller/Entities/BaseItem.cs | 4 +- .../Entities/CollectionFolder.cs | 31 +++-- MediaBrowser.Controller/Entities/Video.cs | 2 +- MediaBrowser.Controller/Library/ILibraryManager.cs | 6 +- MediaBrowser.Model/Configuration/LibraryOptions.cs | 8 ++ .../Dto/DtoService.cs | 4 +- .../Library/CoreResolutionIgnoreRule.cs | 3 +- .../Library/LibraryManager.cs | 140 ++++++++++++++------- .../LiveTv/EmbyTV/EmbyTV.cs | 8 +- 11 files changed, 160 insertions(+), 68 deletions(-) (limited to 'MediaBrowser.Controller/Entities') diff --git a/MediaBrowser.Api/Library/LibraryStructureService.cs b/MediaBrowser.Api/Library/LibraryStructureService.cs index 72966a7cd..0ed4ee2a2 100644 --- a/MediaBrowser.Api/Library/LibraryStructureService.cs +++ b/MediaBrowser.Api/Library/LibraryStructureService.cs @@ -112,6 +112,8 @@ namespace MediaBrowser.Api.Library /// The name. public string Path { get; set; } + public MediaPathInfo PathInfo { get; set; } + /// /// Gets or sets a value indicating whether [refresh library]. /// @@ -212,7 +214,12 @@ namespace MediaBrowser.Api.Library { var libraryOptions = request.LibraryOptions ?? new LibraryOptions(); - _libraryManager.AddVirtualFolder(request.Name, request.CollectionType, request.Paths, libraryOptions, request.RefreshLibrary); + if (request.Paths != null && request.Paths.Length > 0) + { + libraryOptions.PathInfos = request.Paths.Select(i => new MediaPathInfo { Path = i }).ToArray(); + } + + _libraryManager.AddVirtualFolder(request.Name, request.CollectionType, libraryOptions, request.RefreshLibrary); } /// @@ -308,7 +315,16 @@ namespace MediaBrowser.Api.Library try { - _libraryManager.AddMediaPath(request.Name, request.Path); + var mediaPath = request.PathInfo; + + if (mediaPath == null) + { + mediaPath = new MediaPathInfo + { + Path = request.Path + }; + } + _libraryManager.AddMediaPath(request.Name, mediaPath); } finally { diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index 1af55a389..599e72314 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -262,7 +262,7 @@ namespace MediaBrowser.Controller.Entities.Audio Protocol = locationType == LocationType.Remote ? MediaProtocol.Http : MediaProtocol.File, MediaStreams = MediaSourceManager.GetMediaStreams(i.Id).ToList(), Name = i.Name, - Path = enablePathSubstituion ? GetMappedPath(i.Path, locationType) : i.Path, + Path = enablePathSubstituion ? GetMappedPath(i, i.Path, locationType) : i.Path, RunTimeTicks = i.RunTimeTicks, Container = i.Container, Size = i.Size diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 6e0a33620..30e0f3ee7 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -2121,11 +2121,11 @@ namespace MediaBrowser.Controller.Entities return hasChanges; } - protected static string GetMappedPath(string path, LocationType locationType) + protected static string GetMappedPath(BaseItem item, string path, LocationType locationType) { if (locationType == LocationType.FileSystem || locationType == LocationType.Offline) { - return LibraryManager.GetPathAfterNetworkSubstitution(path); + return LibraryManager.GetPathAfterNetworkSubstitution(path, item); } return path; diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 30ea26eb6..77d7ca7f2 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -48,24 +48,14 @@ namespace MediaBrowser.Controller.Entities private static readonly Dictionary LibraryOptions = new Dictionary(); public LibraryOptions GetLibraryOptions() { - lock (LibraryOptions) - { - LibraryOptions options; - if (!LibraryOptions.TryGetValue(Path, out options)) - { - options = LoadLibraryOptions(); - LibraryOptions[Path] = options; - } - - return options; - } + return GetLibraryOptions(Path); } - private LibraryOptions LoadLibraryOptions() + private static LibraryOptions LoadLibraryOptions(string path) { try { - var result = XmlSerializer.DeserializeFromFile(typeof(LibraryOptions), GetLibraryOptionsPath(Path)) as LibraryOptions; + var result = XmlSerializer.DeserializeFromFile(typeof(LibraryOptions), GetLibraryOptionsPath(path)) as LibraryOptions; if (result == null) { @@ -100,6 +90,21 @@ namespace MediaBrowser.Controller.Entities SaveLibraryOptions(Path, options); } + public static LibraryOptions GetLibraryOptions(string path) + { + lock (LibraryOptions) + { + LibraryOptions options; + if (!LibraryOptions.TryGetValue(path, out options)) + { + options = LoadLibraryOptions(path); + LibraryOptions[path] = options; + } + + return options; + } + } + public static void SaveLibraryOptions(string path, LibraryOptions options) { lock (LibraryOptions) diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index 8809f155c..1406a05ce 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -600,7 +600,7 @@ namespace MediaBrowser.Controller.Entities Protocol = locationType == LocationType.Remote ? MediaProtocol.Http : MediaProtocol.File, MediaStreams = mediaStreams, Name = GetMediaSourceName(i, mediaStreams), - Path = enablePathSubstitution ? GetMappedPath(i.Path, locationType) : i.Path, + Path = enablePathSubstitution ? GetMappedPath(i, i.Path, locationType) : i.Path, RunTimeTicks = i.RunTimeTicks, Video3DFormat = i.Video3DFormat, VideoType = i.VideoType, diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index d5c2fcd20..184a245c1 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -506,7 +506,7 @@ namespace MediaBrowser.Controller.Library /// QueryResult<BaseItem>. QueryResult QueryItems(InternalItemsQuery query); - string GetPathAfterNetworkSubstitution(string path); + string GetPathAfterNetworkSubstitution(string path, BaseItem ownerItem = null); /// /// Substitutes the path. @@ -556,9 +556,9 @@ namespace MediaBrowser.Controller.Library /// true if XXXX, false otherwise. bool IgnoreFile(FileSystemMetadata file, BaseItem parent); - void AddVirtualFolder(string name, string collectionType, string[] mediaPaths, LibraryOptions options, bool refreshLibrary); + void AddVirtualFolder(string name, string collectionType, LibraryOptions options, bool refreshLibrary); void RemoveVirtualFolder(string name, bool refreshLibrary); - void AddMediaPath(string virtualFolderName, string path); + void AddMediaPath(string virtualFolderName, MediaPathInfo path); void RemoveMediaPath(string virtualFolderName, string path); QueryResult> GetGenres(InternalItemsQuery query); diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs index 770ad433d..460ee0918 100644 --- a/MediaBrowser.Model/Configuration/LibraryOptions.cs +++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs @@ -9,11 +9,19 @@ public bool EnableChapterImageExtraction { get; set; } public bool ExtractChapterImagesDuringLibraryScan { get; set; } public bool DownloadImagesInAdvance { get; set; } + public MediaPathInfo[] PathInfos { get; set; } public LibraryOptions() { EnablePhotos = true; EnableRealtimeMonitor = true; + PathInfos = new MediaPathInfo[] { }; } } + + public class MediaPathInfo + { + public string Path { get; set; } + public string NetworkPath { get; set; } + } } diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index ae676626d..e4f1460c6 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -1509,7 +1509,7 @@ namespace MediaBrowser.Server.Implementations.Dto } } - private string GetMappedPath(IHasMetadata item) + private string GetMappedPath(BaseItem item) { var path = item.Path; @@ -1517,7 +1517,7 @@ namespace MediaBrowser.Server.Implementations.Dto if (locationType == LocationType.FileSystem || locationType == LocationType.Offline) { - path = _libraryManager.GetPathAfterNetworkSubstitution(path); + path = _libraryManager.GetPathAfterNetworkSubstitution(path, item); } return path; diff --git a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs index ba7e33890..b550d1dda 100644 --- a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs +++ b/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs @@ -33,7 +33,8 @@ namespace MediaBrowser.Server.Implementations.Library // Synology "@eaDir", - "eaDir" + "eaDir", + "#recycle" }; diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 1f8c77953..cef0022cf 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -2527,8 +2527,29 @@ namespace MediaBrowser.Server.Implementations.Library }).OrderBy(i => i.Path).ToList(); } - public string GetPathAfterNetworkSubstitution(string path) + public string GetPathAfterNetworkSubstitution(string path, BaseItem ownerItem) { + if (ownerItem != null) + { + var libraryOptions = GetLibraryOptions(ownerItem); + if (libraryOptions != null) + { + foreach (var pathInfo in libraryOptions.PathInfos) + { + if (string.IsNullOrWhiteSpace(pathInfo.NetworkPath)) + { + continue; + } + + var substitutionResult = SubstitutePathInternal(path, pathInfo.Path, pathInfo.NetworkPath); + if (substitutionResult.Item2) + { + return substitutionResult.Item1; + } + } + } + } + foreach (var map in ConfigurationManager.Configuration.PathSubstitutions) { path = SubstitutePath(path, map.From, map.To); @@ -2538,6 +2559,11 @@ namespace MediaBrowser.Server.Implementations.Library } public string SubstitutePath(string path, string from, string to) + { + return SubstitutePathInternal(path, from, to).Item1; + } + + private Tuple SubstitutePathInternal(string path, string from, string to) { if (string.IsNullOrWhiteSpace(path)) { @@ -2552,7 +2578,11 @@ namespace MediaBrowser.Server.Implementations.Library throw new ArgumentNullException("to"); } - var newPath = path.Replace(from.Trim(), to.Trim(), StringComparison.OrdinalIgnoreCase); + from = from.Trim(); + to = to.Trim(); + + var newPath = path.Replace(from, to, StringComparison.OrdinalIgnoreCase); + var changed = false; if (!string.Equals(newPath, path)) { @@ -2564,9 +2594,11 @@ namespace MediaBrowser.Server.Implementations.Library { newPath = newPath.Replace('/', '\\'); } + + changed = true; } - return newPath; + return new Tuple(newPath, changed); } private void SetExtraTypeFromFilename(Video item) @@ -2695,7 +2727,7 @@ namespace MediaBrowser.Server.Implementations.Library throw new InvalidOperationException(); } - public void AddVirtualFolder(string name, string collectionType, string[] mediaPaths, LibraryOptions options, bool refreshLibrary) + public void AddVirtualFolder(string name, string collectionType, LibraryOptions options, bool refreshLibrary) { if (string.IsNullOrWhiteSpace(name)) { @@ -2713,12 +2745,13 @@ namespace MediaBrowser.Server.Implementations.Library virtualFolderPath = Path.Combine(rootFolderPath, name); } - if (mediaPaths != null) + var mediaPathInfos = options.PathInfos; + if (mediaPathInfos != null) { - var invalidpath = mediaPaths.FirstOrDefault(i => !_fileSystem.DirectoryExists(i)); + var invalidpath = mediaPathInfos.FirstOrDefault(i => !_fileSystem.DirectoryExists(i.Path)); if (invalidpath != null) { - throw new ArgumentException("The specified path does not exist: " + invalidpath + "."); + throw new ArgumentException("The specified path does not exist: " + invalidpath.Path + "."); } } @@ -2740,11 +2773,11 @@ namespace MediaBrowser.Server.Implementations.Library CollectionFolder.SaveLibraryOptions(virtualFolderPath, options); - if (mediaPaths != null) + if (mediaPathInfos != null) { - foreach (var path in mediaPaths) + foreach (var path in mediaPathInfos) { - AddMediaPath(name, path); + AddMediaPathInternal(name, path, false); } } } @@ -2770,6 +2803,61 @@ namespace MediaBrowser.Server.Implementations.Library } } + private const string ShortcutFileExtension = ".mblink"; + private const string ShortcutFileSearch = "*" + ShortcutFileExtension; + public void AddMediaPath(string virtualFolderName, MediaPathInfo pathInfo) + { + AddMediaPathInternal(virtualFolderName, pathInfo, true); + } + + private void AddMediaPathInternal(string virtualFolderName, MediaPathInfo pathInfo, bool saveLibraryOptions) + { + if (pathInfo == null) + { + throw new ArgumentNullException("path"); + } + + var path = pathInfo.Path; + + if (string.IsNullOrWhiteSpace(path)) + { + throw new ArgumentNullException("path"); + } + + if (!_fileSystem.DirectoryExists(path)) + { + throw new DirectoryNotFoundException("The path does not exist."); + } + + var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; + var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName); + + var shortcutFilename = _fileSystem.GetFileNameWithoutExtension(path); + + var lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension); + + while (_fileSystem.FileExists(lnk)) + { + shortcutFilename += "1"; + lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension); + } + + _fileSystem.CreateShortcut(lnk, path); + + RemoveContentTypeOverrides(path); + + if (saveLibraryOptions) + { + var libraryOptions = CollectionFolder.GetLibraryOptions(virtualFolderPath); + + var list = libraryOptions.PathInfos.ToList(); + list.Add(pathInfo); + libraryOptions.PathInfos = list.ToArray(); + + CollectionFolder.SaveLibraryOptions(virtualFolderPath, libraryOptions); + } + } + public void RemoveVirtualFolder(string name, bool refreshLibrary) { if (string.IsNullOrWhiteSpace(name)) @@ -2814,38 +2902,6 @@ namespace MediaBrowser.Server.Implementations.Library } } - private const string ShortcutFileExtension = ".mblink"; - private const string ShortcutFileSearch = "*" + ShortcutFileExtension; - public void AddMediaPath(string virtualFolderName, string path) - { - if (string.IsNullOrWhiteSpace(path)) - { - throw new ArgumentNullException("path"); - } - - if (!_fileSystem.DirectoryExists(path)) - { - throw new DirectoryNotFoundException("The path does not exist."); - } - - var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; - var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName); - - var shortcutFilename = _fileSystem.GetFileNameWithoutExtension(path); - - var lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension); - - while (_fileSystem.FileExists(lnk)) - { - shortcutFilename += "1"; - lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension); - } - - _fileSystem.CreateShortcut(lnk, path); - - RemoveContentTypeOverrides(path); - } - private void RemoveContentTypeOverrides(string path) { if (string.IsNullOrWhiteSpace(path)) diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index a3ca54b4a..e358f9d25 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -143,9 +143,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV continue; } + var mediaPathInfos = pathsToCreate.Select(i => new MediaPathInfo { Path = i }).ToArray(); + + var libraryOptions = new LibraryOptions + { + PathInfos = mediaPathInfos + }; try { - _libraryManager.AddVirtualFolder(recordingFolder.Name, recordingFolder.CollectionType, pathsToCreate.ToArray(), new LibraryOptions(), true); + _libraryManager.AddVirtualFolder(recordingFolder.Name, recordingFolder.CollectionType, libraryOptions, true); } catch (Exception ex) { -- cgit v1.2.3 From 76c7bfcb6795771cb06ef354fbf76d6e39de8948 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 29 Sep 2016 08:55:49 -0400 Subject: update closing of streams --- MediaBrowser.Api/ApiEntryPoint.cs | 8 +- MediaBrowser.Api/LiveTv/LiveTvService.cs | 46 ++++++- MediaBrowser.Api/Playback/BaseStreamingService.cs | 16 +-- MediaBrowser.Api/Playback/MediaInfoService.cs | 2 +- .../Progressive/BaseProgressiveStreamingService.cs | 2 - MediaBrowser.Api/Playback/StreamState.cs | 2 +- .../Entities/InternalItemsQuery.cs | 2 + .../Library/IMediaSourceManager.cs | 3 +- .../Library/IMediaSourceProvider.cs | 3 +- MediaBrowser.Controller/LiveTv/ILiveTvManager.cs | 3 +- MediaBrowser.Controller/LiveTv/ITunerHost.cs | 5 +- MediaBrowser.Controller/LiveTv/LiveStream.cs | 23 +++- MediaBrowser.Controller/LiveTv/LiveTvChannel.cs | 53 +++++++- MediaBrowser.Controller/LiveTv/TimerInfo.cs | 1 + MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs | 2 +- MediaBrowser.Model/Dto/MediaSourceInfo.cs | 7 +- MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs | 24 ++++ MediaBrowser.Model/LiveTv/ProgramQuery.cs | 7 + .../LiveTv/RecommendedProgramQuery.cs | 6 + MediaBrowser.Model/LiveTv/RecordingQuery.cs | 1 + .../Channels/ChannelDynamicMediaSourceProvider.cs | 2 +- .../Library/MediaSourceManager.cs | 59 +++++---- .../LiveTv/EmbyTV/DirectRecorder.cs | 16 +-- .../LiveTv/EmbyTV/EmbyTV.cs | 143 +++++++++++++-------- .../LiveTv/EmbyTV/EncodedRecorder.cs | 39 +----- .../LiveTv/EmbyTV/RecordingHelper.cs | 1 + .../LiveTv/LiveTvManager.cs | 90 ++++++++----- .../LiveTv/LiveTvMediaSourceProvider.cs | 4 +- .../LiveTv/TunerHosts/BaseTunerHost.cs | 4 +- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 8 -- .../TunerHosts/HdHomerun/HdHomerunLiveStream.cs | 7 +- .../LiveTv/TunerHosts/M3UTunerHost.cs | 5 - .../Persistence/SqliteItemRepository.cs | 113 +++++++++++++--- .../Session/SessionManager.cs | 2 +- .../Sync/SyncedMediaSourceProvider.cs | 2 +- MediaBrowser.WebDashboard/Api/DashboardService.cs | 90 +------------ 36 files changed, 475 insertions(+), 326 deletions(-) (limited to 'MediaBrowser.Controller/Entities') diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index 2f5b9e1e0..c6e45c61a 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -567,7 +567,7 @@ namespace MediaBrowser.Api { try { - await _mediaSourceManager.CloseLiveStream(job.LiveStreamId, CancellationToken.None).ConfigureAwait(false); + await _mediaSourceManager.CloseLiveStream(job.LiveStreamId).ConfigureAwait(false); } catch (Exception ex) { @@ -789,12 +789,12 @@ namespace MediaBrowser.Api { if (KillTimer == null) { - Logger.Debug("Starting kill timer at {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); + //Logger.Debug("Starting kill timer at {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); KillTimer = new Timer(callback, this, intervalMs, Timeout.Infinite); } else { - Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); + //Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); KillTimer.Change(intervalMs, Timeout.Infinite); } } @@ -813,7 +813,7 @@ namespace MediaBrowser.Api { var intervalMs = PingTimeout; - Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); + //Logger.Debug("Changing kill timer to {0}ms. JobId {1} PlaySessionId {2}", intervalMs, Id, PlaySessionId); KillTimer.Change(intervalMs, Timeout.Infinite); } } diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index a5f8fce6e..9c1105082 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -48,6 +48,21 @@ namespace MediaBrowser.Api.LiveTv [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? StartIndex { get; set; } + [ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] + public bool? IsMovie { get; set; } + + [ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] + public bool? IsSeries { get; set; } + + [ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] + public bool? IsNews { get; set; } + + [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] + public bool? IsKids { get; set; } + + [ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] + public bool? IsSports { get; set; } + /// /// The maximum number of items to return /// @@ -163,6 +178,7 @@ namespace MediaBrowser.Api.LiveTv public bool? IsSeries { get; set; } public bool? IsKids { get; set; } public bool? IsSports { get; set; } + public bool? IsNews { get; set; } public GetRecordings() { @@ -309,6 +325,12 @@ namespace MediaBrowser.Api.LiveTv [ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] public bool? IsMovie { get; set; } + [ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] + public bool? IsSeries { get; set; } + + [ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] + public bool? IsNews { get; set; } + [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] public bool? IsKids { get; set; } @@ -380,15 +402,21 @@ namespace MediaBrowser.Api.LiveTv [ApiMember(Name = "HasAired", Description = "Optional. Filter by programs that have completed airing, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] public bool? HasAired { get; set; } - [ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] - public bool? IsSports { get; set; } + [ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] + public bool? IsSeries { get; set; } - [ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + [ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] public bool? IsMovie { get; set; } - [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + [ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] + public bool? IsNews { get; set; } + + [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] public bool? IsKids { get; set; } + [ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] + public bool? IsSports { 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; } @@ -815,6 +843,11 @@ namespace MediaBrowser.Api.LiveTv IsLiked = request.IsLiked, IsDisliked = request.IsDisliked, EnableFavoriteSorting = request.EnableFavoriteSorting, + IsMovie = request.IsMovie, + IsSeries = request.IsSeries, + IsNews = request.IsNews, + IsKids = request.IsKids, + IsSports = request.IsSports, AddCurrentProgram = request.AddCurrentProgram }, CancellationToken.None).ConfigureAwait(false); @@ -897,7 +930,9 @@ namespace MediaBrowser.Api.LiveTv query.Limit = request.Limit; query.SortBy = (request.SortBy ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); query.SortOrder = request.SortOrder; + query.IsNews = request.IsNews; query.IsMovie = request.IsMovie; + query.IsSeries = request.IsSeries; query.IsKids = request.IsKids; query.IsSports = request.IsSports; query.Genres = (request.Genres ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); @@ -915,8 +950,10 @@ namespace MediaBrowser.Api.LiveTv IsAiring = request.IsAiring, Limit = request.Limit, HasAired = request.HasAired, + IsSeries = request.IsSeries, IsMovie = request.IsMovie, IsKids = request.IsKids, + IsNews = request.IsNews, IsSports = request.IsSports, EnableTotalRecordCount = request.EnableTotalRecordCount }; @@ -948,6 +985,7 @@ namespace MediaBrowser.Api.LiveTv IsInProgress = request.IsInProgress, EnableTotalRecordCount = request.EnableTotalRecordCount, IsMovie = request.IsMovie, + IsNews = request.IsNews, IsSeries = request.IsSeries, IsKids = request.IsKids, IsSports = request.IsSports diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index fe9869664..8e57650b4 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1861,14 +1861,14 @@ namespace MediaBrowser.Api.Playback MediaSourceInfo mediaSource = null; if (string.IsNullOrWhiteSpace(request.LiveStreamId)) { - //TranscodingJob currentJob = !string.IsNullOrWhiteSpace(request.PlaySessionId) ? - // ApiEntryPoint.Instance.GetTranscodingJob(request.PlaySessionId) - // : null; - - //if (currentJob != null) - //{ - // mediaSource = currentJob.MediaSource; - //} + TranscodingJob currentJob = !string.IsNullOrWhiteSpace(request.PlaySessionId) ? + ApiEntryPoint.Instance.GetTranscodingJob(request.PlaySessionId) + : null; + + if (currentJob != null) + { + mediaSource = currentJob.MediaSource; + } if (mediaSource == null) { diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 656b2ee08..761538c83 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -140,7 +140,7 @@ namespace MediaBrowser.Api.Playback public void Post(CloseMediaSource request) { - var task = _mediaSourceManager.CloseLiveStream(request.LiveStreamId, CancellationToken.None); + var task = _mediaSourceManager.CloseLiveStream(request.LiveStreamId); Task.WaitAll(task); } diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index a68319109..4bb62f47f 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -27,12 +27,10 @@ namespace MediaBrowser.Api.Playback.Progressive public abstract class BaseProgressiveStreamingService : BaseStreamingService { protected readonly IImageProcessor ImageProcessor; - protected readonly IHttpClient HttpClient; protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer) { ImageProcessor = imageProcessor; - HttpClient = httpClient; } /// diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index ef0282abc..019f378c5 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -225,7 +225,7 @@ namespace MediaBrowser.Api.Playback { try { - await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId, CancellationToken.None).ConfigureAwait(false); + await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId).ConfigureAwait(false); } catch (Exception ex) { diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index deea63112..d917b7d6d 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -101,6 +101,8 @@ namespace MediaBrowser.Controller.Entities 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; } public int? MinPlayers { get; set; } public int? MaxPlayers { get; set; } diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs index 1df77cdc9..c06470c5e 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs @@ -92,8 +92,7 @@ namespace MediaBrowser.Controller.Library /// Closes the media source. /// /// The live stream identifier. - /// The cancellation token. /// Task. - Task CloseLiveStream(string id, CancellationToken cancellationToken); + Task CloseLiveStream(string id); } } diff --git a/MediaBrowser.Controller/Library/IMediaSourceProvider.cs b/MediaBrowser.Controller/Library/IMediaSourceProvider.cs index 5b033af4a..56366e5a8 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceProvider.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceProvider.cs @@ -28,8 +28,7 @@ namespace MediaBrowser.Controller.Library /// Closes the media source. /// /// The live stream identifier. - /// The cancellation token. /// Task. - Task CloseMediaSource(string liveStreamId, CancellationToken cancellationToken); + Task CloseMediaSource(string liveStreamId); } } diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index 41c5dbdbb..d65d1ae30 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -220,9 +220,8 @@ namespace MediaBrowser.Controller.LiveTv /// Closes the live stream. /// /// The identifier. - /// The cancellation token. /// Task. - Task CloseLiveStream(string id, CancellationToken cancellationToken); + Task CloseLiveStream(string id); /// /// Gets the guide information. diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs index 3c8b964a2..89d035649 100644 --- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs +++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs @@ -22,9 +22,8 @@ namespace MediaBrowser.Controller.LiveTv /// /// Gets the channels. /// - /// The cancellation token. /// Task<IEnumerable<ChannelInfo>>. - Task> GetChannels(CancellationToken cancellationToken); + Task> GetChannels(bool enableCache, CancellationToken cancellationToken); /// /// Gets the tuner infos. /// @@ -46,8 +45,6 @@ namespace MediaBrowser.Controller.LiveTv /// The cancellation token. /// Task<List<MediaSourceInfo>>. Task> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken); - - string ApplyDuration(string streamPath, TimeSpan duration); } public interface IConfigurableTunerHost { diff --git a/MediaBrowser.Controller/LiveTv/LiveStream.cs b/MediaBrowser.Controller/LiveTv/LiveStream.cs index 15d09d857..1bb198632 100644 --- a/MediaBrowser.Controller/LiveTv/LiveStream.cs +++ b/MediaBrowser.Controller/LiveTv/LiveStream.cs @@ -1,4 +1,5 @@ -using System.Threading; +using System; +using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Dto; @@ -7,17 +8,27 @@ namespace MediaBrowser.Controller.LiveTv public class LiveStream { public MediaSourceInfo OriginalMediaSource { get; set; } - public MediaSourceInfo PublicMediaSource { get; set; } - public string Id { get; set; } + public MediaSourceInfo OpenedMediaSource { get; set; } + public DateTime DateOpened { get; set; } + public int ConsumerCount { get; set; } + public ITunerHost TunerHost { get; set; } + public string OriginalStreamId { get; set; } public LiveStream(MediaSourceInfo mediaSource) { OriginalMediaSource = mediaSource; - PublicMediaSource = mediaSource; - Id = mediaSource.Id; + OpenedMediaSource = mediaSource; } - public virtual Task Open(CancellationToken cancellationToken) + public async Task Open(CancellationToken cancellationToken) + { + await OpenInternal(cancellationToken).ConfigureAwait(false); + DateOpened = DateTime.UtcNow; + + OpenedMediaSource.DateLiveStreamOpened = DateOpened; + } + + protected virtual Task OpenInternal(CancellationToken cancellationToken) { return Task.FromResult(true); } diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs index 50aeed27d..69a1c24ea 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs @@ -9,7 +9,7 @@ using System.Runtime.Serialization; namespace MediaBrowser.Controller.LiveTv { - public class LiveTvChannel : BaseItem, IHasMediaSources + public class LiveTvChannel : BaseItem, IHasMediaSources, IHasProgramAttributes { public override List GetUserDataKeys() { @@ -137,5 +137,56 @@ namespace MediaBrowser.Controller.LiveTv { return false; } + + [IgnoreDataMember] + public bool IsMovie { get; set; } + + /// + /// Gets or sets a value indicating whether this instance is sports. + /// + /// true if this instance is sports; otherwise, false. + [IgnoreDataMember] + public bool IsSports { get; set; } + + /// + /// Gets or sets a value indicating whether this instance is series. + /// + /// true if this instance is series; otherwise, false. + [IgnoreDataMember] + public bool IsSeries { get; set; } + + /// + /// Gets or sets a value indicating whether this instance is live. + /// + /// true if this instance is live; otherwise, false. + [IgnoreDataMember] + public bool IsLive { get; set; } + + /// + /// Gets or sets a value indicating whether this instance is news. + /// + /// true if this instance is news; otherwise, false. + [IgnoreDataMember] + public bool IsNews { get; set; } + + /// + /// Gets or sets a value indicating whether this instance is kids. + /// + /// true if this instance is kids; otherwise, false. + [IgnoreDataMember] + public bool IsKids { get; set; } + + [IgnoreDataMember] + public bool IsPremiere { get; set; } + + [IgnoreDataMember] + public bool IsRepeat { get; set; } + + /// + /// Gets or sets the episode title. + /// + /// The episode title. + [IgnoreDataMember] + public string EpisodeTitle { get; set; } } } diff --git a/MediaBrowser.Controller/LiveTv/TimerInfo.cs b/MediaBrowser.Controller/LiveTv/TimerInfo.cs index 42c3480ce..978e9e1a8 100644 --- a/MediaBrowser.Controller/LiveTv/TimerInfo.cs +++ b/MediaBrowser.Controller/LiveTv/TimerInfo.cs @@ -101,6 +101,7 @@ namespace MediaBrowser.Controller.LiveTv public bool IsMovie { get; set; } public bool IsKids { get; set; } public bool IsSports { get; set; } + public bool IsNews { get; set; } public int? ProductionYear { get; set; } public string EpisodeTitle { get; set; } public DateTime? OriginalAirDate { get; set; } diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs index 490a51128..d3738d903 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs @@ -140,7 +140,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { try { - await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId, CancellationToken.None).ConfigureAwait(false); + await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId).ConfigureAwait(false); } catch (Exception ex) { diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs index bb07d9cb6..2be69c8e4 100644 --- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs +++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Model.Entities; +using System; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.Extensions; using MediaBrowser.Model.MediaInfo; using System.Collections.Generic; @@ -52,7 +53,9 @@ namespace MediaBrowser.Model.Dto public string TranscodingUrl { get; set; } public string TranscodingSubProtocol { get; set; } public string TranscodingContainer { get; set; } - + + public DateTime? DateLiveStreamOpened { get; set; } + public MediaSourceInfo() { Formats = new List(); diff --git a/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs b/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs index 0ece1e4a0..f76368a7b 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs @@ -61,6 +61,30 @@ namespace MediaBrowser.Model.LiveTv public bool AddCurrentProgram { get; set; } public bool EnableUserData { get; set; } + /// + /// Used to specific whether to return news or not + /// + /// If set to null, all programs will be returned + public bool? IsNews { get; set; } + + /// + /// Used to specific whether to return movies or not + /// + /// If set to null, all programs will be returned + public bool? IsMovie { get; set; } + + /// + /// Gets or sets a value indicating whether this instance is kids. + /// + /// null if [is kids] contains no value, true if [is kids]; otherwise, false. + public bool? IsKids { get; set; } + /// + /// Gets or sets a value indicating whether this instance is sports. + /// + /// null if [is sports] contains no value, true if [is sports]; otherwise, false. + public bool? IsSports { get; set; } + public bool? IsSeries { get; set; } + public LiveTvChannelQuery() { EnableUserData = true; diff --git a/MediaBrowser.Model/LiveTv/ProgramQuery.cs b/MediaBrowser.Model/LiveTv/ProgramQuery.cs index bf459237a..7886342e7 100644 --- a/MediaBrowser.Model/LiveTv/ProgramQuery.cs +++ b/MediaBrowser.Model/LiveTv/ProgramQuery.cs @@ -62,6 +62,12 @@ namespace MediaBrowser.Model.LiveTv /// public DateTime? MaxEndDate { get; set; } + /// + /// Used to specific whether to return news or not + /// + /// If set to null, all programs will be returned + public bool? IsNews { get; set; } + /// /// Used to specific whether to return movies or not /// @@ -83,6 +89,7 @@ namespace MediaBrowser.Model.LiveTv /// Skips over a given number of items within the results. Use for paging. /// public int? StartIndex { get; set; } + public bool? IsSeries { get; set; } /// /// Gets or sets a value indicating whether this instance has aired. diff --git a/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs b/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs index 0e6d081a1..4bc506bf6 100644 --- a/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs +++ b/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs @@ -45,11 +45,17 @@ namespace MediaBrowser.Model.LiveTv /// The limit. public int? Limit { get; set; } + /// + /// Gets or sets a value indicating whether this instance is movie. + /// + /// null if [is movie] contains no value, true if [is movie]; otherwise, false. + public bool? IsNews { get; set; } /// /// Gets or sets a value indicating whether this instance is movie. /// /// null if [is movie] contains no value, true if [is movie]; otherwise, false. public bool? IsMovie { get; set; } + public bool? IsSeries { get; set; } /// /// Gets or sets a value indicating whether this instance is kids. /// diff --git a/MediaBrowser.Model/LiveTv/RecordingQuery.cs b/MediaBrowser.Model/LiveTv/RecordingQuery.cs index 0ba5f1779..265aad335 100644 --- a/MediaBrowser.Model/LiveTv/RecordingQuery.cs +++ b/MediaBrowser.Model/LiveTv/RecordingQuery.cs @@ -68,6 +68,7 @@ namespace MediaBrowser.Model.LiveTv /// The fields. public ItemFields[] Fields { get; set; } public bool? EnableImages { get; set; } + public bool? IsNews { get; set; } public bool? IsMovie { get; set; } public bool? IsSeries { get; set; } public bool? IsKids { get; set; } diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs index 3239b20b2..6cba1b441 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs @@ -35,7 +35,7 @@ namespace MediaBrowser.Server.Implementations.Channels throw new NotImplementedException(); } - public Task CloseMediaSource(string liveStreamId, CancellationToken cancellationToken) + public Task CloseMediaSource(string liveStreamId) { throw new NotImplementedException(); } diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index c20245a6e..c7650102f 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs @@ -355,7 +355,7 @@ namespace MediaBrowser.Server.Implementations.Library .ToList(); } - private readonly ConcurrentDictionary _openStreams = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private readonly Dictionary _openStreams = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1); public async Task OpenLiveStream(LiveStreamRequest request, bool enableAutoClose, CancellationToken cancellationToken) @@ -383,7 +383,7 @@ namespace MediaBrowser.Server.Implementations.Library Id = mediaSource.LiveStreamId, MediaSource = mediaSource }; - _openStreams.AddOrUpdate(mediaSource.LiveStreamId, info, (key, i) => info); + _openStreams[mediaSource.LiveStreamId] = info; if (enableAutoClose) { @@ -421,7 +421,7 @@ namespace MediaBrowser.Server.Implementations.Library throw new ArgumentNullException("id"); } - _logger.Debug("Getting live stream {0}", id); + _logger.Debug("Getting already opened live stream {0}", id); await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); @@ -465,17 +465,16 @@ namespace MediaBrowser.Server.Implementations.Library } } - private async Task CloseLiveStreamWithProvider(IMediaSourceProvider provider, string streamId, CancellationToken cancellationToken) + private async Task CloseLiveStreamWithProvider(IMediaSourceProvider provider, string streamId) { _logger.Info("Closing live stream {0} with provider {1}", streamId, provider.GetType().Name); try { - await provider.CloseMediaSource(streamId, cancellationToken).ConfigureAwait(false); + await provider.CloseMediaSource(streamId).ConfigureAwait(false); } catch (NotImplementedException) { - } catch (Exception ex) { @@ -483,37 +482,35 @@ namespace MediaBrowser.Server.Implementations.Library } } - public async Task CloseLiveStream(string id, CancellationToken cancellationToken) + public async Task CloseLiveStream(string id) { if (string.IsNullOrWhiteSpace(id)) { throw new ArgumentNullException("id"); } - await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + await _liveStreamSemaphore.WaitAsync().ConfigureAwait(false); try { LiveStreamInfo current; + if (_openStreams.TryGetValue(id, out current)) { + _openStreams.Remove(id); + current.Closed = true; + if (current.MediaSource.RequiresClosing) { var tuple = GetProvider(id); - await CloseLiveStreamWithProvider(tuple.Item1, tuple.Item2, cancellationToken).ConfigureAwait(false); + await CloseLiveStreamWithProvider(tuple.Item1, tuple.Item2).ConfigureAwait(false); } - } - LiveStreamInfo removed; - if (_openStreams.TryRemove(id, out removed)) - { - removed.Closed = true; - } - - if (_openStreams.Count == 0) - { - StopCloseTimer(); + if (_openStreams.Count == 0) + { + StopCloseTimer(); + } } } finally @@ -565,10 +562,20 @@ namespace MediaBrowser.Server.Implementations.Library private async void CloseTimerCallback(object state) { - var infos = _openStreams - .Values - .Where(i => i.EnableCloseTimer && DateTime.UtcNow - i.Date > _openStreamMaxAge) - .ToList(); + List infos; + await _liveStreamSemaphore.WaitAsync().ConfigureAwait(false); + + try + { + infos = _openStreams + .Values + .Where(i => i.EnableCloseTimer && DateTime.UtcNow - i.Date > _openStreamMaxAge) + .ToList(); + } + finally + { + _liveStreamSemaphore.Release(); + } foreach (var info in infos) { @@ -576,7 +583,7 @@ namespace MediaBrowser.Server.Implementations.Library { try { - await CloseLiveStream(info.Id, CancellationToken.None).ConfigureAwait(false); + await CloseLiveStream(info.Id).ConfigureAwait(false); } catch (Exception ex) { @@ -608,12 +615,10 @@ namespace MediaBrowser.Server.Implementations.Library { foreach (var key in _openStreams.Keys.ToList()) { - var task = CloseLiveStream(key, CancellationToken.None); + var task = CloseLiveStream(key); Task.WaitAll(task); } - - _openStreams.Clear(); } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs index 0d043669a..0f8c15e71 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs @@ -47,19 +47,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _logger.Info("Copying recording stream to file {0}", targetFile); - if (mediaSource.RunTimeTicks.HasValue) - { - // The media source already has a fixed duration - // But add another stop 1 minute later just in case the recording gets stuck for any reason - var durationToken = new CancellationTokenSource(duration.Add(TimeSpan.FromMinutes(1))); - cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; - } - else - { - // The media source if infinite so we need to handle stopping ourselves - var durationToken = new CancellationTokenSource(duration); - cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; - } + // The media source if infinite so we need to handle stopping ourselves + var durationToken = new CancellationTokenSource(duration); + cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; await CopyUntilCancelled(response.Content, output, cancellationToken).ConfigureAwait(false); } diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index f53ec3ee8..ef19dcbc9 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -340,22 +340,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _timerProvider.Delete(timer); } - private List _channelCache = null; private async Task> GetChannelsAsync(bool enableCache, CancellationToken cancellationToken) { - if (enableCache && _channelCache != null) - { - - return _channelCache.ToList(); - } - var list = new List(); foreach (var hostInstance in _liveTvManager.TunerHosts) { try { - var channels = await hostInstance.GetChannels(cancellationToken).ConfigureAwait(false); + var channels = await hostInstance.GetChannels(enableCache, cancellationToken).ConfigureAwait(false); list.AddRange(channels); } @@ -388,7 +381,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } - _channelCache = list.ToList(); return list; } @@ -400,7 +392,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { try { - var channels = await hostInstance.GetChannels(cancellationToken).ConfigureAwait(false); + var channels = await hostInstance.GetChannels(false, cancellationToken).ConfigureAwait(false); list.AddRange(channels); } @@ -632,6 +624,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV existingTimer.Genres = updatedTimer.Genres; existingTimer.HomePageUrl = updatedTimer.HomePageUrl; existingTimer.IsKids = updatedTimer.IsKids; + existingTimer.IsNews = updatedTimer.IsNews; existingTimer.IsMovie = updatedTimer.IsMovie; existingTimer.IsProgramSeries = updatedTimer.IsProgramSeries; existingTimer.IsSports = updatedTimer.IsSports; @@ -836,33 +829,68 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { var result = await GetChannelStreamInternal(channelId, streamId, cancellationToken).ConfigureAwait(false); - return result.Item1.PublicMediaSource; + return result.Item2; + } + + private MediaSourceInfo CloneMediaSource(MediaSourceInfo mediaSource, int consumerId) + { + var json = _jsonSerializer.SerializeToString(mediaSource); + mediaSource = _jsonSerializer.DeserializeFromString(json); + + mediaSource.Id = consumerId.ToString(CultureInfo.InvariantCulture) + "_" + mediaSource.Id; + + return mediaSource; } - private async Task> GetChannelStreamInternal(string channelId, string streamId, CancellationToken cancellationToken) + private async Task> GetChannelStreamInternal(string channelId, string streamId, CancellationToken cancellationToken) { _logger.Info("Streaming Channel " + channelId); - foreach (var hostInstance in _liveTvManager.TunerHosts) + await _liveStreamsSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + + var result = _liveStreams.Values.FirstOrDefault(i => string.Equals(i.OriginalStreamId, streamId, StringComparison.OrdinalIgnoreCase)); + + if (result != null) { - try - { - var result = await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false); + //result.ConsumerCount++; - await _liveStreamsSemaphore.WaitAsync().ConfigureAwait(false); - _liveStreams[result.Id] = result; - _liveStreamsSemaphore.Release(); + //_logger.Info("Live stream {0} consumer count is now {1}", streamId, result.ConsumerCount); - return new Tuple(result, hostInstance); - } - catch (FileNotFoundException) - { - } - catch (Exception e) + //_liveStreamsSemaphore.Release(); + //return new Tuple(result, CloneMediaSource(result.OpenedMediaSource, result.ConsumerCount - 1), result.TunerHost); + } + + try + { + foreach (var hostInstance in _liveTvManager.TunerHosts) { - _logger.ErrorException("Error getting channel stream", e); + try + { + result = await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false); + + _liveStreams[result.OpenedMediaSource.Id] = result; + + result.ConsumerCount++; + result.TunerHost = hostInstance; + result.OriginalStreamId = streamId; + + _logger.Info("Returning mediasource streamId {0}, mediaSource.Id {1}, mediaSource.LiveStreamId {2}", + streamId, result.OpenedMediaSource.Id, result.OpenedMediaSource.LiveStreamId); + + return new Tuple(result, CloneMediaSource(result.OpenedMediaSource, 0), hostInstance); + } + catch (FileNotFoundException) + { + } + catch (OperationCanceledException) + { + } } } + finally + { + _liveStreamsSemaphore.Release(); + } throw new ApplicationException("Tuner not found."); } @@ -896,25 +924,41 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV public async Task CloseLiveStream(string id, CancellationToken cancellationToken) { - await _liveStreamsSemaphore.WaitAsync().ConfigureAwait(false); + // Ignore the consumer id + id = id.Substring(id.IndexOf('_') + 1); + + await _liveStreamsSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); try { LiveStream stream; if (_liveStreams.TryGetValue(id, out stream)) { - _liveStreams.Remove(id); + stream.ConsumerCount--; - try + _logger.Info("Live stream {0} consumer count is now {1}", id, stream.ConsumerCount); + + if (stream.ConsumerCount <= 0) { + _liveStreams.Remove(id); + + _logger.Info("Closing live stream {0}", id); + await stream.Close().ConfigureAwait(false); _logger.Info("Live stream {0} closed successfully", id); } - catch (Exception ex) - { - _logger.ErrorException("Error closing live stream", ex); - } } + else + { + _logger.Warn("Live stream not found: {0}, unable to close", id); + } + } + catch (OperationCanceledException) + { + } + catch (Exception ex) + { + _logger.ErrorException("Error closing live stream", ex); } finally { @@ -1095,20 +1139,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV var recordPath = GetRecordingPath(timer, out seriesPath); var recordingStatus = RecordingStatus.New; - LiveStream liveStream = null; + string liveStreamId = null; try { var allMediaSources = await GetChannelStreamMediaSources(timer.ChannelId, CancellationToken.None).ConfigureAwait(false); - var liveStreamInfo = - await - GetChannelStreamInternal(timer.ChannelId, allMediaSources[0].Id, CancellationToken.None) + var liveStreamInfo = await GetChannelStreamInternal(timer.ChannelId, allMediaSources[0].Id, CancellationToken.None) .ConfigureAwait(false); - liveStream = liveStreamInfo.Item1; - var mediaStreamInfo = liveStreamInfo.Item1.PublicMediaSource; - var tunerHost = liveStreamInfo.Item2; + + var mediaStreamInfo = liveStreamInfo.Item2; + liveStreamId = mediaStreamInfo.Id; // HDHR doesn't seem to release the tuner right away after first probing with ffmpeg //await Task.Delay(3000, cancellationToken).ConfigureAwait(false); @@ -1140,15 +1182,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV EnforceKeepUpTo(timer); }; - var pathWithDuration = tunerHost.ApplyDuration(mediaStreamInfo.Path, duration); - - // If it supports supplying duration via url - if (!string.Equals(pathWithDuration, mediaStreamInfo.Path, StringComparison.OrdinalIgnoreCase)) - { - mediaStreamInfo.Path = pathWithDuration; - mediaStreamInfo.RunTimeTicks = duration.Ticks; - } - await recorder.Record(mediaStreamInfo, recordPath, duration, onStarted, cancellationToken) .ConfigureAwait(false); @@ -1166,11 +1199,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV recordingStatus = RecordingStatus.Error; } - if (liveStream != null) + if (!string.IsNullOrWhiteSpace(liveStreamId)) { try { - await CloseLiveStream(liveStream.Id, CancellationToken.None).ConfigureAwait(false); + await CloseLiveStream(liveStreamId, CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { @@ -1251,7 +1284,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } - private readonly SemaphoreSlim _recordingDeleteSemaphore = new SemaphoreSlim(1,1); + private readonly SemaphoreSlim _recordingDeleteSemaphore = new SemaphoreSlim(1, 1); private async Task DeleteLibraryItemsForTimers(List timers) { foreach (var timer in timers) @@ -1295,7 +1328,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } catch (FileNotFoundException) { - + } } @@ -1492,6 +1525,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { AddGenre(timer.Genres, "Kids"); } + if (timer.IsNews) + { + AddGenre(timer.Genres, "News"); + } foreach (var genre in timer.Genres) { diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index f74a76e3f..ce5f14f4b 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -71,38 +71,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV var durationToken = new CancellationTokenSource(duration); cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token; - await RecordFromFile(mediaSource, mediaSource.Path, targetFile, false, duration, onStarted, cancellationToken).ConfigureAwait(false); + await RecordFromFile(mediaSource, mediaSource.Path, targetFile, duration, onStarted, cancellationToken).ConfigureAwait(false); _logger.Info("Recording completed to file {0}", targetFile); } - private async void DeleteTempFile(string path) - { - for (var i = 0; i < 10; i++) - { - try - { - File.Delete(path); - return; - } - catch (FileNotFoundException) - { - return; - } - catch (DirectoryNotFoundException) - { - return; - } - catch (Exception ex) - { - _logger.ErrorException("Error deleting recording temp file", ex); - } - - await Task.Delay(1000).ConfigureAwait(false); - } - } - - private Task RecordFromFile(MediaSourceInfo mediaSource, string inputFile, string targetFile, bool deleteInputFileAfterCompletion, TimeSpan duration, Action onStarted, CancellationToken cancellationToken) + private Task RecordFromFile(MediaSourceInfo mediaSource, string inputFile, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken) { _targetPath = targetFile; _fileSystem.CreateDirectory(Path.GetDirectoryName(targetFile)); @@ -143,7 +117,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(_json.SerializeToString(mediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine); _logFileStream.Write(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length); - process.Exited += (sender, args) => OnFfMpegProcessExited(process, inputFile, deleteInputFileAfterCompletion); + process.Exited += (sender, args) => OnFfMpegProcessExited(process, inputFile); process.Start(); @@ -252,7 +226,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV /// /// Processes the exited. /// - private void OnFfMpegProcessExited(Process process, string inputFile, bool deleteInputFileAfterCompletion) + private void OnFfMpegProcessExited(Process process, string inputFile) { _hasExited = true; @@ -278,11 +252,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV _logger.Error("FFMpeg recording exited with an error for {0}.", _targetPath); _taskCompletionSource.TrySetException(new Exception(string.Format("Recording for {0} failed", _targetPath))); } - - if (deleteInputFileAfterCompletion) - { - DeleteTempFile(inputFile); - } } private void DisposeLogStream() diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs index f9c04abc5..bb6935e8e 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs @@ -42,6 +42,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV timerInfo.EpisodeNumber = programInfo.EpisodeNumber; timerInfo.IsMovie = programInfo.IsMovie; timerInfo.IsKids = programInfo.IsKids; + timerInfo.IsNews = programInfo.IsNews; timerInfo.IsSports = programInfo.IsSports; timerInfo.ProductionYear = programInfo.ProductionYear; timerInfo.EpisodeTitle = programInfo.EpisodeTitle; diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 8a4572813..7c61d2396 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -62,9 +62,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv private readonly List _services = new List(); - private readonly ConcurrentDictionary _openStreams = - new ConcurrentDictionary(); - private readonly SemaphoreSlim _refreshRecordingsLock = new SemaphoreSlim(1, 1); private readonly List _tunerHosts = new List(); @@ -153,6 +150,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv var channels = _libraryManager.GetItemList(new InternalItemsQuery { + IsMovie = query.IsMovie, + IsNews = query.IsNews, + IsKids = query.IsKids, + IsSports = query.IsSports, + IsSeries = query.IsSeries, IncludeItemTypes = new[] { typeof(LiveTvChannel).Name }, SortBy = new[] { ItemSortBy.SortName }, TopParentIds = new[] { topFolder.Id.ToString("N") } @@ -407,15 +409,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv _logger.Info("Live stream info: {0}", _jsonSerializer.SerializeToString(info)); Normalize(info, service, isVideo); - var data = new LiveStreamData - { - Info = info, - IsChannel = isChannel, - ItemId = id - }; - - _openStreams.AddOrUpdate(info.Id, data, (key, i) => data); - return info; } catch (Exception ex) @@ -937,8 +930,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv MaxStartDate = query.MaxStartDate, ChannelIds = query.ChannelIds, IsMovie = query.IsMovie, + IsSeries = query.IsSeries, IsSports = query.IsSports, IsKids = query.IsKids, + IsNews = query.IsNews, Genres = query.Genres, StartIndex = query.StartIndex, Limit = query.Limit, @@ -985,7 +980,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv { IncludeItemTypes = new[] { typeof(LiveTvProgram).Name }, IsAiring = query.IsAiring, + IsNews = query.IsNews, IsMovie = query.IsMovie, + IsSeries = query.IsSeries, IsSports = query.IsSports, IsKids = query.IsKids, EnableTotalRecordCount = query.EnableTotalRecordCount, @@ -1014,7 +1011,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv var programList = programs.ToList(); - var factorChannelWatchCount = (query.IsAiring ?? false) || (query.IsKids ?? false) || (query.IsSports ?? false) || (query.IsMovie ?? false); + var factorChannelWatchCount = (query.IsAiring ?? false) || (query.IsKids ?? false) || (query.IsSports ?? false) || (query.IsMovie ?? false) || (query.IsNews ?? false) || (query.IsSeries ?? false); programs = programList.OrderBy(i => i.StartDate.Date) .ThenByDescending(i => GetRecommendationScore(i, user.Id, factorChannelWatchCount)) @@ -1305,6 +1302,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv var start = DateTime.UtcNow.AddHours(-1); var end = start.AddDays(guideDays); + var isMovie = false; + var isSports = false; + var isNews = false; + var isKids = false; + var iSSeries = false; + var channelPrograms = await service.GetProgramsAsync(currentChannel.ExternalId, start, end, cancellationToken).ConfigureAwait(false); foreach (var program in channelPrograms) @@ -1312,7 +1315,40 @@ namespace MediaBrowser.Server.Implementations.LiveTv var programItem = await GetProgram(program, currentChannel, currentChannel.ChannelType, service.Name, cancellationToken).ConfigureAwait(false); programs.Add(programItem.Id); + + if (program.IsMovie) + { + isMovie = true; + } + + if (program.IsSeries) + { + iSSeries = true; + } + + if (program.IsSports) + { + isSports = true; + } + + if (program.IsNews) + { + isNews = true; + } + + if (program.IsKids) + { + isKids = true; + } } + + currentChannel.IsMovie = isMovie; + currentChannel.IsNews = isNews; + currentChannel.IsSports = isSports; + currentChannel.IsKids = isKids; + currentChannel.IsSeries = iSSeries; + + await currentChannel.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) { @@ -1647,6 +1683,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv recordings = recordings.Where(i => i.IsMovie == val); } + if (query.IsNews.HasValue) + { + var val = query.IsNews.Value; + recordings = recordings.Where(i => i.IsNews == val); + } + if (query.IsSeries.HasValue) { var val = query.IsSeries.Value; @@ -2444,9 +2486,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv internal bool IsChannel; } - public async Task CloseLiveStream(string id, CancellationToken cancellationToken) + public async Task CloseLiveStream(string id) { - await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + await _liveStreamSemaphore.WaitAsync().ConfigureAwait(false); try { @@ -2461,12 +2503,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv id = parts[1]; - LiveStreamData data; - _openStreams.TryRemove(id, out data); - _logger.Info("Closing live stream from {0}, stream Id: {1}", service.Name, id); - await service.CloseLiveStream(id, cancellationToken).ConfigureAwait(false); + await service.CloseLiveStream(id, CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { @@ -2500,7 +2539,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv Dispose(true); } - private readonly object _disposeLock = new object(); private bool _isDisposed = false; /// /// Releases unmanaged and - optionally - managed resources. @@ -2511,18 +2549,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv if (dispose) { _isDisposed = true; - - lock (_disposeLock) - { - foreach (var stream in _openStreams.Values.ToList()) - { - var task = CloseLiveStream(stream.Info.Id, CancellationToken.None); - - Task.WaitAll(task); - } - - _openStreams.Clear(); - } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs index cdba1873e..aacc0c22b 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs @@ -204,9 +204,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv } } - public Task CloseMediaSource(string liveStreamId, CancellationToken cancellationToken) + public Task CloseMediaSource(string liveStreamId) { - return _liveTvManager.CloseLiveStream(liveStreamId, cancellationToken); + return _liveTvManager.CloseLiveStream(liveStreamId); } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs index 6beea352a..a4236763f 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs @@ -73,7 +73,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts .ToList(); } - public async Task> GetChannels(CancellationToken cancellationToken) + public async Task> GetChannels(bool enableCache, CancellationToken cancellationToken) { var list = new List(); @@ -83,7 +83,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts { try { - var channels = await GetChannels(host, true, cancellationToken).ConfigureAwait(false); + var channels = await GetChannels(host, enableCache, cancellationToken).ConfigureAwait(false); var newChannels = channels.Where(i => !list.Any(l => string.Equals(i.Id, l.Id, StringComparison.OrdinalIgnoreCase))).ToList(); list.AddRange(newChannels); diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index b40b74436..9f71940e1 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -67,14 +67,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun return id; } - public string ApplyDuration(string streamPath, TimeSpan duration) - { - streamPath += streamPath.IndexOf('?') == -1 ? "?" : "&"; - streamPath += "duration=" + Convert.ToInt32(duration.TotalSeconds).ToString(CultureInfo.InvariantCulture); - - return streamPath; - } - private async Task> GetLineup(TunerHostInfo info, CancellationToken cancellationToken) { var options = new HttpRequestOptions diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs index d3540d180..d6574db22 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs @@ -34,7 +34,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun _appHost = appHost; } - public override async Task Open(CancellationToken openCancellationToken) + protected override async Task OpenInternal(CancellationToken openCancellationToken) { _liveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested(); @@ -54,13 +54,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun await taskCompletionSource.Task.ConfigureAwait(false); - PublicMediaSource.Path = _appHost.GetLocalApiUrl("localhost") + "/LiveTv/LiveStreamFiles/" + Path.GetFileNameWithoutExtension(tempFile) + "/stream.ts"; + OpenedMediaSource.Path = _appHost.GetLocalApiUrl("localhost") + "/LiveTv/LiveStreamFiles/" + Path.GetFileNameWithoutExtension(tempFile) + "/stream.ts"; - PublicMediaSource.Protocol = MediaProtocol.Http; + OpenedMediaSource.Protocol = MediaProtocol.Http; } public override Task Close() { + _logger.Info("Closing HDHR live stream"); _liveStreamCancellationTokenSource.Cancel(); return _liveStreamTaskCompletionSource.Task; diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index d9c0bb8bf..e5d6102b1 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -153,10 +153,5 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts { return Task.FromResult(true); } - - public string ApplyDuration(string streamPath, TimeSpan duration) - { - return streamPath; - } } } diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index 05c282687..097118418 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -2530,38 +2530,111 @@ namespace MediaBrowser.Server.Implementations.Persistence whereClauses.Add("IsOffline=@IsOffline"); cmd.Parameters.Add(cmd, "@IsOffline", DbType.Boolean).Value = query.IsOffline; } - if (query.IsMovie.HasValue) + + var exclusiveProgramAttribtues = !(query.IsMovie ?? true) || + !(query.IsSports ?? true) || + !(query.IsKids ?? true) || + !(query.IsNews ?? true) || + !(query.IsSeries ?? true); + + if (exclusiveProgramAttribtues) { - var alternateTypes = new List(); - if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Movie).Name)) + if (query.IsMovie.HasValue) + { + var alternateTypes = new List(); + if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Movie).Name)) + { + alternateTypes.Add(typeof(Movie).FullName); + } + if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Trailer).Name)) + { + alternateTypes.Add(typeof(Trailer).FullName); + } + + if (alternateTypes.Count == 0) + { + whereClauses.Add("IsMovie=@IsMovie"); + cmd.Parameters.Add(cmd, "@IsMovie", DbType.Boolean).Value = query.IsMovie; + } + else + { + whereClauses.Add("(IsMovie is null OR IsMovie=@IsMovie)"); + cmd.Parameters.Add(cmd, "@IsMovie", DbType.Boolean).Value = query.IsMovie; + } + } + if (query.IsSeries.HasValue) { - alternateTypes.Add(typeof(Movie).FullName); + whereClauses.Add("IsSeries=@IsSeries"); + cmd.Parameters.Add(cmd, "@IsSeries", DbType.Boolean).Value = query.IsSeries; } - if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Trailer).Name)) + if (query.IsNews.HasValue) { - alternateTypes.Add(typeof(Trailer).FullName); + whereClauses.Add("IsNews=@IsNews"); + cmd.Parameters.Add(cmd, "@IsNews", DbType.Boolean).Value = query.IsNews; } - - if (alternateTypes.Count == 0) + if (query.IsKids.HasValue) { - whereClauses.Add("IsMovie=@IsMovie"); + whereClauses.Add("IsKids=@IsKids"); + cmd.Parameters.Add(cmd, "@IsKids", DbType.Boolean).Value = query.IsKids; } - else + if (query.IsSports.HasValue) { - whereClauses.Add("(IsMovie is null OR IsMovie=@IsMovie)"); + whereClauses.Add("IsSports=@IsSports"); + cmd.Parameters.Add(cmd, "@IsSports", DbType.Boolean).Value = query.IsSports; } - cmd.Parameters.Add(cmd, "@IsMovie", DbType.Boolean).Value = query.IsMovie; } - if (query.IsKids.HasValue) - { - whereClauses.Add("IsKids=@IsKids"); - cmd.Parameters.Add(cmd, "@IsKids", DbType.Boolean).Value = query.IsKids; - } - if (query.IsSports.HasValue) + else { - whereClauses.Add("IsSports=@IsSports"); - cmd.Parameters.Add(cmd, "@IsSports", DbType.Boolean).Value = query.IsSports; + var programAttribtues = new List(); + if (query.IsMovie ?? false) + { + var alternateTypes = new List(); + if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Movie).Name)) + { + alternateTypes.Add(typeof(Movie).FullName); + } + if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Trailer).Name)) + { + alternateTypes.Add(typeof(Trailer).FullName); + } + + if (alternateTypes.Count == 0) + { + programAttribtues.Add("IsMovie=@IsMovie"); + } + else + { + programAttribtues.Add("(IsMovie is null OR IsMovie=@IsMovie)"); + } + + cmd.Parameters.Add(cmd, "@IsMovie", DbType.Boolean).Value = true; + } + if (query.IsSports ?? false) + { + programAttribtues.Add("IsSports=@IsSports"); + cmd.Parameters.Add(cmd, "@IsSports", DbType.Boolean).Value = true; + } + if (query.IsNews ?? false) + { + programAttribtues.Add("IsNews=@IsNews"); + cmd.Parameters.Add(cmd, "@IsNews", DbType.Boolean).Value = true; + } + if (query.IsSeries ?? false) + { + programAttribtues.Add("IsSeries=@IsSeries"); + cmd.Parameters.Add(cmd, "@IsSeries", DbType.Boolean).Value = true; + } + if (query.IsKids ?? false) + { + programAttribtues.Add("IsKids=@IsKids"); + cmd.Parameters.Add(cmd, "@IsKids", DbType.Boolean).Value = true; + } + if (programAttribtues.Count > 0) + { + whereClauses.Add("("+string.Join(" OR ", programAttribtues.ToArray())+")"); + } } + if (query.IsFolder.HasValue) { whereClauses.Add("IsFolder=@IsFolder"); diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index 48f48cdcc..5857e9dbc 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -818,7 +818,7 @@ namespace MediaBrowser.Server.Implementations.Session { try { - await _mediaSourceManager.CloseLiveStream(info.LiveStreamId, CancellationToken.None).ConfigureAwait(false); + await _mediaSourceManager.CloseLiveStream(info.LiveStreamId).ConfigureAwait(false); } catch (Exception ex) { diff --git a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs index a2b5851ac..cb666b6ac 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs @@ -150,7 +150,7 @@ namespace MediaBrowser.Server.Implementations.Sync } } - public Task CloseMediaSource(string liveStreamId, CancellationToken cancellationToken) + public Task CloseMediaSource(string liveStreamId) { throw new NotImplementedException(); } diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 2f8b34879..4850b6fe0 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -289,7 +289,7 @@ namespace MediaBrowser.WebDashboard.Api return list; } - private List> GetDeployIgnoreFilenames() + private List> GetDeployIgnoreFilenames() { var list = new List>(); @@ -313,8 +313,11 @@ namespace MediaBrowser.WebDashboard.Api public async Task Get(GetDashboardPackage request) { - var path = Path.Combine(_serverConfigurationManager.ApplicationPaths.ProgramDataPath, - "webclient-dump"); + var mode = request.Mode; + + var path = string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase) ? + Path.Combine(_serverConfigurationManager.ApplicationPaths.ProgramDataPath, "webclient-dump") + : "C:\\dev\\emby-web-mobile\\src"; try { @@ -333,8 +336,6 @@ namespace MediaBrowser.WebDashboard.Api var appVersion = _appHost.ApplicationVersion.ToString(); - var mode = request.Mode; - // Try to trim the output size a bit var bowerPath = Path.Combine(path, "bower_components"); @@ -372,11 +373,6 @@ namespace MediaBrowser.WebDashboard.Api // Delete things that are unneeded in an attempt to keep the output as trim as possible _fileSystem.DeleteDirectory(Path.Combine(path, "css", "images", "tour"), true); } - else - { - MinifyCssDirectory(path); - MinifyJsDirectory(path); - } await DumpHtml(creator.DashboardUIPath, path, mode, culture, appVersion); @@ -444,78 +440,6 @@ namespace MediaBrowser.WebDashboard.Api } } - private void MinifyCssDirectory(string path) - { - foreach (var file in Directory.GetFiles(path, "*.css", SearchOption.AllDirectories)) - { - if (file.IndexOf(".min.", StringComparison.OrdinalIgnoreCase) != -1) - { - continue; - } - if (file.IndexOf("bower_", StringComparison.OrdinalIgnoreCase) != -1) - { - continue; - } - - try - { - var text = _fileSystem.ReadAllText(file, Encoding.UTF8); - - var result = new KristensenCssMinifier().Minify(text, false, Encoding.UTF8); - - if (result.Errors.Count > 0) - { - Logger.Error("Error minifying css: " + result.Errors[0].Message); - } - else - { - text = result.MinifiedContent; - _fileSystem.WriteAllText(file, text, Encoding.UTF8); - } - } - catch (Exception ex) - { - Logger.ErrorException("Error minifying css", ex); - } - } - } - - private void MinifyJsDirectory(string path) - { - foreach (var file in Directory.GetFiles(path, "*.js", SearchOption.AllDirectories)) - { - if (file.IndexOf(".min.", StringComparison.OrdinalIgnoreCase) != -1) - { - continue; - } - if (file.IndexOf("bower_", StringComparison.OrdinalIgnoreCase) != -1) - { - continue; - } - - try - { - var text = _fileSystem.ReadAllText(file, Encoding.UTF8); - - var result = new CrockfordJsMinifier().Minify(text, false, Encoding.UTF8); - - if (result.Errors.Count > 0) - { - Logger.Error("Error minifying javascript: " + result.Errors[0].Message); - } - else - { - text = result.MinifiedContent; - _fileSystem.WriteAllText(file, text, Encoding.UTF8); - } - } - catch (Exception ex) - { - Logger.ErrorException("Error minifying css", ex); - } - } - } - private async Task DumpHtml(string source, string destination, string mode, string culture, string appVersion) { foreach (var file in Directory.GetFiles(source, "*", SearchOption.TopDirectoryOnly)) @@ -528,7 +452,7 @@ namespace MediaBrowser.WebDashboard.Api private async Task DumpFile(string resourceVirtualPath, string destinationFilePath, string mode, string culture, string appVersion) { - using (var stream = await GetPackageCreator().GetResource(resourceVirtualPath, mode, culture, appVersion, true).ConfigureAwait(false)) + using (var stream = await GetPackageCreator().GetResource(resourceVirtualPath, mode, culture, appVersion, false).ConfigureAwait(false)) { using (var fs = _fileSystem.GetFileStream(destinationFilePath, FileMode.Create, FileAccess.Write, FileShare.Read)) { -- cgit v1.2.3 From 6a7fabc3bd4ab8496a90bd04e9decf48bf16edc2 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 30 Sep 2016 02:50:06 -0400 Subject: add new guide settings --- MediaBrowser.Api/LiveTv/LiveTvService.cs | 52 +++++++++- MediaBrowser.Api/Playback/BaseStreamingService.cs | 1 + .../Entities/InternalItemsQuery.cs | 3 + MediaBrowser.Controller/LiveTv/LiveStream.cs | 2 + MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 2 + MediaBrowser.Model/Dlna/StreamInfo.cs | 17 +++- MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs | 12 ++- MediaBrowser.Model/LiveTv/LiveTvOptions.cs | 1 - .../LiveTv/EmbyTV/EmbyTV.cs | 38 ++++--- .../LiveTv/EmbyTV/EncodedRecorder.cs | 25 ++++- .../LiveTv/LiveTvManager.cs | 109 +++------------------ .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 70 ++++++++++--- .../TunerHosts/HdHomerun/HdHomerunLiveStream.cs | 9 +- .../Persistence/SqliteItemRepository.cs | 81 ++++++++------- 14 files changed, 256 insertions(+), 166 deletions(-) (limited to 'MediaBrowser.Controller/Entities') diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index 9c1105082..5b821df0b 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -104,6 +104,26 @@ namespace MediaBrowser.Api.LiveTv [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] public bool? EnableUserData { get; set; } + public string SortBy { get; set; } + + public SortOrder? SortOrder { get; set; } + + /// + /// Gets the order by. + /// + /// IEnumerable{ItemSortBy}. + public string[] GetOrderBy() + { + var val = SortBy; + + if (string.IsNullOrEmpty(val)) + { + return new string[] { }; + } + + return val.Split(','); + } + public GetChannels() { AddCurrentProgram = true; @@ -650,6 +670,8 @@ namespace MediaBrowser.Api.LiveTv { public string Id { get; set; } public string Container { get; set; } + public long T { get; set; } + public long S { get; set; } } public class LiveTvService : BaseApiService @@ -681,9 +703,35 @@ namespace MediaBrowser.Api.LiveTv outputHeaders["Content-Type"] = MimeTypes.GetMimeType(filePath); + long startPosition = 0; + + if (request.T > 0) + { + var now = DateTime.UtcNow; + + var totalTicks = now.Ticks - request.S; + + if (totalTicks > 0) + { + double requestedOffset = request.T; + requestedOffset = Math.Max(0, requestedOffset - TimeSpan.FromSeconds(10).Ticks); + + var pct = requestedOffset / totalTicks; + + Logger.Info("Live stream offset pct {0}", pct); + + var bytes = new FileInfo(filePath).Length; + Logger.Info("Live stream total bytes {0}", bytes); + startPosition = Convert.ToInt64(pct * bytes); + } + } + + Logger.Info("Live stream starting byte position {0}", startPosition); + var streamSource = new ProgressiveFileCopier(_fileSystem, filePath, outputHeaders, null, Logger, CancellationToken.None) { - AllowEndOfFile = false + AllowEndOfFile = false, + StartPosition = startPosition }; return ResultFactory.GetAsyncStreamWriter(streamSource); @@ -848,6 +896,8 @@ namespace MediaBrowser.Api.LiveTv IsNews = request.IsNews, IsKids = request.IsKids, IsSports = request.IsSports, + SortBy = request.GetOrderBy(), + SortOrder = request.SortOrder ?? SortOrder.Ascending, AddCurrentProgram = request.AddCurrentProgram }, CancellationToken.None).ConfigureAwait(false); diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 8e57650b4..fb4bd9244 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -2602,6 +2602,7 @@ namespace MediaBrowser.Api.Playback inputModifier += " " + GetFastSeekCommandLineParameter(state.Request); inputModifier = inputModifier.Trim(); + //inputModifier += " -fflags +genpts+ignidx+igndts"; if (state.VideoRequest != null && genPts) { inputModifier += " -fflags +genpts"; diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index d917b7d6d..5e70cd587 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -151,6 +151,8 @@ namespace MediaBrowser.Controller.Entities public Dictionary ExcludeProviderIds { get; set; } public bool EnableGroupByMetadataKey { get; set; } + public List> OrderBy { get; set; } + public InternalItemsQuery() { GroupByPresentationUniqueKey = true; @@ -193,6 +195,7 @@ namespace MediaBrowser.Controller.Entities TrailerTypes = new TrailerType[] { }; AirDays = new DayOfWeek[] { }; SeriesStatuses = new SeriesStatus[] { }; + OrderBy = new List>(); } public InternalItemsQuery(User user) diff --git a/MediaBrowser.Controller/LiveTv/LiveStream.cs b/MediaBrowser.Controller/LiveTv/LiveStream.cs index 1bb198632..7d44fbd90 100644 --- a/MediaBrowser.Controller/LiveTv/LiveStream.cs +++ b/MediaBrowser.Controller/LiveTv/LiveStream.cs @@ -13,11 +13,13 @@ namespace MediaBrowser.Controller.LiveTv public int ConsumerCount { get; set; } public ITunerHost TunerHost { get; set; } public string OriginalStreamId { get; set; } + public bool EnableStreamSharing { get; set; } public LiveStream(MediaSourceInfo mediaSource) { OriginalMediaSource = mediaSource; OpenedMediaSource = mediaSource; + EnableStreamSharing = true; } public async Task Open(CancellationToken cancellationToken) diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index d3131eb5a..44103e720 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -235,6 +235,8 @@ namespace MediaBrowser.MediaEncoding.Encoder throw new ResourceNotFoundException("ffprobe not found"); } + path = newPaths.Item1; + if (!ValidateVersion(path)) { throw new ResourceNotFoundException("ffmpeg version 3.0 or greater is required."); diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index ac024f00b..f7dc893c8 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -215,13 +215,26 @@ namespace MediaBrowser.Model.Dlna list.Add(new NameValuePair("MaxWidth", item.MaxWidth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxWidth.Value) : string.Empty)); list.Add(new NameValuePair("MaxHeight", item.MaxHeight.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxHeight.Value) : string.Empty)); - if (StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls")) + var forceStartPosition = false; + long startPositionTicks = item.StartPositionTicks; + //if (item.MediaSource.DateLiveStreamOpened.HasValue && startPositionTicks == 0) + //{ + // var elapsed = DateTime.UtcNow - item.MediaSource.DateLiveStreamOpened.Value; + // elapsed -= TimeSpan.FromSeconds(20); + // if (elapsed.TotalSeconds >= 0) + // { + // startPositionTicks = elapsed.Ticks + startPositionTicks; + // forceStartPosition = true; + // } + //} + + if (StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls") && !forceStartPosition) { list.Add(new NameValuePair("StartTimeTicks", string.Empty)); } else { - list.Add(new NameValuePair("StartTimeTicks", StringHelper.ToStringCultureInvariant(item.StartPositionTicks))); + list.Add(new NameValuePair("StartTimeTicks", StringHelper.ToStringCultureInvariant(startPositionTicks))); } list.Add(new NameValuePair("Level", item.VideoLevel.HasValue ? StringHelper.ToStringCultureInvariant(item.VideoLevel.Value) : string.Empty)); diff --git a/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs b/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs index f76368a7b..4505b80a0 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs @@ -1,4 +1,5 @@ - +using MediaBrowser.Model.Entities; + namespace MediaBrowser.Model.LiveTv { /// @@ -85,9 +86,18 @@ namespace MediaBrowser.Model.LiveTv public bool? IsSports { get; set; } public bool? IsSeries { get; set; } + public string[] SortBy { get; set; } + + /// + /// The sort order to return results with + /// + /// The sort order. + public SortOrder? SortOrder { get; set; } + public LiveTvChannelQuery() { EnableUserData = true; + SortBy = new string[] { }; } } } diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs index ee7dd8b98..a6240afe6 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs @@ -60,7 +60,6 @@ namespace MediaBrowser.Model.LiveTv public TunerHostInfo() { IsEnabled = true; - AllowHWTranscoding = true; } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index ef19dcbc9..3db764ae1 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -483,7 +483,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV if (existingTimer != null) { - if (existingTimer.Status == RecordingStatus.Cancelled) + if (existingTimer.Status == RecordingStatus.Cancelled || + existingTimer.Status == RecordingStatus.Completed) { existingTimer.Status = RecordingStatus.New; _timerProvider.Update(existingTimer); @@ -832,12 +833,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV return result.Item2; } - private MediaSourceInfo CloneMediaSource(MediaSourceInfo mediaSource, int consumerId) + private MediaSourceInfo CloneMediaSource(MediaSourceInfo mediaSource, int consumerId, bool enableStreamSharing) { var json = _jsonSerializer.SerializeToString(mediaSource); mediaSource = _jsonSerializer.DeserializeFromString(json); - mediaSource.Id = consumerId.ToString(CultureInfo.InvariantCulture) + "_" + mediaSource.Id; + mediaSource.Id = Guid.NewGuid().ToString("N") + "_" + mediaSource.Id; + + if (mediaSource.DateLiveStreamOpened.HasValue && enableStreamSharing) + { + var ticks = (DateTime.UtcNow - mediaSource.DateLiveStreamOpened.Value).Ticks - TimeSpan.FromSeconds(10).Ticks; + ticks = Math.Max(0, ticks); + mediaSource.Path += "?t=" + ticks.ToString(CultureInfo.InvariantCulture) + "&s=" + mediaSource.DateLiveStreamOpened.Value.Ticks.ToString(CultureInfo.InvariantCulture); + } return mediaSource; } @@ -850,14 +858,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV var result = _liveStreams.Values.FirstOrDefault(i => string.Equals(i.OriginalStreamId, streamId, StringComparison.OrdinalIgnoreCase)); - if (result != null) + if (result != null && result.EnableStreamSharing) { - //result.ConsumerCount++; + result.ConsumerCount++; - //_logger.Info("Live stream {0} consumer count is now {1}", streamId, result.ConsumerCount); + _logger.Info("Live stream {0} consumer count is now {1}", streamId, result.ConsumerCount); - //_liveStreamsSemaphore.Release(); - //return new Tuple(result, CloneMediaSource(result.OpenedMediaSource, result.ConsumerCount - 1), result.TunerHost); + var openedMediaSource = CloneMediaSource(result.OpenedMediaSource, result.ConsumerCount - 1, result.EnableStreamSharing); + _liveStreamsSemaphore.Release(); + return new Tuple(result, openedMediaSource, result.TunerHost); } try @@ -868,16 +877,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { result = await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false); - _liveStreams[result.OpenedMediaSource.Id] = result; + var openedMediaSource = CloneMediaSource(result.OpenedMediaSource, 0, result.EnableStreamSharing); + + _liveStreams[openedMediaSource.Id] = result; result.ConsumerCount++; result.TunerHost = hostInstance; result.OriginalStreamId = streamId; _logger.Info("Returning mediasource streamId {0}, mediaSource.Id {1}, mediaSource.LiveStreamId {2}", - streamId, result.OpenedMediaSource.Id, result.OpenedMediaSource.LiveStreamId); + streamId, openedMediaSource.Id, openedMediaSource.LiveStreamId); - return new Tuple(result, CloneMediaSource(result.OpenedMediaSource, 0), hostInstance); + return new Tuple(result, openedMediaSource, hostInstance); } catch (FileNotFoundException) { @@ -925,7 +936,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV public async Task CloseLiveStream(string id, CancellationToken cancellationToken) { // Ignore the consumer id - id = id.Substring(id.IndexOf('_') + 1); + //id = id.Substring(id.IndexOf('_') + 1); await _liveStreamsSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); @@ -1143,8 +1154,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV try { - var allMediaSources = - await GetChannelStreamMediaSources(timer.ChannelId, CancellationToken.None).ConfigureAwait(false); + var allMediaSources = await GetChannelStreamMediaSources(timer.ChannelId, CancellationToken.None).ConfigureAwait(false); var liveStreamInfo = await GetChannelStreamInternal(timer.ChannelId, allMediaSources[0].Id, CancellationToken.None) .ConfigureAwait(false); diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index ce5f14f4b..65e653551 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -141,7 +141,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { var maxBitrate = 25000000; videoArgs = string.Format( - "-codec:v:0 libx264 -force_key_frames \"expr:gte(t,n_forced*5)\" {0} -pix_fmt yuv420p -preset superfast -crf 23 -b:v {1} -maxrate {1} -bufsize ({1}*2) -vsync -1 -profile:v high -level 41", + "-codec:v:0 libx264 -force_key_frames \"expr:gte(t,n_forced*5)\" {0} -pix_fmt yuv420p -preset superfast -crf 23 -b:v {1} -maxrate {1} -bufsize ({1}*2) -vsync -1 -profile:v high -level 41 -tune zerolatency", GetOutputSizeParam(), maxBitrate.ToString(CultureInfo.InvariantCulture)); } @@ -151,16 +151,33 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } var durationParam = " -t " + _mediaEncoder.GetTimeParameter(duration.Ticks); - var commandLineArgs = "-fflags +genpts -async 1 -vsync -1 -i \"{0}\"{4} -sn {2} -map_metadata -1 -threads 0 {3} -y \"{1}\""; + var inputModifiers = "-fflags +genpts -async 1 -vsync -1"; + var commandLineArgs = "-i \"{0}\"{4} -sn {2} -map_metadata -1 -threads 0 {3} -y \"{1}\""; + + long startTimeTicks = 0; + //if (mediaSource.DateLiveStreamOpened.HasValue) + //{ + // var elapsed = DateTime.UtcNow - mediaSource.DateLiveStreamOpened.Value; + // elapsed -= TimeSpan.FromSeconds(10); + // if (elapsed.TotalSeconds >= 0) + // { + // startTimeTicks = elapsed.Ticks + startTimeTicks; + // } + //} if (mediaSource.ReadAtNativeFramerate) { - commandLineArgs = "-re " + commandLineArgs; + inputModifiers += " -re"; + } + + if (startTimeTicks > 0) + { + inputModifiers = "-ss " + _mediaEncoder.GetTimeParameter(startTimeTicks) + " " + inputModifiers; } commandLineArgs = string.Format(commandLineArgs, inputTempFile, targetFile, videoArgs, GetAudioArgs(mediaSource), durationParam); - return commandLineArgs; + return inputModifiers + " " + commandLineArgs; } private string GetAudioArgs(MediaSourceInfo mediaSource) diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 7c61d2396..f736307f0 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -148,7 +148,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv var topFolder = await GetInternalLiveTvFolder(cancellationToken).ConfigureAwait(false); - var channels = _libraryManager.GetItemList(new InternalItemsQuery + var internalQuery = new InternalItemsQuery(user) { IsMovie = query.IsMovie, IsNews = query.IsNews, @@ -156,109 +156,32 @@ namespace MediaBrowser.Server.Implementations.LiveTv IsSports = query.IsSports, IsSeries = query.IsSeries, IncludeItemTypes = new[] { typeof(LiveTvChannel).Name }, - SortBy = new[] { ItemSortBy.SortName }, - TopParentIds = new[] { topFolder.Id.ToString("N") } + SortOrder = query.SortOrder ?? SortOrder.Ascending, + TopParentIds = new[] { topFolder.Id.ToString("N") }, + IsFavorite = query.IsFavorite, + IsLiked = query.IsLiked, + StartIndex = query.StartIndex, + Limit = query.Limit + }; - }).Cast(); + internalQuery.OrderBy.AddRange(query.SortBy.Select(i => new Tuple(i, query.SortOrder ?? SortOrder.Ascending))); - if (user != null) + if (query.EnableFavoriteSorting) { - // Avoid implicitly captured closure - var currentUser = user; - - channels = channels - .Where(i => i.IsVisible(currentUser)) - .OrderBy(i => - { - double number = 0; - - if (!string.IsNullOrEmpty(i.Number)) - { - double.TryParse(i.Number, out number); - } - - return number; - - }); - - if (query.IsFavorite.HasValue) - { - var val = query.IsFavorite.Value; - - channels = channels - .Where(i => _userDataManager.GetUserData(user, i).IsFavorite == val); - } - - if (query.IsLiked.HasValue) - { - var val = query.IsLiked.Value; - - channels = channels - .Where(i => - { - var likes = _userDataManager.GetUserData(user, i).Likes; - - return likes.HasValue && likes.Value == val; - }); - } - - if (query.IsDisliked.HasValue) - { - var val = query.IsDisliked.Value; - - channels = channels - .Where(i => - { - var likes = _userDataManager.GetUserData(user, i).Likes; - - return likes.HasValue && likes.Value != val; - }); - } + internalQuery.OrderBy.Insert(0, new Tuple(ItemSortBy.IsFavoriteOrLiked, SortOrder.Descending)); } - var enableFavoriteSorting = query.EnableFavoriteSorting; - - channels = channels.OrderBy(i => - { - if (enableFavoriteSorting) - { - var userData = _userDataManager.GetUserData(user, i); - - if (userData.IsFavorite) - { - return 0; - } - if (userData.Likes.HasValue) - { - if (!userData.Likes.Value) - { - return 3; - } - - return 1; - } - } - - return 2; - }); - - var allChannels = channels.ToList(); - IEnumerable allEnumerable = allChannels; - - if (query.StartIndex.HasValue) + if (!internalQuery.OrderBy.Any(i => string.Equals(i.Item1, ItemSortBy.SortName, StringComparison.OrdinalIgnoreCase))) { - allEnumerable = allEnumerable.Skip(query.StartIndex.Value); + internalQuery.OrderBy.Add(new Tuple(ItemSortBy.SortName, SortOrder.Ascending)); } - if (query.Limit.HasValue) - { - allEnumerable = allEnumerable.Take(query.Limit.Value); - } + var channelResult = _libraryManager.GetItemsResult(internalQuery); var result = new QueryResult { - Items = allEnumerable.ToArray(), - TotalRecordCount = allChannels.Count + Items = channelResult.Items.Cast().ToArray(), + TotalRecordCount = channelResult.TotalRecordCount }; return result; diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 68450105e..14ae45228 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -104,8 +104,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun }); } + private Dictionary _modelCache = new Dictionary(); private async Task GetModelInfo(TunerHostInfo info, CancellationToken cancellationToken) { + lock (_modelCache) + { + DiscoverResponse response; + if (_modelCache.TryGetValue(info.Url, out response)) + { + return response.ModelNumber; + } + } + try { using (var stream = await _httpClient.Get(new HttpRequestOptions() @@ -119,6 +129,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun { var response = JsonSerializer.DeserializeFromStream(stream); + lock (_modelCache) + { + _modelCache[info.Id] = response; + } + return response.ModelNumber; } } @@ -126,8 +141,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun { if (ex.StatusCode.HasValue && ex.StatusCode.Value == System.Net.HttpStatusCode.NotFound) { + var defaultValue = "HDHR"; // HDHR4 doesn't have this api - return "HDHR"; + lock (_modelCache) + { + _modelCache[info.Id] = new DiscoverResponse + { + ModelNumber = defaultValue + }; + } + return defaultValue; } throw; @@ -427,18 +450,21 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun try { - string model = await GetModelInfo(info, cancellationToken).ConfigureAwait(false); - model = model ?? string.Empty; - - if (info.AllowHWTranscoding && (model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1)) + if (info.AllowHWTranscoding) { - list.Add(await GetMediaSource(info, hdhrId, "heavy").ConfigureAwait(false)); + string model = await GetModelInfo(info, cancellationToken).ConfigureAwait(false); + model = model ?? string.Empty; + + if ((model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1)) + { + list.Add(await GetMediaSource(info, hdhrId, "heavy").ConfigureAwait(false)); - list.Add(await GetMediaSource(info, hdhrId, "internet540").ConfigureAwait(false)); - list.Add(await GetMediaSource(info, hdhrId, "internet480").ConfigureAwait(false)); - list.Add(await GetMediaSource(info, hdhrId, "internet360").ConfigureAwait(false)); - list.Add(await GetMediaSource(info, hdhrId, "internet240").ConfigureAwait(false)); - list.Add(await GetMediaSource(info, hdhrId, "mobile").ConfigureAwait(false)); + list.Add(await GetMediaSource(info, hdhrId, "internet540").ConfigureAwait(false)); + list.Add(await GetMediaSource(info, hdhrId, "internet480").ConfigureAwait(false)); + list.Add(await GetMediaSource(info, hdhrId, "internet360").ConfigureAwait(false)); + list.Add(await GetMediaSource(info, hdhrId, "internet240").ConfigureAwait(false)); + list.Add(await GetMediaSource(info, hdhrId, "mobile").ConfigureAwait(false)); + } } } catch @@ -474,6 +500,23 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun var mediaSource = await GetMediaSource(info, hdhrId, profile).ConfigureAwait(false); var liveStream = new HdHomerunLiveStream(mediaSource, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost); + if (info.AllowHWTranscoding) + { + var model = await GetModelInfo(info, cancellationToken).ConfigureAwait(false); + + if ((model ?? string.Empty).IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1) + { + liveStream.EnableStreamSharing = !info.AllowHWTranscoding; + } + else + { + liveStream.EnableStreamSharing = true; + } + } + else + { + liveStream.EnableStreamSharing = true; + } return liveStream; } @@ -484,6 +527,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun return; } + lock (_modelCache) + { + _modelCache.Clear(); + } + try { // Test it by pulling down the lineup diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs index d6574db22..57722881d 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs @@ -52,11 +52,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun StartStreamingToTempFile(output, tempFile, url, taskCompletionSource, _liveStreamCancellationTokenSource.Token); - await taskCompletionSource.Task.ConfigureAwait(false); + //OpenedMediaSource.Protocol = MediaProtocol.File; + //OpenedMediaSource.Path = tempFile; + //OpenedMediaSource.ReadAtNativeFramerate = true; OpenedMediaSource.Path = _appHost.GetLocalApiUrl("localhost") + "/LiveTv/LiveStreamFiles/" + Path.GetFileNameWithoutExtension(tempFile) + "/stream.ts"; - OpenedMediaSource.Protocol = MediaProtocol.Http; + + await taskCompletionSource.Task.ConfigureAwait(false); + + //await Task.Delay(5000).ConfigureAwait(false); } public override Task Close() diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index 097118418..672cd1bc2 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -1730,28 +1730,28 @@ namespace MediaBrowser.Server.Implementations.Persistence return true; } - if (query.SortBy != null && query.SortBy.Length > 0) + var sortingFields = query.SortBy.ToList(); + sortingFields.AddRange(query.OrderBy.Select(i => i.Item1)); + + if (sortingFields.Contains(ItemSortBy.IsFavoriteOrLiked, StringComparer.OrdinalIgnoreCase)) { - if (query.SortBy.Contains(ItemSortBy.IsFavoriteOrLiked, StringComparer.OrdinalIgnoreCase)) - { - return true; - } - if (query.SortBy.Contains(ItemSortBy.IsPlayed, StringComparer.OrdinalIgnoreCase)) - { - return true; - } - if (query.SortBy.Contains(ItemSortBy.IsUnplayed, StringComparer.OrdinalIgnoreCase)) - { - return true; - } - if (query.SortBy.Contains(ItemSortBy.PlayCount, StringComparer.OrdinalIgnoreCase)) - { - return true; - } - if (query.SortBy.Contains(ItemSortBy.DatePlayed, StringComparer.OrdinalIgnoreCase)) - { - return true; - } + return true; + } + if (sortingFields.Contains(ItemSortBy.IsPlayed, StringComparer.OrdinalIgnoreCase)) + { + return true; + } + if (sortingFields.Contains(ItemSortBy.IsUnplayed, StringComparer.OrdinalIgnoreCase)) + { + return true; + } + if (sortingFields.Contains(ItemSortBy.PlayCount, StringComparer.OrdinalIgnoreCase)) + { + return true; + } + if (sortingFields.Contains(ItemSortBy.DatePlayed, StringComparer.OrdinalIgnoreCase)) + { + return true; } if (query.IsFavoriteOrLiked.HasValue) @@ -2151,34 +2151,41 @@ namespace MediaBrowser.Server.Implementations.Persistence private string GetOrderByText(InternalItemsQuery query) { + var orderBy = query.OrderBy.ToList(); + var enableOrderInversion = true; + + if (orderBy.Count == 0) + { + orderBy.AddRange(query.SortBy.Select(i => new Tuple(i, query.SortOrder))); + } + else + { + enableOrderInversion = false; + } + if (query.SimilarTo != null) { - if (query.SortBy == null || query.SortBy.Length == 0) + if (orderBy.Count == 0) { - if (query.User != null) - { - query.SortBy = new[] { "SimilarityScore", ItemSortBy.Random }; - } - else - { - query.SortBy = new[] { "SimilarityScore", ItemSortBy.Random }; - } + orderBy.Add(new Tuple("SimilarityScore", SortOrder.Descending)); + orderBy.Add(new Tuple(ItemSortBy.Random, SortOrder.Ascending)); query.SortOrder = SortOrder.Descending; + enableOrderInversion = false; } } - if (query.SortBy == null || query.SortBy.Length == 0) + query.OrderBy = orderBy; + + if (orderBy.Count == 0) { return string.Empty; } - var isAscending = query.SortOrder != SortOrder.Descending; - - return " ORDER BY " + string.Join(",", query.SortBy.Select(i => + return " ORDER BY " + string.Join(",", orderBy.Select(i => { - var columnMap = MapOrderByField(i, query); - var columnAscending = isAscending; - if (columnMap.Item2) + var columnMap = MapOrderByField(i.Item1, query); + var columnAscending = i.Item2 == SortOrder.Ascending; + if (columnMap.Item2 && enableOrderInversion) { columnAscending = !columnAscending; } -- cgit v1.2.3 From 3f77a9a8a220e94bd9111e948a495343b3d3b1a2 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 1 Oct 2016 03:06:00 -0400 Subject: update series timer editor --- MediaBrowser.Api/ItemUpdateService.cs | 6 +- MediaBrowser.Api/LiveTv/LiveTvService.cs | 3 + MediaBrowser.Controller/Entities/BaseItem.cs | 5 +- .../Entities/InternalItemsQuery.cs | 1 + MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs | 2 + MediaBrowser.Dlna/Main/DlnaEntryPoint.cs | 7 +- MediaBrowser.Model/LiveTv/ProgramQuery.cs | 1 + .../LiveTv/LiveTvManager.cs | 90 +++++++++++++++++++++- .../Persistence/SqliteItemRepository.cs | 21 ++++- 9 files changed, 120 insertions(+), 16 deletions(-) (limited to 'MediaBrowser.Controller/Entities') diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs index cda7ce0c6..64b6b18d6 100644 --- a/MediaBrowser.Api/ItemUpdateService.cs +++ b/MediaBrowser.Api/ItemUpdateService.cs @@ -243,11 +243,7 @@ namespace MediaBrowser.Api hasBudget.Revenue = request.Revenue; } - var hasOriginalTitle = item as IHasOriginalTitle; - if (hasOriginalTitle != null) - { - hasOriginalTitle.OriginalTitle = hasOriginalTitle.OriginalTitle; - } + item.OriginalTitle = string.IsNullOrWhiteSpace(request.OriginalTitle) ? null : request.OriginalTitle; var hasCriticRating = item as IHasCriticRating; if (hasCriticRating != null) diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index 5b821df0b..ecc17374f 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -386,6 +386,8 @@ namespace MediaBrowser.Api.LiveTv [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] public bool? EnableUserData { get; set; } + public string SeriesTimerId { get; set; } + /// /// Fields to return within the items, in addition to basic information /// @@ -985,6 +987,7 @@ namespace MediaBrowser.Api.LiveTv query.IsSeries = request.IsSeries; query.IsKids = request.IsKids; query.IsSports = request.IsSports; + query.SeriesTimerId = request.SeriesTimerId; query.Genres = (request.Genres ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); var result = await _liveTvManager.GetPrograms(query, GetDtoOptions(request), CancellationToken.None).ConfigureAwait(false); diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 30e0f3ee7..492058f98 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -256,7 +256,10 @@ namespace MediaBrowser.Controller.Entities } [IgnoreDataMember] - public string ExternalSeriesId + public string ExternalSeriesId { get; set; } + + [IgnoreDataMember] + public string ExternalSeriesIdLegacy { get { return this.GetProviderId("ProviderExternalSeriesId"); } set diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index 5e70cd587..60af2c56a 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -139,6 +139,7 @@ namespace MediaBrowser.Controller.Entities public DayOfWeek[] AirDays { get; set; } public SeriesStatus[] SeriesStatuses { get; set; } public string AlbumArtistStartsWithOrGreater { get; set; } + public string ExternalSeriesId { get; set; } public string[] AlbumNames { get; set; } public string[] ArtistNames { get; set; } diff --git a/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs b/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs index a828e22e3..5c73ed833 100644 --- a/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs +++ b/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs @@ -27,6 +27,8 @@ namespace MediaBrowser.Controller.LiveTv /// public string Name { get; set; } + public string ServiceName { get; set; } + /// /// Description of the recording. /// diff --git a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs index 5d2231da8..51e115cc2 100644 --- a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs +++ b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs @@ -339,12 +339,9 @@ namespace MediaBrowser.Dlna.Main if (_Publisher != null) { var devices = _Publisher.Devices.ToList(); + var tasks = devices.Select(i => _Publisher.RemoveDevice(i)).ToArray(); - foreach (var device in devices) - { - var task = _Publisher.RemoveDevice(device); - Task.WaitAll(task); - } + Task.WaitAll(tasks); //foreach (var device in devices) //{ // try diff --git a/MediaBrowser.Model/LiveTv/ProgramQuery.cs b/MediaBrowser.Model/LiveTv/ProgramQuery.cs index 7886342e7..ad57d1473 100644 --- a/MediaBrowser.Model/LiveTv/ProgramQuery.cs +++ b/MediaBrowser.Model/LiveTv/ProgramQuery.cs @@ -41,6 +41,7 @@ namespace MediaBrowser.Model.LiveTv /// /// The user identifier. public string UserId { get; set; } + public string SeriesTimerId { get; set; } /// /// The earliest date for which a program starts to return diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index f736307f0..025f5a162 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -565,6 +565,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv }; } + var seriesId = info.SeriesId; + if (string.IsNullOrWhiteSpace(seriesId) && info.IsSeries) + { + seriesId = info.Name.GetMD5().ToString("N"); + } + if (!item.ParentId.Equals(channel.Id)) { forceUpdate = true; @@ -584,7 +590,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv item.EpisodeTitle = info.EpisodeTitle; item.ExternalId = info.Id; - item.ExternalSeriesId = info.SeriesId; + item.ExternalSeriesIdLegacy = seriesId; + + if (!string.IsNullOrWhiteSpace(seriesId) && !string.Equals(item.ExternalSeriesId, seriesId, StringComparison.Ordinal)) + { + forceUpdate = true; + } + item.ExternalSeriesId = seriesId; + item.Genres = info.Genres; item.IsHD = info.IsHD; item.IsKids = info.IsKids; @@ -825,7 +838,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv var dto = _dtoService.GetBaseItemDto(program, new DtoOptions(), user); var list = new List>(); - list.Add(new Tuple(dto, program.ServiceName, program.ExternalId, program.ExternalSeriesId)); + list.Add(new Tuple(dto, program.ServiceName, program.ExternalId, program.ExternalSeriesIdLegacy)); await AddRecordingInfo(list, cancellationToken).ConfigureAwait(false); @@ -866,6 +879,27 @@ namespace MediaBrowser.Server.Implementations.LiveTv TopParentIds = new[] { topFolder.Id.ToString("N") } }; + if (!string.IsNullOrWhiteSpace(query.SeriesTimerId)) + { + var seriesTimers = await GetSeriesTimersInternal(new SeriesTimerQuery {}, cancellationToken).ConfigureAwait(false); + var seriesTimer = seriesTimers.Items.FirstOrDefault(i => string.Equals(_tvDtoService.GetInternalSeriesTimerId(i.ServiceName, i.Id).ToString("N"), query.SeriesTimerId, StringComparison.OrdinalIgnoreCase)); + if (seriesTimer != null) + { + internalQuery.ExternalSeriesId = seriesTimer.SeriesId; + + if (string.IsNullOrWhiteSpace(seriesTimer.SeriesId)) + { + // Better to return nothing than every program in the database + return new QueryResult(); + } + } + else + { + // Better to return nothing than every program in the database + return new QueryResult(); + } + } + if (query.HasAired.HasValue) { if (query.HasAired.Value) @@ -1730,7 +1764,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv dto.ServiceName = serviceName; } - recordingTuples.Add(new Tuple(dto, serviceName, program.ExternalId, program.ExternalSeriesId)); + recordingTuples.Add(new Tuple(dto, serviceName, program.ExternalId, program.ExternalSeriesIdLegacy)); } await AddRecordingInfo(recordingTuples, CancellationToken.None).ConfigureAwait(false); @@ -2005,6 +2039,56 @@ namespace MediaBrowser.Server.Implementations.LiveTv return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase)); } + private async Task> GetSeriesTimersInternal(SeriesTimerQuery query, CancellationToken cancellationToken) + { + var tasks = _services.Select(async i => + { + try + { + var recs = await i.GetSeriesTimersAsync(cancellationToken).ConfigureAwait(false); + return recs.Select(r => + { + r.ServiceName = i.Name; + return new Tuple(r, i); + }); + } + catch (Exception ex) + { + _logger.ErrorException("Error getting recordings", ex); + return new List>(); + } + }); + var results = await Task.WhenAll(tasks).ConfigureAwait(false); + var timers = results.SelectMany(i => i.ToList()); + + if (string.Equals(query.SortBy, "Priority", StringComparison.OrdinalIgnoreCase)) + { + timers = query.SortOrder == SortOrder.Descending ? + timers.OrderBy(i => i.Item1.Priority).ThenByStringDescending(i => i.Item1.Name) : + timers.OrderByDescending(i => i.Item1.Priority).ThenByString(i => i.Item1.Name); + } + else + { + timers = query.SortOrder == SortOrder.Descending ? + timers.OrderByStringDescending(i => i.Item1.Name) : + timers.OrderByString(i => i.Item1.Name); + } + + var returnArray = timers + .Select(i => + { + return i.Item1; + + }) + .ToArray(); + + return new QueryResult + { + Items = returnArray, + TotalRecordCount = returnArray.Length + }; + } + public async Task> GetSeriesTimers(SeriesTimerQuery query, CancellationToken cancellationToken) { var tasks = _services.Select(async i => diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index 672cd1bc2..c843ab596 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -270,6 +270,7 @@ namespace MediaBrowser.Server.Implementations.Persistence _connection.AddColumn(Logger, "TypedBaseItems", "SeasonId", "GUID"); _connection.AddColumn(Logger, "TypedBaseItems", "SeriesId", "GUID"); _connection.AddColumn(Logger, "TypedBaseItems", "SeriesSortName", "Text"); + _connection.AddColumn(Logger, "TypedBaseItems", "ExternalSeriesId", "Text"); _connection.AddColumn(Logger, "UserDataKeys", "Priority", "INT"); _connection.AddColumn(Logger, "ItemValues", "CleanValue", "Text"); @@ -413,7 +414,8 @@ namespace MediaBrowser.Server.Implementations.Persistence "SeriesSortName", "PresentationUniqueKey", "InheritedParentalRatingValue", - "InheritedTags" + "InheritedTags", + "ExternalSeriesId" }; private readonly string[] _mediaStreamSaveColumns = @@ -535,7 +537,8 @@ namespace MediaBrowser.Server.Implementations.Persistence "SeasonName", "SeasonId", "SeriesId", - "SeriesSortName" + "SeriesSortName", + "ExternalSeriesId" }; _saveItemCommand = _connection.CreateCommand(); _saveItemCommand.CommandText = "replace into TypedBaseItems (" + string.Join(",", saveColumns.ToArray()) + ") values ("; @@ -975,6 +978,8 @@ namespace MediaBrowser.Server.Implementations.Persistence _saveItemCommand.GetParameter(index++).Value = null; } + _saveItemCommand.GetParameter(index++).Value = item.ExternalSeriesId; + _saveItemCommand.Transaction = transaction; _saveItemCommand.ExecuteNonQuery(); @@ -1466,6 +1471,12 @@ namespace MediaBrowser.Server.Implementations.Persistence } index++; + if (!reader.IsDBNull(index)) + { + item.ExternalSeriesId = reader.GetString(index); + } + index++; + return item; } @@ -2852,6 +2863,12 @@ namespace MediaBrowser.Server.Implementations.Persistence cmd.Parameters.Add(cmd, "@MinSortName", DbType.String).Value = query.MinSortName; } + if (!string.IsNullOrWhiteSpace(query.ExternalSeriesId)) + { + whereClauses.Add("ExternalSeriesId=@ExternalSeriesId"); + cmd.Parameters.Add(cmd, "@ExternalSeriesId", DbType.String).Value = query.ExternalSeriesId; + } + if (!string.IsNullOrWhiteSpace(query.Name)) { whereClauses.Add("CleanName=@Name"); -- cgit v1.2.3 From 951e2b6de30631e8b30c9290fc9e0cb5eea5ea2e Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 1 Oct 2016 16:29:24 -0400 Subject: allow channel items in collections and playlists --- MediaBrowser.Controller/Entities/Folder.cs | 26 +++++++--------------- .../Movies/GenericMovieDbInfo.cs | 6 +---- .../LiveTv/LiveTvManager.cs | 4 ---- .../TunerHosts/HdHomerun/HdHomerunLiveStream.cs | 3 +++ 4 files changed, 12 insertions(+), 27 deletions(-) (limited to 'MediaBrowser.Controller/Entities') diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index f1d8def4b..d1e089850 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -1149,29 +1149,19 @@ namespace MediaBrowser.Controller.Entities return LinkedChildren .Select(i => { - var requiresPostFilter = true; - - if (!string.IsNullOrWhiteSpace(i.Path)) - { - requiresPostFilter = false; - - if (!locations.Any(l => FileSystem.ContainsSubPath(l, i.Path))) - { - return null; - } - } - var child = GetLinkedChild(i); - if (requiresPostFilter && child != null) + if (child != null) { - if (string.IsNullOrWhiteSpace(child.Path)) + var childLocationType = child.LocationType; + if (childLocationType == LocationType.Remote || childLocationType == LocationType.Virtual) { - Logger.Debug("Found LinkedChild with null path: {0}", child.Name); - return child; + if (!child.IsVisibleStandalone(user)) + { + return null; + } } - - if (!locations.Any(l => FileSystem.ContainsSubPath(l, child.Path))) + else if (childLocationType == LocationType.FileSystem && !locations.Any(l => FileSystem.ContainsSubPath(l, child.Path))) { return null; } diff --git a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs index 54302f39a..41a061467 100644 --- a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs +++ b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs @@ -126,11 +126,7 @@ namespace MediaBrowser.Providers.Movies movie.Name = movieData.GetTitle() ?? movie.Name; - var hasOriginalTitle = movie as IHasOriginalTitle; - if (hasOriginalTitle != null) - { - hasOriginalTitle.OriginalTitle = movieData.GetOriginalTitle(); - } + movie.OriginalTitle = movieData.GetOriginalTitle(); // Bug in Mono: WebUtility.HtmlDecode should return null if the string is null but in Mono it generate an System.ArgumentNullException. movie.Overview = movieData.overview != null ? WebUtility.HtmlDecode(movieData.overview) : null; diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 025f5a162..e34868428 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -566,10 +566,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv } var seriesId = info.SeriesId; - if (string.IsNullOrWhiteSpace(seriesId) && info.IsSeries) - { - seriesId = info.Name.GetMD5().ToString("N"); - } if (!item.ParentId.Equals(channel.Id)) { diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs index 57722881d..2cdd0571b 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs @@ -58,6 +58,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun OpenedMediaSource.Path = _appHost.GetLocalApiUrl("localhost") + "/LiveTv/LiveStreamFiles/" + Path.GetFileNameWithoutExtension(tempFile) + "/stream.ts"; OpenedMediaSource.Protocol = MediaProtocol.Http; + OpenedMediaSource.SupportsDirectPlay = false; + OpenedMediaSource.SupportsDirectStream = true; + OpenedMediaSource.SupportsTranscoding = true; await taskCompletionSource.Task.ConfigureAwait(false); -- cgit v1.2.3 From 911d9f4598e4cafe24a8eb6d40a0d95356d3437c Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 2 Oct 2016 00:31:47 -0400 Subject: move more metadata settings to per library --- MediaBrowser.Api/StartupWizardService.cs | 6 ----- MediaBrowser.Controller/Entities/BaseItem.cs | 6 +++-- .../Entities/CollectionFolder.cs | 2 +- MediaBrowser.Model/Configuration/ChapterOptions.cs | 6 ----- MediaBrowser.Model/Configuration/LibraryOptions.cs | 4 ++++ .../MediaInfo/FFProbeVideoInfo.cs | 4 ++-- .../TV/MissingEpisodeProvider.cs | 2 +- .../TV/TheTVDB/TvdbPrescanTask.cs | 17 ++++++++------ .../Library/LibraryManager.cs | 27 ++++++++++++++-------- .../MediaEncoder/EncodingManager.cs | 26 ++------------------- .../Session/SessionManager.cs | 3 +-- 11 files changed, 42 insertions(+), 61 deletions(-) (limited to 'MediaBrowser.Controller/Entities') diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index 176b497d7..ebb3204a4 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -88,8 +88,6 @@ namespace MediaBrowser.Api var result = new StartupConfiguration { UICulture = _config.Configuration.UICulture, - EnableInternetProviders = _config.Configuration.EnableInternetProviders, - SaveLocalMeta = _config.Configuration.SaveLocalMeta, MetadataCountryCode = _config.Configuration.MetadataCountryCode, PreferredMetadataLanguage = _config.Configuration.PreferredMetadataLanguage }; @@ -123,8 +121,6 @@ namespace MediaBrowser.Api public void Post(UpdateStartupConfiguration request) { _config.Configuration.UICulture = request.UICulture; - _config.Configuration.EnableInternetProviders = request.EnableInternetProviders; - _config.Configuration.SaveLocalMeta = request.SaveLocalMeta; _config.Configuration.MetadataCountryCode = request.MetadataCountryCode; _config.Configuration.PreferredMetadataLanguage = request.PreferredMetadataLanguage; _config.SaveConfiguration(); @@ -215,8 +211,6 @@ namespace MediaBrowser.Api public class StartupConfiguration { public string UICulture { get; set; } - public bool EnableInternetProviders { get; set; } - public bool SaveLocalMeta { get; set; } public string MetadataCountryCode { get; set; } public string PreferredMetadataLanguage { get; set; } public string LiveTvTunerType { get; set; } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 492058f98..be88c535e 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -422,7 +422,7 @@ namespace MediaBrowser.Controller.Entities public virtual bool IsInternetMetadataEnabled() { - return ConfigurationManager.Configuration.EnableInternetProviders; + return LibraryManager.GetLibraryOptions(this).EnableInternetProviders; } public virtual bool CanDelete() @@ -1341,7 +1341,9 @@ namespace MediaBrowser.Controller.Entities return false; } - return ConfigurationManager.Configuration.SaveLocalMeta; + var libraryOptions = LibraryManager.GetLibraryOptions(this); + + return libraryOptions.SaveLocalMetadata; } /// diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 77d7ca7f2..04ba53263 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -111,7 +111,7 @@ namespace MediaBrowser.Controller.Entities { LibraryOptions[path] = options; - options.SchemaVersion = 2; + options.SchemaVersion = 3; XmlSerializer.SerializeToFile(options, GetLibraryOptionsPath(path)); } } diff --git a/MediaBrowser.Model/Configuration/ChapterOptions.cs b/MediaBrowser.Model/Configuration/ChapterOptions.cs index f9ff6b4f9..c7bb6f861 100644 --- a/MediaBrowser.Model/Configuration/ChapterOptions.cs +++ b/MediaBrowser.Model/Configuration/ChapterOptions.cs @@ -2,17 +2,11 @@ { public class ChapterOptions { - public bool EnableMovieChapterImageExtraction { get; set; } - public bool EnableEpisodeChapterImageExtraction { get; set; } - public bool EnableOtherVideoChapterImageExtraction { get; set; } - public bool DownloadMovieChapters { get; set; } public bool DownloadEpisodeChapters { get; set; } public string[] FetcherOrder { get; set; } public string[] DisabledFetchers { get; set; } - - public bool ExtractDuringLibraryScan { get; set; } public ChapterOptions() { diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs index 460ee0918..5f8c77ccd 100644 --- a/MediaBrowser.Model/Configuration/LibraryOptions.cs +++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs @@ -11,11 +11,15 @@ public bool DownloadImagesInAdvance { get; set; } public MediaPathInfo[] PathInfos { get; set; } + public bool SaveLocalMetadata { get; set; } + public bool EnableInternetProviders { get; set; } + public LibraryOptions() { EnablePhotos = true; EnableRealtimeMonitor = true; PathInfos = new MediaPathInfo[] { }; + EnableInternetProviders = true; } } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 0f8cf93fb..66fe7ea81 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -262,8 +262,8 @@ namespace MediaBrowser.Providers.MediaInfo NormalizeChapterNames(chapters); var libraryOptions = _libraryManager.GetLibraryOptions(video); - var extractDuringScan = chapterOptions.ExtractDuringLibraryScan; - if (libraryOptions != null && libraryOptions.SchemaVersion >= 2) + var extractDuringScan = false; + if (libraryOptions != null) { extractDuringScan = libraryOptions.ExtractChapterImagesDuringLibraryScan; } diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index a12402f4f..9c212e8a0 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -118,7 +118,7 @@ namespace MediaBrowser.Providers.TV var hasNewEpisodes = false; - if (_config.Configuration.EnableInternetProviders && addNewItems) + if (addNewItems && !group.Any(i => !i.IsInternetMetadataEnabled())) { var seriesConfig = _config.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, typeof(Series).Name, StringComparison.OrdinalIgnoreCase)); diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs index 215e0640f..d9e1037d8 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs @@ -74,12 +74,6 @@ namespace MediaBrowser.Providers.TV /// Task. public async Task Run(IProgress progress, CancellationToken cancellationToken) { - if (!_config.Configuration.EnableInternetProviders) - { - progress.Report(100); - return; - } - var seriesConfig = _config.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, typeof(Series).Name, StringComparison.OrdinalIgnoreCase)); if (seriesConfig != null && seriesConfig.DisabledMetadataFetchers.Contains(TvdbSeriesProvider.Current.Name, StringComparer.OrdinalIgnoreCase)) @@ -116,7 +110,9 @@ namespace MediaBrowser.Providers.TV IncludeItemTypes = new[] { typeof(Series).Name }, Recursive = true, GroupByPresentationUniqueKey = false - }).Cast(); + + }).Cast() + .ToList(); var seriesIdsInLibrary = seriesList .Where(i => !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tvdb))) @@ -126,6 +122,13 @@ namespace MediaBrowser.Providers.TV var missingSeries = seriesIdsInLibrary.Except(existingDirectories, StringComparer.OrdinalIgnoreCase) .ToList(); + var enableInternetProviders = seriesList.Count == 0 ? false : seriesList[0].IsInternetMetadataEnabled(); + if (!enableInternetProviders) + { + progress.Report(100); + return; + } + // If this is our first time, update all series if (string.IsNullOrEmpty(lastUpdateTime)) { diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index f6fb158ae..93ee91c21 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -1216,12 +1216,7 @@ namespace MediaBrowser.Server.Implementations.Library if (libraryFolder != null) { info.ItemId = libraryFolder.Id.ToString("N"); - } - - var collectionFolder = libraryFolder as CollectionFolder; - if (collectionFolder != null) - { - info.LibraryOptions = collectionFolder.GetLibraryOptions(); + info.LibraryOptions = GetLibraryOptions(libraryFolder); } return info; @@ -1889,11 +1884,23 @@ namespace MediaBrowser.Server.Implementations.Library public LibraryOptions GetLibraryOptions(BaseItem item) { - var collectionFolder = GetCollectionFolders(item) - .OfType() - .FirstOrDefault(); + var collectionFolder = item as CollectionFolder; + if (collectionFolder == null) + { + collectionFolder = GetCollectionFolders(item) + .OfType() + .FirstOrDefault(); + } + + var options = collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions(); + + if (options.SchemaVersion < 3) + { + options.SaveLocalMetadata = ConfigurationManager.Configuration.SaveLocalMeta; + options.EnableInternetProviders = ConfigurationManager.Configuration.EnableInternetProviders; + } - return collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions(); + return options; } public string GetContentType(BaseItem item) diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs index 06c109dfc..21e847c68 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs +++ b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs @@ -61,7 +61,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder } var libraryOptions = _libraryManager.GetLibraryOptions(video); - if (libraryOptions != null && libraryOptions.SchemaVersion >= 2) + if (libraryOptions != null) { if (!libraryOptions.EnableChapterImageExtraction) { @@ -70,29 +70,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder } else { - var options = _chapterManager.GetConfiguration(); - - if (video is Movie) - { - if (!options.EnableMovieChapterImageExtraction) - { - return false; - } - } - else if (video is Episode) - { - if (!options.EnableEpisodeChapterImageExtraction) - { - return false; - } - } - else - { - if (!options.EnableOtherVideoChapterImageExtraction) - { - return false; - } - } + return false; } // Can't extract images if there are no video streams diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index d2bdee9fa..2fcc76588 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -242,8 +242,7 @@ namespace MediaBrowser.Server.Implementations.Session var userLastActivityDate = user.LastActivityDate ?? DateTime.MinValue; user.LastActivityDate = activityDate; - // Don't log in the db anymore frequently than 10 seconds - if ((activityDate - userLastActivityDate).TotalSeconds > 10) + if ((activityDate - userLastActivityDate).TotalSeconds > 60) { try { -- cgit v1.2.3 From d3583c14605dbf31aed7dd921fb2085c1c0b7be1 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 3 Oct 2016 02:28:45 -0400 Subject: update lists --- MediaBrowser.Api/Playback/BaseStreamingService.cs | 6 ++++-- MediaBrowser.Api/Playback/StreamState.cs | 13 +++++++++++++ MediaBrowser.Controller/Entities/InternalItemsQuery.cs | 3 +++ MediaBrowser.Dlna/Didl/DidlBuilder.cs | 6 ++++-- MediaBrowser.Dlna/PlayTo/PlayToController.cs | 3 ++- MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs | 13 +++++++++++++ MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs | 3 ++- MediaBrowser.Model/Dlna/ConditionProcessor.cs | 5 ++++- MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs | 6 ++++-- MediaBrowser.Model/Dlna/DeviceProfile.cs | 5 +++-- MediaBrowser.Model/Dlna/ProfileConditionValue.cs | 3 ++- MediaBrowser.Model/Dlna/StreamBuilder.cs | 10 ++++++---- MediaBrowser.Model/Dlna/StreamInfo.cs | 13 +++++++++++++ MediaBrowser.Providers/Manager/ImageSaver.cs | 14 ++++++++++++++ .../LiveTv/EmbyTV/EmbyTV.cs | 13 +++++++------ .../LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs | 12 +++++++++++- .../Persistence/SqliteItemRepository.cs | 12 ++++++++++++ 17 files changed, 117 insertions(+), 23 deletions(-) (limited to 'MediaBrowser.Controller/Entities') diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index fb4bd9244..7f41cb059 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -2329,7 +2329,8 @@ namespace MediaBrowser.Api.Playback state.TargetRefFrames, state.TargetVideoStreamCount, state.TargetAudioStreamCount, - state.TargetVideoCodecTag); + state.TargetVideoCodecTag, + state.IsTargetAVC); if (mediaProfile != null) { @@ -2547,7 +2548,8 @@ namespace MediaBrowser.Api.Playback state.TargetRefFrames, state.TargetVideoStreamCount, state.TargetAudioStreamCount, - state.TargetVideoCodecTag + state.TargetVideoCodecTag, + state.IsTargetAVC ).FirstOrDefault() ?? string.Empty; } diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index 019f378c5..863bc0193 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -516,5 +516,18 @@ namespace MediaBrowser.Api.Playback return false; } } + + public bool? IsTargetAVC + { + get + { + if (Request.Static) + { + return VideoStream == null ? null : VideoStream.IsAVC; + } + + return true; + } + } } } diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index 60af2c56a..89fe71d3c 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -154,6 +154,9 @@ namespace MediaBrowser.Controller.Entities public List> OrderBy { get; set; } + public DateTime? MinDateCreated { get; set; } + public DateTime? MinDateLastSaved { get; set; } + public InternalItemsQuery() { GroupByPresentationUniqueKey = true; diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs index 84c1b3bde..f53dec3bf 100644 --- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs +++ b/MediaBrowser.Dlna/Didl/DidlBuilder.cs @@ -177,7 +177,8 @@ namespace MediaBrowser.Dlna.Didl streamInfo.TargetRefFrames, streamInfo.TargetVideoStreamCount, streamInfo.TargetAudioStreamCount, - streamInfo.TargetVideoCodecTag); + streamInfo.TargetVideoCodecTag, + streamInfo.IsTargetAVC); foreach (var contentFeature in contentFeatureList) { @@ -322,7 +323,8 @@ namespace MediaBrowser.Dlna.Didl streamInfo.TargetRefFrames, streamInfo.TargetVideoStreamCount, streamInfo.TargetAudioStreamCount, - streamInfo.TargetVideoCodecTag); + streamInfo.TargetVideoCodecTag, + streamInfo.IsTargetAVC); var filename = url.Substring(0, url.IndexOf('?')); diff --git a/MediaBrowser.Dlna/PlayTo/PlayToController.cs b/MediaBrowser.Dlna/PlayTo/PlayToController.cs index 6345e2105..f6b24e615 100644 --- a/MediaBrowser.Dlna/PlayTo/PlayToController.cs +++ b/MediaBrowser.Dlna/PlayTo/PlayToController.cs @@ -540,7 +540,8 @@ namespace MediaBrowser.Dlna.PlayTo streamInfo.TargetRefFrames, streamInfo.TargetVideoStreamCount, streamInfo.TargetAudioStreamCount, - streamInfo.TargetVideoCodecTag); + streamInfo.TargetVideoCodecTag, + streamInfo.IsTargetAVC); return list.FirstOrDefault(); } diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs index d3738d903..0c7ff1b76 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs @@ -392,6 +392,19 @@ namespace MediaBrowser.MediaEncoding.Encoder } } + public bool? IsTargetAVC + { + get + { + if (Options.Static) + { + return VideoStream == null ? null : VideoStream.IsAVC; + } + + return false; + } + } + public int? TargetVideoStreamCount { get diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs index c72532669..b66a3ed96 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs @@ -823,7 +823,8 @@ namespace MediaBrowser.MediaEncoding.Encoder state.TargetRefFrames, state.TargetVideoStreamCount, state.TargetAudioStreamCount, - state.TargetVideoCodecTag); + state.TargetVideoCodecTag, + state.IsTargetAVC); if (mediaProfile != null) { diff --git a/MediaBrowser.Model/Dlna/ConditionProcessor.cs b/MediaBrowser.Model/Dlna/ConditionProcessor.cs index 69f1369dc..ec13cacfa 100644 --- a/MediaBrowser.Model/Dlna/ConditionProcessor.cs +++ b/MediaBrowser.Model/Dlna/ConditionProcessor.cs @@ -20,12 +20,15 @@ namespace MediaBrowser.Model.Dlna int? refFrames, int? numVideoStreams, int? numAudioStreams, - string videoCodecTag) + string videoCodecTag, + bool? isAvc) { switch (condition.Property) { case ProfileConditionValue.IsAnamorphic: return IsConditionSatisfied(condition, isAnamorphic); + case ProfileConditionValue.IsAvc: + return IsConditionSatisfied(condition, isAvc); case ProfileConditionValue.VideoFramerate: return IsConditionSatisfied(condition, videoFramerate); case ProfileConditionValue.VideoLevel: diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs index c4b3383a2..4a16a2780 100644 --- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs +++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs @@ -118,7 +118,8 @@ namespace MediaBrowser.Model.Dlna int? refFrames, int? numVideoStreams, int? numAudioStreams, - string videoCodecTag) + string videoCodecTag, + bool? isAvc) { // first bit means Time based seek supported, second byte range seek supported (not sure about the order now), so 01 = only byte seek, 10 = time based, 11 = both, 00 = none string orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetOrgOpValue(runtimeTicks.HasValue, isDirectStream, transcodeSeekInfo); @@ -159,7 +160,8 @@ namespace MediaBrowser.Model.Dlna refFrames, numVideoStreams, numAudioStreams, - videoCodecTag); + videoCodecTag, + isAvc); List orgPnValues = new List(); diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs index d6a322322..884a9f29d 100644 --- a/MediaBrowser.Model/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs @@ -285,7 +285,8 @@ namespace MediaBrowser.Model.Dlna int? refFrames, int? numVideoStreams, int? numAudioStreams, - string videoCodecTag) + string videoCodecTag, + bool? isAvc) { container = StringHelper.TrimStart(container ?? string.Empty, '.'); @@ -319,7 +320,7 @@ namespace MediaBrowser.Model.Dlna var anyOff = false; foreach (ProfileCondition c in i.Conditions) { - if (!conditionProcessor.IsVideoConditionSatisfied(c, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, refFrames, numVideoStreams, numAudioStreams, videoCodecTag)) + if (!conditionProcessor.IsVideoConditionSatisfied(c, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)) { anyOff = true; break; diff --git a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs index c17a09c3f..7e2002f17 100644 --- a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs +++ b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs @@ -20,6 +20,7 @@ NumAudioStreams = 16, NumVideoStreams = 17, IsSecondaryAudio = 18, - VideoCodecTag = 19 + VideoCodecTag = 19, + IsAvc = 20 } } \ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 30d1498bf..52b7fd43a 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -541,6 +541,7 @@ namespace MediaBrowser.Model.Dlna float? videoFramerate = videoStream == null ? null : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate; bool? isAnamorphic = videoStream == null ? null : videoStream.IsAnamorphic; string videoCodecTag = videoStream == null ? null : videoStream.CodecTag; + bool? isAvc = videoStream == null ? null : videoStream.IsAVC; TransportStreamTimestamp? timestamp = videoStream == null ? TransportStreamTimestamp.None : item.Timestamp; int? packetLength = videoStream == null ? null : videoStream.PacketLength; @@ -549,7 +550,7 @@ namespace MediaBrowser.Model.Dlna int? numAudioStreams = item.GetStreamCount(MediaStreamType.Audio); int? numVideoStreams = item.GetStreamCount(MediaStreamType.Video); - if (!conditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, refFrames, numVideoStreams, numAudioStreams, videoCodecTag)) + if (!conditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)) { LogConditionFailure(options.Profile, "VideoCodecProfile", applyCondition, item); applyConditions = false; @@ -718,6 +719,7 @@ namespace MediaBrowser.Model.Dlna float? videoFramerate = videoStream == null ? null : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate; bool? isAnamorphic = videoStream == null ? null : videoStream.IsAnamorphic; string videoCodecTag = videoStream == null ? null : videoStream.CodecTag; + bool? isAvc = videoStream == null ? null : videoStream.IsAVC; int? audioBitrate = audioStream == null ? null : audioStream.BitRate; int? audioChannels = audioStream == null ? null : audioStream.Channels; @@ -733,7 +735,7 @@ namespace MediaBrowser.Model.Dlna // Check container conditions foreach (ProfileCondition i in conditions) { - if (!conditionProcessor.IsVideoConditionSatisfied(i, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, refFrames, numVideoStreams, numAudioStreams, videoCodecTag)) + if (!conditionProcessor.IsVideoConditionSatisfied(i, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)) { LogConditionFailure(profile, "VideoContainerProfile", i, mediaSource); @@ -760,7 +762,7 @@ namespace MediaBrowser.Model.Dlna bool applyConditions = true; foreach (ProfileCondition applyCondition in i.ApplyConditions) { - if (!conditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, refFrames, numVideoStreams, numAudioStreams, videoCodecTag)) + if (!conditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)) { LogConditionFailure(profile, "VideoCodecProfile", applyCondition, mediaSource); applyConditions = false; @@ -780,7 +782,7 @@ namespace MediaBrowser.Model.Dlna foreach (ProfileCondition i in conditions) { - if (!conditionProcessor.IsVideoConditionSatisfied(i, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, refFrames, numVideoStreams, numAudioStreams, videoCodecTag)) + if (!conditionProcessor.IsVideoConditionSatisfied(i, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)) { LogConditionFailure(profile, "VideoCodecProfile", i, mediaSource); diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index f7dc893c8..c9cb873e1 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -676,6 +676,19 @@ namespace MediaBrowser.Model.Dlna } } + public bool? IsTargetAVC + { + get + { + if (IsDirectStream) + { + return TargetVideoStream == null ? null : TargetVideoStream.IsAVC; + } + + return true; + } + } + public int? TargetWidth { get diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs index 3de330557..36b5987a0 100644 --- a/MediaBrowser.Providers/Manager/ImageSaver.cs +++ b/MediaBrowser.Providers/Manager/ImageSaver.cs @@ -211,6 +211,20 @@ namespace MediaBrowser.Providers.Manager throw; } } + catch (IOException ex) + { + var retry = !string.IsNullOrWhiteSpace(retryPath) && + !string.Equals(path, retryPath, StringComparison.OrdinalIgnoreCase); + + if (retry) + { + _logger.Error("IOException saving to {0}. {2}. Will retry saving to {1}", path, retryPath, ex.Message); + } + else + { + throw; + } + } source.Position = 0; await SaveImageToLocation(source, retryPath, cancellationToken).ConfigureAwait(false); diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 3db764ae1..4530bfcb6 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -606,15 +606,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV ActiveRecordingInfo activeRecordingInfo; if (!_activeRecordings.TryGetValue(updatedTimer.Id, out activeRecordingInfo)) { - UpdateExistingTimerWithNewData(existingTimer, updatedTimer); - - _timerProvider.Update(existingTimer); + existingTimer.PrePaddingSeconds = updatedTimer.PrePaddingSeconds; + existingTimer.PostPaddingSeconds = updatedTimer.PostPaddingSeconds; + existingTimer.IsPostPaddingRequired = updatedTimer.IsPostPaddingRequired; + existingTimer.IsPrePaddingRequired = updatedTimer.IsPrePaddingRequired; } return Task.FromResult(true); } - private void UpdateExistingTimerWithNewData(TimerInfo existingTimer, TimerInfo updatedTimer) + private void UpdateExistingTimerWithNewMetadata(TimerInfo existingTimer, TimerInfo updatedTimer) { // Update the program info but retain the status existingTimer.ChannelId = updatedTimer.ChannelId; @@ -1430,7 +1431,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV { SaveSeriesNfo(timer, recordingPath, seriesPath); } - else if (!timer.IsMovie || timer.IsSports) + else if (!timer.IsMovie || timer.IsSports || timer.IsNews) { SaveVideoNfo(timer, recordingPath); } @@ -1620,7 +1621,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV ActiveRecordingInfo activeRecordingInfo; if (!_activeRecordings.TryGetValue(timer.Id, out activeRecordingInfo)) { - UpdateExistingTimerWithNewData(existingTimer, timer); + UpdateExistingTimerWithNewMetadata(existingTimer, timer); if (ShouldCancelTimerForSeriesTimer(seriesTimer, timer)) { diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs index 2cdd0571b..d49e3f258 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs @@ -104,7 +104,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun Action onStarted = null; if (isFirstAttempt) { - onStarted = () => openTaskCompletionSource.TrySetResult(true); + onStarted = () => ResolveWhenExists(openTaskCompletionSource, tempFilePath, cancellationToken); } await DirectRecorder.CopyUntilCancelled(response.Content, outputStream, onStarted, cancellationToken).ConfigureAwait(false); } @@ -137,6 +137,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun }).ConfigureAwait(false); } + private async void ResolveWhenExists(TaskCompletionSource taskCompletionSource, string file, CancellationToken cancellationToken) + { + while (!File.Exists(file) && !cancellationToken.IsCancellationRequested) + { + await Task.Delay(50).ConfigureAwait(false); + } + + taskCompletionSource.TrySetResult(true); + } + private async void DeleteTempFile(string path) { for (var i = 0; i < 10; i++) diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index c843ab596..5a11742dc 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -2724,6 +2724,18 @@ namespace MediaBrowser.Server.Implementations.Persistence cmd.Parameters.Add(cmd, "@MinIndexNumber", DbType.Int32).Value = query.MinIndexNumber.Value; } + if (query.MinDateCreated.HasValue) + { + whereClauses.Add("DateCreated>=@MinDateCreated"); + cmd.Parameters.Add(cmd, "@MinDateCreated", DbType.DateTime).Value = query.MinDateCreated.Value; + } + + if (query.MinDateLastSaved.HasValue) + { + whereClauses.Add("DateLastSaved>=@MinDateLastSaved"); + cmd.Parameters.Add(cmd, "@MinDateLastSaved", DbType.DateTime).Value = query.MinDateLastSaved.Value; + } + //if (query.MinPlayers.HasValue) //{ // whereClauses.Add("Players>=@MinPlayers"); -- cgit v1.2.3 From 50e66869872579d2cbd8337c4b114cf68dff814a Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 7 Oct 2016 11:08:13 -0400 Subject: update live stream management --- Emby.Drawing/ImageProcessor.cs | 13 +- MediaBrowser.Api/MediaBrowser.Api.csproj | 4 + MediaBrowser.Api/StartupWizardService.cs | 1 + MediaBrowser.Controller/Entities/BaseItem.cs | 28 ++- MediaBrowser.Controller/Entities/Game.cs | 2 +- MediaBrowser.Controller/Entities/IHasImages.cs | 6 +- MediaBrowser.Controller/Entities/Movies/Movie.cs | 6 +- MediaBrowser.Controller/Entities/MusicVideo.cs | 11 +- MediaBrowser.Controller/Entities/Trailer.cs | 4 +- MediaBrowser.Controller/Entities/Video.cs | 2 +- MediaBrowser.Controller/LiveTv/LiveStream.cs | 1 + .../MediaBrowser.Controller.csproj | 2 - MediaBrowser.Controller/Net/IHttpResultFactory.cs | 8 - .../Providers/IImageFileSaver.cs | 20 -- MediaBrowser.Controller/Providers/IImageSaver.cs | 11 - .../Providers/IProviderManager.cs | 7 - MediaBrowser.Controller/Providers/ItemInfo.cs | 2 +- MediaBrowser.Dlna/Eventing/EventManager.cs | 1 - .../Images/LocalImageProvider.cs | 2 +- MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs | 2 +- .../Encoder/EncodingUtils.cs | 2 +- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 4 +- .../Configuration/ServerConfiguration.cs | 1 + .../Notifications/NotificationType.cs | 1 - MediaBrowser.Providers/Manager/ImageSaver.cs | 10 +- MediaBrowser.Providers/Manager/ProviderManager.cs | 10 +- .../TV/TheTVDB/TvdbSeriesProvider.cs | 3 +- .../EntryPoints/Notifications/Notifications.cs | 17 +- .../HttpServer/HttpListenerHost.cs | 10 +- .../HttpServer/HttpResultFactory.cs | 23 -- .../HttpServer/NativeWebSocket.cs | 240 ------------------ .../HttpServer/RangeRequestWriter.cs | 18 -- .../SocketSharp/WebSocketSharpResponse.cs | 18 +- .../Library/LibraryManager.cs | 2 +- .../Library/Resolvers/Audio/MusicArtistResolver.cs | 18 +- .../Library/Resolvers/Movies/MovieResolver.cs | 30 +-- .../Library/UserDataManager.cs | 24 +- .../LiveTv/EmbyTV/EmbyTV.cs | 33 ++- .../LiveTv/LiveTvManager.cs | 132 ++++------ .../LiveTv/LiveTvMediaSourceProvider.cs | 19 +- .../LiveTv/TunerHosts/BaseTunerHost.cs | 19 -- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 38 ++- .../TunerHosts/HdHomerun/HdHomerunLiveStream.cs | 196 ++++----------- .../LiveTv/TunerHosts/MulticastStream.cs | 96 ++++++++ .../LiveTv/TunerHosts/QueueStream.cs | 93 +++++++ .../Localization/Core/en-US.json | 1 - .../MediaBrowser.Server.Implementations.csproj | 3 +- .../Notifications/CoreNotificationTypes.cs | 7 - .../Persistence/SqliteItemRepository.cs | 7 +- .../ServerManager/WebSocketConnection.cs | 63 +---- .../ApplicationHost.cs | 15 +- MediaBrowser.XbmcMetadata/Images/XbmcImageSaver.cs | 272 --------------------- .../MediaBrowser.XbmcMetadata.csproj | 1 - 53 files changed, 484 insertions(+), 1075 deletions(-) delete mode 100644 MediaBrowser.Controller/Providers/IImageFileSaver.cs delete mode 100644 MediaBrowser.Controller/Providers/IImageSaver.cs delete mode 100644 MediaBrowser.Server.Implementations/HttpServer/NativeWebSocket.cs create mode 100644 MediaBrowser.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs create mode 100644 MediaBrowser.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs delete mode 100644 MediaBrowser.XbmcMetadata/Images/XbmcImageSaver.cs (limited to 'MediaBrowser.Controller/Entities') diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index 80ebbb719..e9f8f81f3 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -829,18 +829,7 @@ namespace Emby.Drawing // Run the enhancers sequentially in order of priority foreach (var enhancer in imageEnhancers) { - var typeName = enhancer.GetType().Name; - - try - { - await enhancer.EnhanceImageAsync(item, inputPath, outputPath, imageType, imageIndex).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.ErrorException("{0} failed enhancing {1}", ex, typeName, item.Name); - - throw; - } + await enhancer.EnhanceImageAsync(item, inputPath, outputPath, imageType, imageIndex).ConfigureAwait(false); // Feed the output into the next enhancer as input inputPath = outputPath; diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index a98637650..96d7874f0 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -197,6 +197,10 @@ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B} MediaBrowser.Model + + {2e781478-814d-4a48-9d80-bff206441a65} + MediaBrowser.Server.Implementations + diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index ebb3204a4..4c5abc996 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -116,6 +116,7 @@ namespace MediaBrowser.Api config.EnableCaseSensitiveItemIds = true; //config.EnableFolderView = true; config.SchemaVersion = 109; + config.EnableSimpleArtistDetection = true; } public void Post(UpdateStartupConfiguration request) diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index be88c535e..90a22b217 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -115,6 +115,22 @@ namespace MediaBrowser.Controller.Entities [IgnoreDataMember] public bool IsInMixedFolder { get; set; } + [IgnoreDataMember] + protected virtual bool SupportsIsInMixedFolderDetection + { + get { return false; } + } + + public bool DetectIsInMixedFolder() + { + if (SupportsIsInMixedFolderDetection) + { + + } + + return IsInMixedFolder; + } + [IgnoreDataMember] public virtual bool SupportsRemoteImageDownloading { @@ -1116,7 +1132,7 @@ namespace MediaBrowser.Controller.Entities var hasThemeMedia = this as IHasThemeMedia; if (hasThemeMedia != null) { - if (!IsInMixedFolder) + if (!DetectIsInMixedFolder()) { themeSongsChanged = await RefreshThemeSongs(hasThemeMedia, options, fileSystemChildren, cancellationToken).ConfigureAwait(false); @@ -1266,7 +1282,15 @@ namespace MediaBrowser.Controller.Entities { var current = this; - return current.IsInMixedFolder == newItem.IsInMixedFolder; + if (!SupportsIsInMixedFolderDetection) + { + if (current.IsInMixedFolder != newItem.IsInMixedFolder) + { + return false; + } + } + + return true; } public void AfterMetadataRefresh() diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs index 24910498f..a48b9f564 100644 --- a/MediaBrowser.Controller/Entities/Game.cs +++ b/MediaBrowser.Controller/Entities/Game.cs @@ -98,7 +98,7 @@ namespace MediaBrowser.Controller.Entities public override IEnumerable GetDeletePaths() { - if (!IsInMixedFolder) + if (!DetectIsInMixedFolder()) { return new[] { System.IO.Path.GetDirectoryName(Path) }; } diff --git a/MediaBrowser.Controller/Entities/IHasImages.cs b/MediaBrowser.Controller/Entities/IHasImages.cs index a38b7394d..1ab0566e0 100644 --- a/MediaBrowser.Controller/Entities/IHasImages.cs +++ b/MediaBrowser.Controller/Entities/IHasImages.cs @@ -150,11 +150,7 @@ namespace MediaBrowser.Controller.Entities /// true if [supports local metadata]; otherwise, false. bool SupportsLocalMetadata { get; } - /// - /// Gets a value indicating whether this instance is in mixed folder. - /// - /// true if this instance is in mixed folder; otherwise, false. - bool IsInMixedFolder { get; } + bool DetectIsInMixedFolder(); /// /// Gets a value indicating whether this instance is locked. diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs index f0270497c..e1e336147 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs @@ -81,7 +81,7 @@ namespace MediaBrowser.Controller.Entities.Movies // Must have a parent to have special features // In other words, it must be part of the Parent/Child tree - if (LocationType == LocationType.FileSystem && GetParent() != null && !IsInMixedFolder) + if (LocationType == LocationType.FileSystem && GetParent() != null && !DetectIsInMixedFolder()) { var specialFeaturesChanged = await RefreshSpecialFeatures(options, fileSystemChildren, cancellationToken).ConfigureAwait(false); @@ -119,7 +119,7 @@ namespace MediaBrowser.Controller.Entities.Movies { var info = GetItemLookupInfo(); - if (!IsInMixedFolder) + if (!DetectIsInMixedFolder()) { info.Name = System.IO.Path.GetFileName(ContainingFolderPath); } @@ -145,7 +145,7 @@ namespace MediaBrowser.Controller.Entities.Movies else { // Try to get the year from the folder name - if (!IsInMixedFolder) + if (!DetectIsInMixedFolder()) { info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath)); diff --git a/MediaBrowser.Controller/Entities/MusicVideo.cs b/MediaBrowser.Controller/Entities/MusicVideo.cs index 8b749b7a5..9254802dd 100644 --- a/MediaBrowser.Controller/Entities/MusicVideo.cs +++ b/MediaBrowser.Controller/Entities/MusicVideo.cs @@ -37,6 +37,15 @@ namespace MediaBrowser.Controller.Entities } } + [IgnoreDataMember] + protected override bool SupportsIsInMixedFolderDetection + { + get + { + return true; + } + } + public override UnratedItem GetBlockUnratedType() { return UnratedItem.Music; @@ -65,7 +74,7 @@ namespace MediaBrowser.Controller.Entities else { // Try to get the year from the folder name - if (!IsInMixedFolder) + if (!DetectIsInMixedFolder()) { info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath)); diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs index 7a987a68e..f68cd2c85 100644 --- a/MediaBrowser.Controller/Entities/Trailer.cs +++ b/MediaBrowser.Controller/Entities/Trailer.cs @@ -64,7 +64,7 @@ namespace MediaBrowser.Controller.Entities info.IsLocalTrailer = TrailerTypes.Contains(TrailerType.LocalTrailer); - if (!IsInMixedFolder && LocationType == LocationType.FileSystem) + if (!DetectIsInMixedFolder() && LocationType == LocationType.FileSystem) { info.Name = System.IO.Path.GetFileName(ContainingFolderPath); } @@ -90,7 +90,7 @@ namespace MediaBrowser.Controller.Entities else { // Try to get the year from the folder name - if (!IsInMixedFolder) + if (!DetectIsInMixedFolder()) { info = LibraryManager.ParseName(System.IO.Path.GetFileName(ContainingFolderPath)); diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index 1406a05ce..c64cdf57d 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -480,7 +480,7 @@ namespace MediaBrowser.Controller.Entities public override IEnumerable GetDeletePaths() { - if (!IsInMixedFolder) + if (!DetectIsInMixedFolder()) { return new[] { ContainingFolderPath }; } diff --git a/MediaBrowser.Controller/LiveTv/LiveStream.cs b/MediaBrowser.Controller/LiveTv/LiveStream.cs index 7d44fbd90..a5d432a54 100644 --- a/MediaBrowser.Controller/LiveTv/LiveStream.cs +++ b/MediaBrowser.Controller/LiveTv/LiveStream.cs @@ -14,6 +14,7 @@ namespace MediaBrowser.Controller.LiveTv public ITunerHost TunerHost { get; set; } public string OriginalStreamId { get; set; } public bool EnableStreamSharing { get; set; } + public string UniqueId = Guid.NewGuid().ToString("N"); public LiveStream(MediaSourceInfo mediaSource) { diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index e9d2054da..7c1114e22 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -287,9 +287,7 @@ - - diff --git a/MediaBrowser.Controller/Net/IHttpResultFactory.cs b/MediaBrowser.Controller/Net/IHttpResultFactory.cs index 8fdb1ce37..ca453840f 100644 --- a/MediaBrowser.Controller/Net/IHttpResultFactory.cs +++ b/MediaBrowser.Controller/Net/IHttpResultFactory.cs @@ -11,14 +11,6 @@ namespace MediaBrowser.Controller.Net /// public interface IHttpResultFactory { - /// - /// Throws the error. - /// - /// The status code. - /// The error message. - /// The response headers. - void ThrowError(int statusCode, string errorMessage, IDictionary responseHeaders = null); - /// /// Gets the result. /// diff --git a/MediaBrowser.Controller/Providers/IImageFileSaver.cs b/MediaBrowser.Controller/Providers/IImageFileSaver.cs deleted file mode 100644 index 3e11d8bf8..000000000 --- a/MediaBrowser.Controller/Providers/IImageFileSaver.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Collections.Generic; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.Drawing; -using MediaBrowser.Model.Entities; - -namespace MediaBrowser.Controller.Providers -{ - public interface IImageFileSaver : IImageSaver - { - /// - /// Gets the save paths. - /// - /// The item. - /// The type. - /// The format. - /// The index. - /// IEnumerable{System.String}. - IEnumerable GetSavePaths(IHasImages item, ImageType type, ImageFormat format, int index); - } -} \ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/IImageSaver.cs b/MediaBrowser.Controller/Providers/IImageSaver.cs deleted file mode 100644 index 62017160f..000000000 --- a/MediaBrowser.Controller/Providers/IImageSaver.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace MediaBrowser.Controller.Providers -{ - public interface IImageSaver - { - /// - /// Gets the name. - /// - /// The name. - string Name { get; } - } -} diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs index 3eefa9647..d3e5685bb 100644 --- a/MediaBrowser.Controller/Providers/IProviderManager.cs +++ b/MediaBrowser.Controller/Providers/IProviderManager.cs @@ -95,15 +95,8 @@ namespace MediaBrowser.Controller.Providers /// /// Adds the metadata providers. /// - /// The image providers. - /// The metadata services. - /// The metadata providers. - /// The savers. - /// The image savers. - /// The external ids. void AddParts(IEnumerable imageProviders, IEnumerable metadataServices, IEnumerable metadataProviders, IEnumerable savers, - IEnumerable imageSavers, IEnumerable externalIds); /// diff --git a/MediaBrowser.Controller/Providers/ItemInfo.cs b/MediaBrowser.Controller/Providers/ItemInfo.cs index 63cc48058..8de11b743 100644 --- a/MediaBrowser.Controller/Providers/ItemInfo.cs +++ b/MediaBrowser.Controller/Providers/ItemInfo.cs @@ -10,7 +10,7 @@ namespace MediaBrowser.Controller.Providers { Path = item.Path; ContainingFolderPath = item.ContainingFolderPath; - IsInMixedFolder = item.IsInMixedFolder; + IsInMixedFolder = item.DetectIsInMixedFolder(); var video = item as Video; if (video != null) diff --git a/MediaBrowser.Dlna/Eventing/EventManager.cs b/MediaBrowser.Dlna/Eventing/EventManager.cs index 68f012c3a..51c8d2d91 100644 --- a/MediaBrowser.Dlna/Eventing/EventManager.cs +++ b/MediaBrowser.Dlna/Eventing/EventManager.cs @@ -156,7 +156,6 @@ namespace MediaBrowser.Dlna.Eventing } catch (OperationCanceledException) { - throw; } catch { diff --git a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs index fe61a7a46..ef9160b70 100644 --- a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs +++ b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs @@ -132,7 +132,7 @@ namespace MediaBrowser.LocalMetadata.Images } var imagePrefix = item.FileNameWithoutExtension + "-"; - var isInMixedFolder = item.IsInMixedFolder; + var isInMixedFolder = item.DetectIsInMixedFolder(); PopulatePrimaryImages(item, images, files, imagePrefix, isInMixedFolder); diff --git a/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs index a90789a3e..5592c068c 100644 --- a/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs @@ -99,7 +99,7 @@ namespace MediaBrowser.LocalMetadata.Savers public static string GetGameSavePath(Game item) { - if (item.IsInMixedFolder) + if (item.DetectIsInMixedFolder()) { return Path.ChangeExtension(item.Path, ".xml"); } diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs index 33e90743a..5d0f1f075 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs @@ -76,7 +76,7 @@ namespace MediaBrowser.MediaEncoding.Encoder public static string GetProbeSizeArgument(bool isDvd) { - return isDvd ? "-probesize 1G -analyzeduration 200M" : " -analyzeduration 2M"; + return isDvd ? "-probesize 1G -analyzeduration 200M" : ""; } } } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 25ad14fe8..5c3345008 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -426,8 +426,10 @@ namespace MediaBrowser.MediaEncoding.Encoder var inputFiles = MediaEncoderHelpers.GetInputArgument(FileSystem, request.InputPath, request.Protocol, request.MountedIso, request.PlayableStreamFileNames); + var probeSizeArgument = GetProbeSizeArgument(inputFiles, request.Protocol); + return GetMediaInfoInternal(GetInputArgument(inputFiles, request.Protocol), request.InputPath, request.Protocol, extractChapters, - GetProbeSizeArgument(inputFiles, request.Protocol), request.MediaType == DlnaProfileType.Audio, request.VideoType, cancellationToken); + probeSizeArgument, request.MediaType == DlnaProfileType.Audio, request.VideoType, cancellationToken); } /// diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 1d2928f67..e7f8e6548 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -203,6 +203,7 @@ namespace MediaBrowser.Model.Configuration public string[] CodecsUsed { get; set; } public bool EnableChannelView { get; set; } public bool EnableExternalContentInSuggestions { get; set; } + public bool EnableSimpleArtistDetection { get; set; } public int ImageExtractionTimeoutMs { get; set; } /// diff --git a/MediaBrowser.Model/Notifications/NotificationType.cs b/MediaBrowser.Model/Notifications/NotificationType.cs index f5e3624f0..eefd15808 100644 --- a/MediaBrowser.Model/Notifications/NotificationType.cs +++ b/MediaBrowser.Model/Notifications/NotificationType.cs @@ -16,7 +16,6 @@ namespace MediaBrowser.Model.Notifications PluginUpdateInstalled, PluginUninstalled, NewLibraryContent, - NewLibraryContentMultiple, ServerRestartRequired, TaskFailed, CameraImageUploaded, diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs index c9b3f22c5..5203adc9d 100644 --- a/MediaBrowser.Providers/Manager/ImageSaver.cs +++ b/MediaBrowser.Providers/Manager/ImageSaver.cs @@ -371,7 +371,7 @@ namespace MediaBrowser.Providers.Manager return Path.Combine(seriesFolder, imageFilename); } - if (item.IsInMixedFolder) + if (item.DetectIsInMixedFolder()) { return GetSavePathForItemInMixedFolder(item, type, "landscape", extension); } @@ -447,7 +447,7 @@ namespace MediaBrowser.Providers.Manager path = Path.Combine(Path.GetDirectoryName(item.Path), "metadata", filename + extension); } - else if (item.IsInMixedFolder) + else if (item.DetectIsInMixedFolder()) { path = GetSavePathForItemInMixedFolder(item, type, filename, extension); } @@ -514,7 +514,7 @@ namespace MediaBrowser.Providers.Manager if (imageIndex.Value == 0) { - if (item.IsInMixedFolder) + if (item.DetectIsInMixedFolder()) { return new[] { GetSavePathForItemInMixedFolder(item, type, "fanart", extension) }; } @@ -540,7 +540,7 @@ namespace MediaBrowser.Providers.Manager var outputIndex = imageIndex.Value; - if (item.IsInMixedFolder) + if (item.DetectIsInMixedFolder()) { return new[] { GetSavePathForItemInMixedFolder(item, type, "fanart" + outputIndex.ToString(UsCulture), extension) }; } @@ -583,7 +583,7 @@ namespace MediaBrowser.Providers.Manager return new[] { Path.Combine(seasonFolder, imageFilename) }; } - if (item.IsInMixedFolder || item is MusicVideo) + if (item.DetectIsInMixedFolder() || item is MusicVideo) { return new[] { GetSavePathForItemInMixedFolder(item, type, string.Empty, extension) }; } diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 7e28254b0..ae1d60eb9 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -58,7 +58,6 @@ namespace MediaBrowser.Providers.Manager private IMetadataService[] _metadataServices = { }; private IMetadataProvider[] _metadataProviders = { }; private IEnumerable _savers; - private IImageSaver[] _imageSavers; private readonly IServerApplicationPaths _appPaths; private readonly IJsonSerializer _json; @@ -91,21 +90,14 @@ namespace MediaBrowser.Providers.Manager /// /// Adds the metadata providers. /// - /// The image providers. - /// The metadata services. - /// The metadata providers. - /// The metadata savers. - /// The image savers. - /// The external ids. public void AddParts(IEnumerable imageProviders, IEnumerable metadataServices, IEnumerable metadataProviders, IEnumerable metadataSavers, - IEnumerable imageSavers, IEnumerable externalIds) + IEnumerable externalIds) { ImageProviders = imageProviders.ToArray(); _metadataServices = metadataServices.OrderBy(i => i.Order).ToArray(); _metadataProviders = metadataProviders.ToArray(); - _imageSavers = imageSavers.ToArray(); _externalIds = externalIds.OrderBy(i => i.Name).ToArray(); _savers = metadataSavers.Where(i => diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index ca4f1b956..2572a4f58 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -41,7 +41,7 @@ namespace MediaBrowser.Providers.TV private readonly ILibraryManager _libraryManager; private readonly IMemoryStreamProvider _memoryStreamProvider; - public TvdbSeriesProvider(IZipClient zipClient, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager config, ILogger logger, ILibraryManager libraryManager) + public TvdbSeriesProvider(IZipClient zipClient, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager config, ILogger logger, ILibraryManager libraryManager, IMemoryStreamProvider memoryStreamProvider) { _zipClient = zipClient; _httpClient = httpClient; @@ -49,6 +49,7 @@ namespace MediaBrowser.Providers.TV _config = config; _logger = logger; _libraryManager = libraryManager; + _memoryStreamProvider = memoryStreamProvider; Current = this; } diff --git a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs index e84b66c5a..f7fe707da 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs @@ -377,10 +377,10 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications DisposeLibraryUpdateTimer(); } - if (items.Count == 1) - { - var item = items.First(); + items = items.Take(10).ToList(); + foreach (var item in items) + { var notification = new NotificationRequest { NotificationType = NotificationType.NewLibraryContent.ToString() @@ -388,17 +388,6 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications notification.Variables["Name"] = GetItemName(item); - await SendNotification(notification).ConfigureAwait(false); - } - else - { - var notification = new NotificationRequest - { - NotificationType = NotificationType.NewLibraryContentMultiple.ToString() - }; - - notification.Variables["ItemCount"] = items.Count.ToString(CultureInfo.InvariantCulture); - await SendNotification(notification).ConfigureAwait(false); } } diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs index 7dc6fbb25..2ebeb0d44 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -94,12 +94,12 @@ namespace MediaBrowser.Server.Implementations.HttpServer // The Markdown feature causes slow startup times (5 mins+) on cold boots for some users // Custom format allows images - HostConfig.Instance.EnableFeatures = Feature.Csv | Feature.Html | Feature.Json | Feature.Jsv | Feature.Metadata | Feature.Xml | Feature.CustomFormat; + HostConfig.Instance.EnableFeatures = Feature.Html | Feature.Json | Feature.CustomFormat; container.Adapter = _containerAdapter; Plugins.RemoveAll(x => x is NativeTypesFeature); - Plugins.Add(new SwaggerFeature()); + //Plugins.Add(new SwaggerFeature()); Plugins.Add(new CorsFeature(allowedHeaders: "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization")); //Plugins.Add(new AuthFeature(() => new AuthUserSession(), new IAuthProvider[] { @@ -546,8 +546,10 @@ namespace MediaBrowser.Server.Implementations.HttpServer } } } - - throw new NotImplementedException("Cannot execute handler: " + handler + " at PathInfo: " + httpReq.PathInfo); + else + { + httpRes.Close(); + } } /// diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs index 04085d3c7..10d6f7493 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -683,29 +683,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer } } - /// - /// Gets the error result. - /// - /// The status code. - /// The error message. - /// The response headers. - /// System.Object. - public void ThrowError(int statusCode, string errorMessage, IDictionary responseHeaders = null) - { - var error = new HttpError - { - Status = statusCode, - ErrorCode = errorMessage - }; - - if (responseHeaders != null) - { - AddResponseHeaders(error, responseHeaders); - } - - throw error; - } - public object GetAsyncStreamWriter(IAsyncStreamSource streamSource) { return new AsyncStreamWriter(streamSource); diff --git a/MediaBrowser.Server.Implementations/HttpServer/NativeWebSocket.cs b/MediaBrowser.Server.Implementations/HttpServer/NativeWebSocket.cs deleted file mode 100644 index cac2f8e09..000000000 --- a/MediaBrowser.Server.Implementations/HttpServer/NativeWebSocket.cs +++ /dev/null @@ -1,240 +0,0 @@ -using MediaBrowser.Common.Events; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Logging; -using System; -using System.Net.WebSockets; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using WebSocketMessageType = MediaBrowser.Model.Net.WebSocketMessageType; -using WebSocketState = MediaBrowser.Model.Net.WebSocketState; - -namespace MediaBrowser.Server.Implementations.HttpServer -{ - /// - /// Class NativeWebSocket - /// - public class NativeWebSocket : IWebSocket - { - /// - /// The logger - /// - private readonly ILogger _logger; - - public event EventHandler Closed; - - /// - /// Gets or sets the web socket. - /// - /// The web socket. - private System.Net.WebSockets.WebSocket WebSocket { get; set; } - - private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); - - /// - /// Initializes a new instance of the class. - /// - /// The socket. - /// The logger. - /// socket - public NativeWebSocket(WebSocket socket, ILogger logger) - { - if (socket == null) - { - throw new ArgumentNullException("socket"); - } - - if (logger == null) - { - throw new ArgumentNullException("logger"); - } - - _logger = logger; - WebSocket = socket; - - Receive(); - } - - /// - /// Gets or sets the state. - /// - /// The state. - public WebSocketState State - { - get - { - WebSocketState commonState; - - if (!Enum.TryParse(WebSocket.State.ToString(), true, out commonState)) - { - _logger.Warn("Unrecognized WebSocketState: {0}", WebSocket.State.ToString()); - } - - return commonState; - } - } - - /// - /// Receives this instance. - /// - private async void Receive() - { - while (true) - { - byte[] bytes; - - try - { - bytes = await ReceiveBytesAsync(_cancellationTokenSource.Token).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - break; - } - catch (WebSocketException ex) - { - _logger.ErrorException("Error receiving web socket message", ex); - - break; - } - - if (bytes == null) - { - // Connection closed - EventHelper.FireEventIfNotNull(Closed, this, EventArgs.Empty, _logger); - break; - } - - if (OnReceiveBytes != null) - { - OnReceiveBytes(bytes); - } - } - } - - /// - /// Receives the async. - /// - /// The cancellation token. - /// Task{WebSocketMessageInfo}. - /// Connection closed - private async Task ReceiveBytesAsync(CancellationToken cancellationToken) - { - var bytes = new byte[4096]; - var buffer = new ArraySegment(bytes); - - var result = await WebSocket.ReceiveAsync(buffer, cancellationToken).ConfigureAwait(false); - - if (result.CloseStatus.HasValue) - { - _logger.Info("Web socket connection closed by client. Reason: {0}", result.CloseStatus.Value); - return null; - } - - return buffer.Array; - } - - /// - /// Sends the async. - /// - /// The bytes. - /// The type. - /// if set to true [end of message]. - /// The cancellation token. - /// Task. - public Task SendAsync(byte[] bytes, WebSocketMessageType type, bool endOfMessage, CancellationToken cancellationToken) - { - System.Net.WebSockets.WebSocketMessageType nativeType; - - if (!Enum.TryParse(type.ToString(), true, out nativeType)) - { - _logger.Warn("Unrecognized WebSocketMessageType: {0}", type.ToString()); - } - - var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cancellationTokenSource.Token); - - return WebSocket.SendAsync(new ArraySegment(bytes), nativeType, true, linkedTokenSource.Token); - } - - public Task SendAsync(byte[] bytes, bool endOfMessage, CancellationToken cancellationToken) - { - var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cancellationTokenSource.Token); - - return WebSocket.SendAsync(new ArraySegment(bytes), System.Net.WebSockets.WebSocketMessageType.Binary, true, linkedTokenSource.Token); - } - - public Task SendAsync(string text, bool endOfMessage, CancellationToken cancellationToken) - { - var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cancellationTokenSource.Token); - - var bytes = Encoding.UTF8.GetBytes(text); - - return WebSocket.SendAsync(new ArraySegment(bytes), System.Net.WebSockets.WebSocketMessageType.Text, true, linkedTokenSource.Token); - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - } - - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool dispose) - { - if (dispose) - { - _cancellationTokenSource.Cancel(); - - WebSocket.Dispose(); - } - } - - /// - /// Gets or sets the receive action. - /// - /// The receive action. - public Action OnReceiveBytes { get; set; } - - /// - /// Gets or sets the on receive. - /// - /// The on receive. - public Action OnReceive { get; set; } - - /// - /// The _supports native web socket - /// - private static bool? _supportsNativeWebSocket; - - /// - /// Gets a value indicating whether [supports web sockets]. - /// - /// true if [supports web sockets]; otherwise, false. - public static bool IsSupported - { - get - { - if (!_supportsNativeWebSocket.HasValue) - { - try - { - new ClientWebSocket(); - - _supportsNativeWebSocket = true; - } - catch (PlatformNotSupportedException) - { - _supportsNativeWebSocket = false; - } - } - - return _supportsNativeWebSocket.Value; - } - } - } -} diff --git a/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs b/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs index 488c630fe..4b94095f5 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs @@ -191,15 +191,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer } } } - catch (IOException) - { - throw; - } - catch (Exception ex) - { - _logger.ErrorException("Error in range request writer", ex); - throw; - } finally { if (OnComplete != null) @@ -251,15 +242,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer } } } - catch (IOException ex) - { - throw; - } - catch (Exception ex) - { - _logger.ErrorException("Error in range request writer", ex); - throw; - } finally { if (OnComplete != null) diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs index e08be8bd1..a58645ec5 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs @@ -81,20 +81,12 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp public void Write(string text) { - try - { - var bOutput = System.Text.Encoding.UTF8.GetBytes(text); - response.ContentLength64 = bOutput.Length; + var bOutput = System.Text.Encoding.UTF8.GetBytes(text); + response.ContentLength64 = bOutput.Length; - var outputStream = response.OutputStream; - outputStream.Write(bOutput, 0, bOutput.Length); - Close(); - } - catch (Exception ex) - { - _logger.ErrorException("Could not WriteTextToResponse: " + ex.Message, ex); - throw; - } + var outputStream = response.OutputStream; + outputStream.Write(bOutput, 0, bOutput.Length); + Close(); } public void Close() diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index b2bddc70d..f7661f55b 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -2463,7 +2463,7 @@ namespace MediaBrowser.Server.Implementations.Library public IEnumerable