From c273f5fd9c33415a1fa985222e43c9c02f3d36e5 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 30 Jun 2014 15:24:35 -0400 Subject: fixes #860 - Add options to display channels directly within user views --- .../Library/UserViewManager.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Library/UserViewManager.cs') diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs index fc4b9eb4c..9ac82690b 100644 --- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs @@ -90,12 +90,19 @@ namespace MediaBrowser.Server.Implementations.Library { var channelResult = await _channelManager.GetChannels(new ChannelQuery { - Limit = 0, UserId = query.UserId }, cancellationToken).ConfigureAwait(false); - if (channelResult.TotalRecordCount > 0) + var channels = channelResult.Items; + + var embeddedChannels = channels + .Where(i => user.Configuration.DisplayChannelsWithinViews.Contains(i.Id)) + .ToList(); + + list.AddRange(embeddedChannels.Select(i => _channelManager.GetChannel(i.Id))); + + if (channels.Length > embeddedChannels.Count) { list.Add(await _channelManager.GetInternalChannelFolder(query.UserId, cancellationToken).ConfigureAwait(false)); } -- cgit v1.2.3 From 43943657b7ea0c607f998c6397d3878e3fb05c5f Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 1 Jul 2014 00:06:28 -0400 Subject: add additional view settings --- .../Collections/ICollectionManager.cs | 7 ++++ MediaBrowser.Controller/Entities/BaseItem.cs | 5 +++ MediaBrowser.Controller/Entities/Folder.cs | 5 ++- .../Configuration/NotificationOptions.cs | 10 ++--- .../Configuration/UserConfiguration.cs | 3 ++ MediaBrowser.Model/Dlna/DeviceProfile.cs | 10 ++--- MediaBrowser.Model/Dlna/StreamBuilder.cs | 43 ++++++++++++++++++---- MediaBrowser.Model/Dlna/StreamInfo.cs | 22 ++++++++--- MediaBrowser.Model/Extensions/StringHelper.cs | 11 ++++++ MediaBrowser.Model/Notifications/Notification.cs | 5 --- .../Collections/CollectionManager.cs | 11 ++++-- .../Collections/ManualCollectionsFolder.cs | 5 +++ .../Library/UserViewManager.cs | 17 +++++---- 13 files changed, 114 insertions(+), 40 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Library/UserViewManager.cs') diff --git a/MediaBrowser.Controller/Collections/ICollectionManager.cs b/MediaBrowser.Controller/Collections/ICollectionManager.cs index b63c49f99..fdb2a4975 100644 --- a/MediaBrowser.Controller/Collections/ICollectionManager.cs +++ b/MediaBrowser.Controller/Collections/ICollectionManager.cs @@ -38,5 +38,12 @@ namespace MediaBrowser.Controller.Collections /// The user. /// IEnumerable{BaseItem}. IEnumerable CollapseItemsWithinBoxSets(IEnumerable items, User user); + + /// + /// Gets the collections folder. + /// + /// The user identifier. + /// Folder. + Folder GetCollectionsFolder(string userId); } } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index a2ff1b4fd..524d7097b 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -134,6 +134,11 @@ namespace MediaBrowser.Controller.Entities } } + public virtual bool IsHiddenFromUser(User user) + { + return false; + } + [IgnoreDataMember] public virtual bool IsOwnedItem { diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index f503a5ff4..e54e72cd8 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -809,7 +809,10 @@ namespace MediaBrowser.Controller.Entities { if (filter == null || filter(child)) { - list.Add(child); + if (!child.IsHiddenFromUser(user)) + { + list.Add(child); + } } if (recursive && child.IsFolder) diff --git a/MediaBrowser.Model/Configuration/NotificationOptions.cs b/MediaBrowser.Model/Configuration/NotificationOptions.cs index 5aac1652f..dd523d947 100644 --- a/MediaBrowser.Model/Configuration/NotificationOptions.cs +++ b/MediaBrowser.Model/Configuration/NotificationOptions.cs @@ -1,6 +1,4 @@ -using System; -using System.Linq; -using MediaBrowser.Model.Extensions; +using MediaBrowser.Model.Extensions; namespace MediaBrowser.Model.Configuration { @@ -90,7 +88,7 @@ namespace MediaBrowser.Model.Configuration NotificationOption opt = GetOptions(notificationType); return opt == null || - !opt.DisabledServices.Contains(service, StringComparer.OrdinalIgnoreCase); + !ListHelper.ContainsIgnoreCase(opt.DisabledServices, service); } public bool IsEnabledToMonitorUser(string type, string userId) @@ -98,7 +96,7 @@ namespace MediaBrowser.Model.Configuration NotificationOption opt = GetOptions(type); return opt != null && opt.Enabled && - !opt.DisabledMonitorUsers.Contains(userId, StringComparer.OrdinalIgnoreCase); + !ListHelper.ContainsIgnoreCase(opt.DisabledMonitorUsers, userId); } public bool IsEnabledToSendToUser(string type, string userId, UserConfiguration userConfig) @@ -117,7 +115,7 @@ namespace MediaBrowser.Model.Configuration return true; } - return opt.SendToUsers.Contains(userId, StringComparer.OrdinalIgnoreCase); + return ListHelper.ContainsIgnoreCase(opt.SendToUsers, userId); } return false; diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs index 84ce2747a..24a41863e 100644 --- a/MediaBrowser.Model/Configuration/UserConfiguration.cs +++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs @@ -72,6 +72,7 @@ namespace MediaBrowser.Model.Configuration public UnratedItem[] BlockUnratedItems { get; set; } public SubtitlePlaybackMode SubtitleMode { get; set; } + public bool DisplayCollectionsView { get; set; } /// /// Initializes a new instance of the class. @@ -92,6 +93,8 @@ namespace MediaBrowser.Model.Configuration BlockUnratedItems = new UnratedItem[] { }; ExcludeFoldersFromGrouping = new string[] { }; + + DisplayCollectionsView = true; } } } diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs index 807e04427..deaa30714 100644 --- a/MediaBrowser.Model/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs @@ -115,7 +115,7 @@ namespace MediaBrowser.Model.Dlna public TranscodingProfile GetAudioTranscodingProfile(string container, string audioCodec) { - container = (container ?? string.Empty).TrimStart('.'); + container = StringHelper.TrimStart((container ?? string.Empty), '.'); foreach (var i in TranscodingProfiles) { @@ -141,7 +141,7 @@ namespace MediaBrowser.Model.Dlna public TranscodingProfile GetVideoTranscodingProfile(string container, string audioCodec, string videoCodec) { - container = (container ?? string.Empty).TrimStart('.'); + container = StringHelper.TrimStart((container ?? string.Empty), '.'); foreach (var i in TranscodingProfiles) { @@ -172,7 +172,7 @@ namespace MediaBrowser.Model.Dlna public ResponseProfile GetAudioMediaProfile(string container, string audioCodec, int? audioChannels, int? audioBitrate) { - container = (container ?? string.Empty).TrimStart('.'); + container = StringHelper.TrimStart((container ?? string.Empty), '.'); foreach (var i in ResponseProfiles) { @@ -217,7 +217,7 @@ namespace MediaBrowser.Model.Dlna public ResponseProfile GetImageMediaProfile(string container, int? width, int? height) { - container = (container ?? string.Empty).TrimStart('.'); + container = StringHelper.TrimStart((container ?? string.Empty), '.'); foreach (var i in ResponseProfiles) { @@ -270,7 +270,7 @@ namespace MediaBrowser.Model.Dlna TransportStreamTimestamp timestamp, bool? isAnamorphic) { - container = (container ?? string.Empty).TrimStart('.'); + container = StringHelper.TrimStart((container ?? string.Empty), '.'); foreach (var i in ResponseProfiles) { diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 877570844..8aadd428f 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -136,7 +136,12 @@ namespace MediaBrowser.Model.Dlna foreach (CodecProfile i in options.Profile.CodecProfiles) { if (i.Type == CodecType.Audio && i.ContainsCodec(audioCodec)) - conditions.AddRange(i.Conditions); + { + foreach (var c in i.Conditions) + { + conditions.Add(c); + } + } } int? audioChannels = audioStream.Channels; @@ -195,7 +200,12 @@ namespace MediaBrowser.Model.Dlna List audioTranscodingConditions = new List(); foreach (CodecProfile i in audioCodecProfiles) - audioTranscodingConditions.AddRange(i.Conditions); + { + foreach (var c in i.Conditions) + { + audioTranscodingConditions.Add(c); + } + } ApplyTranscodingConditions(playlistItem, audioTranscodingConditions); @@ -276,7 +286,10 @@ namespace MediaBrowser.Model.Dlna { if (i.Type == CodecType.Video && i.ContainsCodec(transcodingProfile.VideoCodec)) { - videoTranscodingConditions.AddRange(i.Conditions); + foreach (var c in i.Conditions) + { + videoTranscodingConditions.Add(c); + } break; } } @@ -287,7 +300,10 @@ namespace MediaBrowser.Model.Dlna { if (i.Type == CodecType.VideoAudio && i.ContainsCodec(transcodingProfile.AudioCodec)) { - audioTranscodingConditions.AddRange(i.Conditions); + foreach (var c in i.Conditions) + { + audioTranscodingConditions.Add(c); + } break; } } @@ -363,7 +379,10 @@ namespace MediaBrowser.Model.Dlna if (i.Type == DlnaProfileType.Video && ListHelper.ContainsIgnoreCase(i.GetContainers(), container)) { - conditions.AddRange(i.Conditions); + foreach (var c in i.Conditions) + { + conditions.Add(c); + } } } @@ -405,7 +424,12 @@ namespace MediaBrowser.Model.Dlna foreach (CodecProfile i in profile.CodecProfiles) { if (i.Type == CodecType.Video && i.ContainsCodec(videoCodec)) - conditions.AddRange(i.Conditions); + { + foreach (var c in i.Conditions) + { + conditions.Add(c); + } + } } foreach (ProfileCondition i in conditions) @@ -429,7 +453,12 @@ namespace MediaBrowser.Model.Dlna foreach (CodecProfile i in profile.CodecProfiles) { if (i.Type == CodecType.VideoAudio && i.ContainsCodec(audioCodec)) - conditions.AddRange(i.Conditions); + { + foreach (var c in i.Conditions) + { + conditions.Add(c); + } + } } foreach (ProfileCondition i in conditions) diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 8d507ae37..b8c256b25 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -305,8 +305,14 @@ namespace MediaBrowser.Model.Dlna { int? totalBitrate = TargetTotalBitrate; + double totalSeconds = RunTimeTicks.Value; + // Convert to ms + totalSeconds /= 10000; + // Convert to seconds + totalSeconds /= 1000; + return totalBitrate.HasValue ? - Convert.ToInt64(totalBitrate.Value * TimeSpan.FromTicks(RunTimeTicks.Value).TotalSeconds) : + Convert.ToInt64(totalBitrate.Value * totalSeconds) : (long?)null; } @@ -375,11 +381,14 @@ namespace MediaBrowser.Model.Dlna Height = videoStream.Height.Value }; + double? maxWidth = MaxWidth.HasValue ? (double)MaxWidth.Value : (double?)null; + double? maxHeight = MaxHeight.HasValue ? (double)MaxHeight.Value : (double?)null; + ImageSize newSize = DrawingUtils.Resize(size, null, null, - MaxWidth, - MaxHeight); + maxWidth, + maxHeight); return Convert.ToInt32(newSize.Width); } @@ -402,11 +411,14 @@ namespace MediaBrowser.Model.Dlna Height = videoStream.Height.Value }; + double? maxWidth = MaxWidth.HasValue ? (double)MaxWidth.Value : (double?)null; + double? maxHeight = MaxHeight.HasValue ? (double)MaxHeight.Value : (double?)null; + ImageSize newSize = DrawingUtils.Resize(size, null, null, - MaxWidth, - MaxHeight); + maxWidth, + maxHeight); return Convert.ToInt32(newSize.Height); } diff --git a/MediaBrowser.Model/Extensions/StringHelper.cs b/MediaBrowser.Model/Extensions/StringHelper.cs index 1c086655f..2eae56cd4 100644 --- a/MediaBrowser.Model/Extensions/StringHelper.cs +++ b/MediaBrowser.Model/Extensions/StringHelper.cs @@ -59,5 +59,16 @@ namespace MediaBrowser.Model.Extensions { return val.ToString(CultureInfo.InvariantCulture); } + + /// + /// Trims the start. + /// + /// The string. + /// The c. + /// System.String. + public static string TrimStart(string str, char c) + { + return str.TrimStart(c); + } } } diff --git a/MediaBrowser.Model/Notifications/Notification.cs b/MediaBrowser.Model/Notifications/Notification.cs index 731c3d303..5439b838d 100644 --- a/MediaBrowser.Model/Notifications/Notification.cs +++ b/MediaBrowser.Model/Notifications/Notification.cs @@ -19,10 +19,5 @@ namespace MediaBrowser.Model.Notifications public string Url { get; set; } public NotificationLevel Level { get; set; } - - public Notification() - { - Date = DateTime.UtcNow; - } } } diff --git a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs b/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs index a69f9e94b..d9323573d 100644 --- a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs +++ b/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs @@ -4,13 +4,13 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; +using MoreLinq; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MoreLinq; namespace MediaBrowser.Server.Implementations.Collections { @@ -27,6 +27,12 @@ namespace MediaBrowser.Server.Implementations.Collections _iLibraryMonitor = iLibraryMonitor; } + public Folder GetCollectionsFolder(string userId) + { + return _libraryManager.RootFolder.Children.Concat(_libraryManager.RootFolder.GetHiddenChildren()).OfType() + .FirstOrDefault(); + } + public async Task CreateCollection(CollectionCreationOptions options) { var name = options.Name; @@ -104,8 +110,7 @@ namespace MediaBrowser.Server.Implementations.Collections } } - return _libraryManager.RootFolder.Children.OfType().FirstOrDefault() ?? - _libraryManager.RootFolder.GetHiddenChildren().OfType().FirstOrDefault(); + return GetCollectionsFolder(string.Empty); } public async Task AddToCollection(Guid collectionId, IEnumerable ids) diff --git a/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs b/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs index c58b9181b..03479d838 100644 --- a/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs +++ b/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs @@ -28,6 +28,11 @@ namespace MediaBrowser.Server.Implementations.Collections } } + public override bool IsHiddenFromUser(User user) + { + return true; + } + public override string CollectionType { get { return Model.Entities.CollectionType.BoxSets; } diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs index 9ac82690b..24e4f0ef4 100644 --- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs @@ -62,28 +62,29 @@ namespace MediaBrowser.Server.Implementations.Library if (recursiveChildren.OfType().Any()) { - list.Add(await GetUserView(CollectionType.TvShows, user, cancellationToken).ConfigureAwait(false)); + list.Add(await GetUserView(CollectionType.TvShows, user, string.Empty, cancellationToken).ConfigureAwait(false)); } if (recursiveChildren.OfType().Any() || recursiveChildren.OfType().Any()) { - list.Add(await GetUserView(CollectionType.Music, user, cancellationToken).ConfigureAwait(false)); + list.Add(await GetUserView(CollectionType.Music, user, string.Empty, cancellationToken).ConfigureAwait(false)); } if (recursiveChildren.OfType().Any()) { - list.Add(await GetUserView(CollectionType.Movies, user, cancellationToken).ConfigureAwait(false)); + list.Add(await GetUserView(CollectionType.Movies, user, string.Empty, cancellationToken).ConfigureAwait(false)); } if (recursiveChildren.OfType().Any()) { - list.Add(await GetUserView(CollectionType.Games, user, cancellationToken).ConfigureAwait(false)); + list.Add(await GetUserView(CollectionType.Games, user, string.Empty, cancellationToken).ConfigureAwait(false)); } - if (recursiveChildren.OfType().Any()) + if (user.Configuration.DisplayCollectionsView || + recursiveChildren.OfType().Any()) { - list.Add(await GetUserView(CollectionType.BoxSets, user, cancellationToken).ConfigureAwait(false)); + list.Add(await GetUserView(CollectionType.BoxSets, user, "zzz_" + CollectionType.BoxSets, cancellationToken).ConfigureAwait(false)); } if (query.IncludeExternalContent) @@ -116,11 +117,11 @@ namespace MediaBrowser.Server.Implementations.Library return list.OrderBy(i => i.SortName); } - private Task GetUserView(string type, User user, CancellationToken cancellationToken) + private Task GetUserView(string type, User user, string sortName, CancellationToken cancellationToken) { var name = _localizationManager.GetLocalizedString("ViewType" + type); - return _libraryManager.GetNamedView(name, type, string.Empty, cancellationToken); + return _libraryManager.GetNamedView(name, type, sortName, cancellationToken); } } } -- cgit v1.2.3 From 3bef6ead9cec4c33d43b6348ae4fc33c9b70316a Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 1 Jul 2014 17:13:32 -0400 Subject: #514 - Support HLS seeking --- MediaBrowser.Api/BaseApiService.cs | 9 ++-- .../DefaultTheme/DefaultThemeService.cs | 2 +- MediaBrowser.Api/Playback/Hls/BaseHlsService.cs | 63 ++++++++++++++++------ MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 42 +++------------ MediaBrowser.Api/Playback/Hls/VideoHlsService.cs | 13 ++++- MediaBrowser.Api/SearchService.cs | 4 +- MediaBrowser.Api/SimilarItemsHelper.cs | 4 +- MediaBrowser.Api/UserLibrary/UserLibraryService.cs | 3 +- MediaBrowser.Controller/Entities/Folder.cs | 27 +--------- MediaBrowser.Controller/Entities/UserView.cs | 12 +++-- MediaBrowser.Model/ApiClient/IApiClient.cs | 7 +++ .../Configuration/UserConfiguration.cs | 2 - MediaBrowser.Model/Dto/StudioDto.cs | 9 +++- .../Dto/DtoService.cs | 7 ++- .../Library/SearchEngine.cs | 2 +- .../Library/UserViewManager.cs | 5 +- 16 files changed, 110 insertions(+), 101 deletions(-) (limited to 'MediaBrowser.Server.Implementations/Library/UserViewManager.cs') diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index 569e12530..f1d596213 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/MediaBrowser.Api/BaseApiService.cs @@ -159,7 +159,7 @@ namespace MediaBrowser.Api return libraryManager.GetPerson(DeSlugPersonName(name, libraryManager)); } - protected IList GetAllLibraryItems(Guid? userId, IUserManager userManager, ILibraryManager libraryManager, string parentId = null) + protected IEnumerable GetAllLibraryItems(Guid? userId, IUserManager userManager, ILibraryManager libraryManager, string parentId = null) { if (!string.IsNullOrEmpty(parentId)) { @@ -169,7 +169,7 @@ namespace MediaBrowser.Api { var user = userManager.GetUserById(userId.Value); - return folder.GetRecursiveChildren(user).ToList(); + return folder.GetRecursiveChildren(user); } return folder.GetRecursiveChildren(); @@ -178,7 +178,7 @@ namespace MediaBrowser.Api { var user = userManager.GetUserById(userId.Value); - return userManager.GetUserById(userId.Value).RootFolder.GetRecursiveChildren(user, null); + return userManager.GetUserById(userId.Value).RootFolder.GetRecursiveChildren(user); } return libraryManager.RootFolder.GetRecursiveChildren(); @@ -239,7 +239,8 @@ namespace MediaBrowser.Api return name; } - return libraryManager.RootFolder.GetRecursiveChildren(i => i is Game) + return libraryManager.RootFolder.GetRecursiveChildren() + .OfType() .SelectMany(i => i.Genres) .Distinct(StringComparer.OrdinalIgnoreCase) .FirstOrDefault(i => diff --git a/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs b/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs index 6acecd342..8bc867a2e 100644 --- a/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs +++ b/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs @@ -381,7 +381,7 @@ namespace MediaBrowser.Api.DefaultTheme var currentUser1 = user; var ownedEpisodes = series - .SelectMany(i => i.GetRecursiveChildren(currentUser1, j => j.LocationType != LocationType.Virtual)) + .SelectMany(i => i.GetRecursiveChildren(currentUser1).Where(j => j.LocationType != LocationType.Virtual)) .OfType() .ToList(); diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 57c2244c7..fa78fa020 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -59,10 +59,11 @@ namespace MediaBrowser.Api.Playback.Hls /// Processes the request. /// /// The request. + /// if set to true [is live]. /// System.Object. - protected object ProcessRequest(StreamRequest request) + protected object ProcessRequest(StreamRequest request, bool isLive) { - return ProcessRequestAsync(request).Result; + return ProcessRequestAsync(request, isLive).Result; } private static readonly SemaphoreSlim FfmpegStartLock = new SemaphoreSlim(1, 1); @@ -70,13 +71,12 @@ namespace MediaBrowser.Api.Playback.Hls /// Processes the request async. /// /// The request. + /// if set to true [is live]. /// Task{System.Object}. - /// - /// A video bitrate is required + /// A video bitrate is required /// or - /// An audio bitrate is required - /// - private async Task ProcessRequestAsync(StreamRequest request) + /// An audio bitrate is required + private async Task ProcessRequestAsync(StreamRequest request, bool isLive) { var cancellationTokenSource = new CancellationTokenSource(); @@ -110,7 +110,8 @@ namespace MediaBrowser.Api.Playback.Hls throw; } - await WaitForMinimumSegmentCount(playlist, GetSegmentWait(), cancellationTokenSource.Token).ConfigureAwait(false); + var waitCount = isLive ? 1 : GetSegmentWait(); + await WaitForMinimumSegmentCount(playlist, waitCount, cancellationTokenSource.Token).ConfigureAwait(false); } } finally @@ -119,6 +120,22 @@ namespace MediaBrowser.Api.Playback.Hls } } + if (isLive) + { + //var file = request.PlaylistId + Path.GetExtension(Request.PathInfo); + + //file = Path.Combine(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, file); + + try + { + return ResultFactory.GetStaticFileResult(Request, playlist, FileShare.ReadWrite); + } + finally + { + ApiEntryPoint.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls); + } + } + var audioBitrate = state.OutputAudioBitrate ?? 0; var videoBitrate = state.OutputVideoBitrate ?? 0; @@ -188,16 +205,18 @@ namespace MediaBrowser.Api.Playback.Hls protected async Task WaitForMinimumSegmentCount(string playlist, int segmentCount, CancellationToken cancellationToken) { - var count = 0; + Logger.Debug("Waiting for {0} segments in {1}", segmentCount, playlist); - // Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written - using (var fileStream = FileSystem.GetFileStream(playlist, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) + while (true) { - using (var reader = new StreamReader(fileStream)) + // Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written + using (var fileStream = FileSystem.GetFileStream(playlist, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true)) { - while (true) + using (var reader = new StreamReader(fileStream)) { - if (!reader.EndOfStream) + var count = 0; + + while (!reader.EndOfStream) { var line = await reader.ReadLineAsync().ConfigureAwait(false); @@ -206,11 +225,12 @@ namespace MediaBrowser.Api.Playback.Hls count++; if (count >= segmentCount) { + Logger.Debug("Finished waiting for {0} segments in {1}", segmentCount, playlist); return; } } } - await Task.Delay(25, cancellationToken).ConfigureAwait(false); + await Task.Delay(100, cancellationToken).ConfigureAwait(false); } } } @@ -229,7 +249,7 @@ namespace MediaBrowser.Api.Playback.Hls var itsOffsetMs = hlsVideoRequest == null ? 0 - : ((GetHlsVideoStream)state.VideoRequest).TimeStampOffsetMs; + : hlsVideoRequest.TimeStampOffsetMs; var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds.ToString(UsCulture)); @@ -240,7 +260,15 @@ namespace MediaBrowser.Api.Playback.Hls // If isEncoding is true we're actually starting ffmpeg var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0"; - var args = string.Format("{0} {1} -i {2} -map_metadata -1 -threads {3} {4} {5} -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9} -y \"{10}\"", + var baseUrlParam = string.Empty; + + if (state.Request is GetLiveHlsStream) + { + baseUrlParam = string.Format(" -hls_base_url \"{0}/\"", + "hls/" + Path.GetFileNameWithoutExtension(outputPath)); + } + + var args = string.Format("{0} {1} -i {2} -map_metadata -1 -threads {3} {4} {5} -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9}{10} -y \"{11}\"", itsOffset, inputModifier, GetInputArgument(state), @@ -251,6 +279,7 @@ namespace MediaBrowser.Api.Playback.Hls state.SegmentLength.ToString(UsCulture), startNumberParam, state.HlsListSize.ToString(UsCulture), + baseUrlParam, outputPath ).Trim(); diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index bde2c5694..5bb610686 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -22,11 +22,6 @@ namespace MediaBrowser.Api.Playback.Hls [Api(Description = "Gets a video stream using HTTP live streaming.")] public class GetMasterHlsVideoStream : VideoStreamRequest { - [ApiMember(Name = "BaselineStreamAudioBitRate", Description = "Optional. Specify the audio bitrate for the baseline stream.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? BaselineStreamAudioBitRate { get; set; } - - [ApiMember(Name = "AppendBaselineStream", Description = "Optional. Whether or not to include a baseline audio-only stream in the master playlist.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool AppendBaselineStream { get; set; } } [Route("/Videos/{Id}/main.m3u8", "GET")] @@ -35,12 +30,6 @@ namespace MediaBrowser.Api.Playback.Hls { } - [Route("/Videos/{Id}/baseline.m3u8", "GET")] - [Api(Description = "Gets a video stream using HTTP live streaming.")] - public class GetBaselineHlsVideoStream : VideoStreamRequest - { - } - /// /// Class GetHlsVideoSegment /// @@ -73,16 +62,11 @@ namespace MediaBrowser.Api.Playback.Hls public object Get(GetDynamicHlsVideoSegment request) { - if (string.Equals("baseline", request.PlaylistId, StringComparison.OrdinalIgnoreCase)) - { - return GetDynamicSegment(request, false).Result; - } - - return GetDynamicSegment(request, true).Result; + return GetDynamicSegment(request).Result; } private static readonly SemaphoreSlim FfmpegStartLock = new SemaphoreSlim(1, 1); - private async Task GetDynamicSegment(GetDynamicHlsVideoSegment request, bool isMain) + private async Task GetDynamicSegment(GetDynamicHlsVideoSegment request) { if ((request.StartTimeTicks ?? 0) > 0) { @@ -322,7 +306,9 @@ namespace MediaBrowser.Api.Playback.Hls var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex); // Main stream - var playlistUrl = "main.m3u8" + queryString; + var playlistUrl = (state.RunTimeTicks ?? 0) > 0 ? "main.m3u8" : "live.m3u8"; + playlistUrl += queryString; + AppendPlaylist(builder, playlistUrl, totalBitrate); if (state.VideoRequest.VideoBitRate.HasValue) @@ -385,13 +371,6 @@ namespace MediaBrowser.Api.Playback.Hls return result; } - public object Get(GetBaselineHlsVideoStream request) - { - var result = GetPlaylistAsync(request, "baseline").Result; - - return result; - } - private async Task GetPlaylistAsync(VideoStreamRequest request, string name) { var state = await GetState(request, CancellationToken.None).ConfigureAwait(false); @@ -506,14 +485,6 @@ namespace MediaBrowser.Api.Playback.Hls /// System.String. protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding) { - var hlsVideoRequest = state.VideoRequest as GetHlsVideoStream; - - var itsOffsetMs = hlsVideoRequest == null - ? 0 - : ((GetHlsVideoStream)state.VideoRequest).TimeStampOffsetMs; - - var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds.ToString(UsCulture)); - var threads = GetNumberOfThreads(state, false); var inputModifier = GetInputModifier(state); @@ -521,8 +492,7 @@ namespace MediaBrowser.Api.Playback.Hls // If isEncoding is true we're actually starting ffmpeg var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0"; - var args = string.Format("{0} {1} -i {2} -map_metadata -1 -threads {3} {4} {5} -flags -global_header {6} -hls_time {7} -start_number {8} -hls_list_size {9} -y \"{10}\"", - itsOffset, + var args = string.Format("{0} -i {1} -map_metadata -1 -threads {2} {3} {4} -flags -global_header {5} -hls_time {6} -start_number {7} -hls_list_size {8} -y \"{9}\"", inputModifier, GetInputArgument(state), threads, diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index 1a925378b..28c0219fc 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -32,6 +32,12 @@ namespace MediaBrowser.Api.Playback.Hls public int TimeStampOffsetMs { get; set; } } + [Route("/Videos/{Id}/live.m3u8", "GET")] + [Api(Description = "Gets a video stream using HTTP live streaming.")] + public class GetLiveHlsStream : VideoStreamRequest + { + } + /// /// Class GetHlsVideoSegment /// @@ -105,7 +111,12 @@ namespace MediaBrowser.Api.Playback.Hls /// System.Object. public object Get(GetHlsVideoStream request) { - return ProcessRequest(request); + return ProcessRequest(request, false); + } + + public object Get(GetLiveHlsStream request) + { + return ProcessRequest(request, true); } /// diff --git a/MediaBrowser.Api/SearchService.cs b/MediaBrowser.Api/SearchService.cs index 9fcbe39eb..e72edcc98 100644 --- a/MediaBrowser.Api/SearchService.cs +++ b/MediaBrowser.Api/SearchService.cs @@ -192,14 +192,14 @@ namespace MediaBrowser.Api { result.Series = season.Series.Name; - result.EpisodeCount = season.GetRecursiveChildren(i => i is Episode).Count; + result.EpisodeCount = season.GetRecursiveChildren().Count(i => i is Episode); } var series = item as Series; if (series != null) { - result.EpisodeCount = series.GetRecursiveChildren(i => i is Episode).Count; + result.EpisodeCount = series.GetRecursiveChildren().Count(i => i is Episode); } var album = item as MusicAlbum; diff --git a/MediaBrowser.Api/SimilarItemsHelper.cs b/MediaBrowser.Api/SimilarItemsHelper.cs index 660b55f30..2da29928b 100644 --- a/MediaBrowser.Api/SimilarItemsHelper.cs +++ b/MediaBrowser.Api/SimilarItemsHelper.cs @@ -78,8 +78,8 @@ namespace MediaBrowser.Api var fields = request.GetItemFields().ToList(); var inputItems = user == null - ? libraryManager.RootFolder.GetRecursiveChildren(i => i.Id != item.Id) - : user.RootFolder.GetRecursiveChildren(user, i => i.Id != item.Id); + ? libraryManager.RootFolder.GetRecursiveChildren().Where(i => i.Id != item.Id) + : user.RootFolder.GetRecursiveChildren(user).Where(i => i.Id != item.Id); var items = GetSimilaritems(item, inputItems.Where(includeInSearch), getSimilarityScore) .ToList(); diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index 96bbd6dff..a1625d052 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -541,7 +541,8 @@ namespace MediaBrowser.Api.UserLibrary if (series != null) { var dtos = series - .GetRecursiveChildren(i => i is Episode && i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0) + .GetRecursiveChildren() + .Where(i => i is Episode && i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0) .OrderBy(i => { if (i.PremiereDate.HasValue) diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index c712d50af..52c414ae8 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -856,19 +856,6 @@ namespace MediaBrowser.Controller.Entities /// IEnumerable{BaseItem}. /// public IEnumerable GetRecursiveChildren(User user, bool includeLinkedChildren = true) - { - return GetRecursiveChildren(user, null, includeLinkedChildren); - } - - /// - /// Gets the recursive children. - /// - /// The user. - /// The filter. - /// if set to true [include linked children]. - /// IList{BaseItem}. - /// - public IList GetRecursiveChildren(User user, Func filter, bool includeLinkedChildren = true) { if (user == null) { @@ -877,7 +864,7 @@ namespace MediaBrowser.Controller.Entities var list = new List(); - var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, true, filter); + var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, true, null); return hasLinkedChildren ? list.DistinctBy(i => i.Id).ToList() : list; } @@ -887,20 +874,10 @@ namespace MediaBrowser.Controller.Entities /// /// IList{BaseItem}. public IList GetRecursiveChildren() - { - return GetRecursiveChildren(i => true); - } - - /// - /// Gets the recursive children. - /// - /// The filter. - /// IEnumerable{BaseItem}. - public IList GetRecursiveChildren(Func filter) { var list = new List(); - AddChildrenToList(list, true, filter); + AddChildrenToList(list, true, null); return list; } diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index ce188554c..86099fdc0 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -18,11 +18,17 @@ namespace MediaBrowser.Controller.Entities switch (ViewType) { case CollectionType.Games: - return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)).OfType(); + return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)) + .OfType(); case CollectionType.BoxSets: - return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)).OfType(); + return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)) + .OfType(); case CollectionType.TvShows: - return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)).OfType(); + return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)) + .OfType(); + case CollectionType.Trailers: + return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)) + .OfType(); default: return mediaFolders.SelectMany(i => i.GetChildren(user, includeLinkedChildren)); } diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs index 4f008571c..33c6f980c 100644 --- a/MediaBrowser.Model/ApiClient/IApiClient.cs +++ b/MediaBrowser.Model/ApiClient/IApiClient.cs @@ -217,6 +217,7 @@ namespace MediaBrowser.Model.ApiClient /// Gets the additional parts. /// /// The item identifier. + /// The user identifier. /// Task{BaseItemDto[]}. Task GetAdditionalParts(string itemId, string userId); @@ -241,6 +242,12 @@ namespace MediaBrowser.Model.ApiClient /// Task{SessionInfoDto[]}. Task GetClientSessionsAsync(SessionQuery query); + /// + /// Gets the client session asynchronous. + /// + /// Task{SessionInfoDto}. + Task GetCurrentSessionAsync(); + /// /// Gets the item counts async. /// diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs index 24a41863e..0834452fb 100644 --- a/MediaBrowser.Model/Configuration/UserConfiguration.cs +++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs @@ -93,8 +93,6 @@ namespace MediaBrowser.Model.Configuration BlockUnratedItems = new UnratedItem[] { }; ExcludeFoldersFromGrouping = new string[] { }; - - DisplayCollectionsView = true; } } } diff --git a/MediaBrowser.Model/Dto/StudioDto.cs b/MediaBrowser.Model/Dto/StudioDto.cs index 4f21784fd..a0027cc4e 100644 --- a/MediaBrowser.Model/Dto/StudioDto.cs +++ b/MediaBrowser.Model/Dto/StudioDto.cs @@ -1,5 +1,4 @@ -using System; -using System.ComponentModel; +using System.ComponentModel; using System.Diagnostics; using System.Runtime.Serialization; @@ -17,6 +16,12 @@ namespace MediaBrowser.Model.Dto /// The name. public string Name { get; set; } + /// + /// Gets or sets the identifier. + /// + /// The identifier. + public string Id { get; set; } + /// /// Gets or sets the primary image tag. /// diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 7f63dac33..73216ca33 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -328,7 +328,8 @@ namespace MediaBrowser.Server.Implementations.Dto if (!string.IsNullOrEmpty(item.Album)) { var parentAlbum = _libraryManager.RootFolder - .GetRecursiveChildren(i => i is MusicAlbum) + .GetRecursiveChildren() + .Where(i => i is MusicAlbum) .FirstOrDefault(i => string.Equals(i.Name, item.Album, StringComparison.OrdinalIgnoreCase)); if (parentAlbum != null) @@ -539,6 +540,7 @@ namespace MediaBrowser.Server.Implementations.Dto if (dictionary.TryGetValue(studio, out entity)) { + studioDto.Id = entity.Id.ToString("N"); studioDto.PrimaryImageTag = GetImageCacheTag(entity, ImageType.Primary); } @@ -1248,7 +1250,8 @@ namespace MediaBrowser.Server.Implementations.Dto } else { - children = folder.GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual); + children = folder.GetRecursiveChildren(user) + .Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual); } // Loop through each recursive child diff --git a/MediaBrowser.Server.Implementations/Library/SearchEngine.cs b/MediaBrowser.Server.Implementations/Library/SearchEngine.cs index 4c65fad68..6faa72b81 100644 --- a/MediaBrowser.Server.Implementations/Library/SearchEngine.cs +++ b/MediaBrowser.Server.Implementations/Library/SearchEngine.cs @@ -41,7 +41,7 @@ namespace MediaBrowser.Server.Implementations.Library { var user = _userManager.GetUserById(new Guid(query.UserId)); - inputItems = user.RootFolder.GetRecursiveChildren(user, null); + inputItems = user.RootFolder.GetRecursiveChildren(user, true); } diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs index 24e4f0ef4..dd2978b17 100644 --- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Querying; namespace MediaBrowser.Server.Implementations.Library { @@ -84,7 +85,7 @@ namespace MediaBrowser.Server.Implementations.Library if (user.Configuration.DisplayCollectionsView || recursiveChildren.OfType().Any()) { - list.Add(await GetUserView(CollectionType.BoxSets, user, "zzz_" + CollectionType.BoxSets, cancellationToken).ConfigureAwait(false)); + list.Add(await GetUserView(CollectionType.BoxSets, user, CollectionType.BoxSets, cancellationToken).ConfigureAwait(false)); } if (query.IncludeExternalContent) @@ -114,7 +115,7 @@ namespace MediaBrowser.Server.Implementations.Library } } - return list.OrderBy(i => i.SortName); + return _libraryManager.Sort(list, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).Cast(); } private Task GetUserView(string type, User user, string sortName, CancellationToken cancellationToken) -- cgit v1.2.3 From d7f3214b3251a210d784e86fcdcfdca9765e69b8 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 13 Jul 2014 17:03:57 -0400 Subject: move last fm providers to a plugin --- .../Channels/ChannelMediaInfo.cs | 2 + .../Configuration/UserConfiguration.cs | 1 + .../MediaBrowser.Providers.csproj | 4 - MediaBrowser.Providers/MediaInfo/FFProbeHelpers.cs | 5 + .../Music/AudioDbAlbumProvider.cs | 3 + .../Music/FanArtAlbumProvider.cs | 2 +- .../Music/FanArtArtistProvider.cs | 4 +- .../Music/LastFmImageProvider.cs | 164 ----------- .../Music/LastfmAlbumProvider.cs | 322 --------------------- .../Music/LastfmArtistProvider.cs | 160 ---------- MediaBrowser.Providers/Music/LastfmHelper.cs | 76 ----- .../Collections/ManualCollectionsFolder.cs | 2 +- .../Library/UserViewManager.cs | 4 +- 13 files changed, 17 insertions(+), 732 deletions(-) delete mode 100644 MediaBrowser.Providers/Music/LastFmImageProvider.cs delete mode 100644 MediaBrowser.Providers/Music/LastfmAlbumProvider.cs delete mode 100644 MediaBrowser.Providers/Music/LastfmArtistProvider.cs delete mode 100644 MediaBrowser.Providers/Music/LastfmHelper.cs (limited to 'MediaBrowser.Server.Implementations/Library/UserViewManager.cs') diff --git a/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs b/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs index 64b480414..f16fd1120 100644 --- a/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs +++ b/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs @@ -29,6 +29,8 @@ namespace MediaBrowser.Controller.Channels public MediaProtocol Protocol { get; set; } + public long? RunTimeTicks { get; set; } + public ChannelMediaInfo() { RequiredHttpHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs index 0834452fb..1afc9a191 100644 --- a/MediaBrowser.Model/Configuration/UserConfiguration.cs +++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs @@ -93,6 +93,7 @@ namespace MediaBrowser.Model.Configuration BlockUnratedItems = new UnratedItem[] { }; ExcludeFoldersFromGrouping = new string[] { }; + DisplayCollectionsView = true; } } } diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index fe22d4c99..9a26101a1 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -126,7 +126,6 @@ - @@ -138,11 +137,8 @@ - - - diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeHelpers.cs b/MediaBrowser.Providers/MediaInfo/FFProbeHelpers.cs index 49c7ebec8..2044979e4 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeHelpers.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeHelpers.cs @@ -12,6 +12,11 @@ namespace MediaBrowser.Providers.MediaInfo /// The result. public static void NormalizeFFProbeResult(InternalMediaInfoResult result) { + if (result == null) + { + throw new ArgumentNullException("result"); + } + if (result.format != null && result.format.tags != null) { result.format.tags = ConvertDictionaryToCaseInSensitive(result.format.tags); diff --git a/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs b/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs index a9b3d8e11..ab9ac2331 100644 --- a/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs +++ b/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs @@ -1,4 +1,5 @@ using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; @@ -86,6 +87,8 @@ namespace MediaBrowser.Providers.Music item.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, result.strMusicBrainzArtistID); item.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, result.strMusicBrainzID); + + item.Overview = (result.strDescriptionEN ?? string.Empty).StripHtml(); } public string Name diff --git a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs index ccab30bc6..94d682f44 100644 --- a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs +++ b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs @@ -67,7 +67,7 @@ namespace MediaBrowser.Providers.Music if (!string.IsNullOrEmpty(artistMusicBrainzId)) { - await FanartArtistProvider.Current.EnsureMovieXml(artistMusicBrainzId, cancellationToken).ConfigureAwait(false); + await FanartArtistProvider.Current.EnsureArtistXml(artistMusicBrainzId, cancellationToken).ConfigureAwait(false); var artistXmlPath = FanartArtistProvider.GetArtistXmlPath(_config.CommonApplicationPaths, artistMusicBrainzId); diff --git a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs index b9916dfba..a8df95fd1 100644 --- a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs +++ b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs @@ -79,7 +79,7 @@ namespace MediaBrowser.Providers.Music if (!String.IsNullOrEmpty(artistMusicBrainzId)) { - await EnsureMovieXml(artistMusicBrainzId, cancellationToken).ConfigureAwait(false); + await EnsureArtistXml(artistMusicBrainzId, cancellationToken).ConfigureAwait(false); var artistXmlPath = GetArtistXmlPath(_config.CommonApplicationPaths, artistMusicBrainzId); @@ -390,7 +390,7 @@ namespace MediaBrowser.Providers.Music } private readonly Task _cachedTask = Task.FromResult(true); - internal Task EnsureMovieXml(string musicBrainzId, CancellationToken cancellationToken) + internal Task EnsureArtistXml(string musicBrainzId, CancellationToken cancellationToken) { var xmlPath = GetArtistXmlPath(_config.ApplicationPaths, musicBrainzId); diff --git a/MediaBrowser.Providers/Music/LastFmImageProvider.cs b/MediaBrowser.Providers/Music/LastFmImageProvider.cs deleted file mode 100644 index b55a97351..000000000 --- a/MediaBrowser.Providers/Music/LastFmImageProvider.cs +++ /dev/null @@ -1,164 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Providers; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Providers.Music -{ - public class LastfmImageProvider : IRemoteImageProvider, IHasOrder - { - private readonly IHttpClient _httpClient; - private readonly IServerConfigurationManager _config; - - public LastfmImageProvider(IHttpClient httpClient, IServerConfigurationManager config) - { - _httpClient = httpClient; - _config = config; - } - - public string Name - { - get { return ProviderName; } - } - - public static string ProviderName - { - get { return "last.fm"; } - } - - public bool Supports(IHasImages item) - { - return item is MusicAlbum || item is MusicArtist; - } - - public IEnumerable GetSupportedImages(IHasImages item) - { - return new List - { - ImageType.Primary - }; - } - - public Task> GetImages(IHasImages item, CancellationToken cancellationToken) - { - var list = new List(); - - RemoteImageInfo info = null; - - var musicBrainzId = item is MusicAlbum ? - item.GetProviderId(MetadataProviders.MusicBrainzAlbum) : - item.GetProviderId(MetadataProviders.MusicBrainzArtist); - - if (!string.IsNullOrEmpty(musicBrainzId)) - { - var cachePath = Path.Combine(_config.ApplicationPaths.CachePath, "lastfm", musicBrainzId, "image.txt"); - - try - { - var parts = File.ReadAllText(cachePath).Split('|'); - - info = GetInfo(parts.FirstOrDefault(), parts.LastOrDefault()); - } - catch (DirectoryNotFoundException) - { - } - catch (FileNotFoundException) - { - } - } - - if (info == null) - { - var musicBrainzReleaseGroupId = item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup); - - if (!string.IsNullOrEmpty(musicBrainzReleaseGroupId)) - { - var cachePath = Path.Combine(_config.ApplicationPaths.CachePath, "lastfm", musicBrainzReleaseGroupId, "image.txt"); - - try - { - var parts = File.ReadAllText(cachePath).Split('|'); - - info = GetInfo(parts.FirstOrDefault(), parts.LastOrDefault()); - } - catch (DirectoryNotFoundException) - { - } - catch (FileNotFoundException) - { - } - } - } - - if (info != null) - { - list.Add(info); - } - - // The only info we have is size - return Task.FromResult>(list.OrderByDescending(i => i.Width ?? 0)); - } - - private RemoteImageInfo GetInfo(string url, string size) - { - if (string.IsNullOrEmpty(url)) - { - return null; - } - - var info = new RemoteImageInfo - { - ProviderName = Name, - Url = url, - Type = ImageType.Primary - }; - - if (string.Equals(size, "mega", StringComparison.OrdinalIgnoreCase)) - { - - } - else if (string.Equals(size, "extralarge", StringComparison.OrdinalIgnoreCase)) - { - - } - else if (string.Equals(size, "large", StringComparison.OrdinalIgnoreCase)) - { - - } - else if (string.Equals(size, "medium", StringComparison.OrdinalIgnoreCase)) - { - - } - - return info; - } - - public int Order - { - get - { - // After all others - return 3; - } - } - - public Task GetImageResponse(string url, CancellationToken cancellationToken) - { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url, - ResourcePool = LastfmArtistProvider.LastfmResourcePool - }); - } - } -} diff --git a/MediaBrowser.Providers/Music/LastfmAlbumProvider.cs b/MediaBrowser.Providers/Music/LastfmAlbumProvider.cs deleted file mode 100644 index 846539748..000000000 --- a/MediaBrowser.Providers/Music/LastfmAlbumProvider.cs +++ /dev/null @@ -1,322 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Serialization; -using MoreLinq; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Providers.Music -{ - public class LastfmAlbumProvider : IRemoteMetadataProvider, IHasOrder - { - private readonly IJsonSerializer _json; - private readonly IHttpClient _httpClient; - - private readonly IServerConfigurationManager _config; - private readonly ILogger _logger; - - public LastfmAlbumProvider(IHttpClient httpClient, IJsonSerializer json, IServerConfigurationManager config, ILogger logger) - { - _httpClient = httpClient; - _json = json; - _config = config; - _logger = logger; - } - - public async Task> GetSearchResults(AlbumInfo searchInfo, CancellationToken cancellationToken) - { - return new List(); - } - - public async Task> GetMetadata(AlbumInfo id, CancellationToken cancellationToken) - { - var result = new MetadataResult(); - - var lastFmData = await GetAlbumResult(id, cancellationToken).ConfigureAwait(false); - - if (lastFmData != null && lastFmData.album != null) - { - result.HasMetadata = true; - result.Item = new MusicAlbum(); - ProcessAlbumData(result.Item, lastFmData.album); - } - - return result; - } - - private async Task GetAlbumResult(AlbumInfo item, CancellationToken cancellationToken) - { - // Try album release Id - var id = item.GetReleaseId(); - if (!string.IsNullOrEmpty(id)) - { - var result = await GetAlbumResult(id, cancellationToken).ConfigureAwait(false); - - if (result != null && result.album != null) - { - return result; - } - } - - // Try album release group Id - id = item.GetReleaseGroupId(); - if (!string.IsNullOrEmpty(id)) - { - var result = await GetAlbumResult(id, cancellationToken).ConfigureAwait(false); - - if (result != null && result.album != null) - { - return result; - } - } - - var albumArtist = item.GetAlbumArtist(); - // Get each song, distinct by the combination of AlbumArtist and Album - var songs = item.SongInfos.DistinctBy(i => (i.AlbumArtists.FirstOrDefault() ?? string.Empty) + (i.Album ?? string.Empty), StringComparer.OrdinalIgnoreCase).ToList(); - - foreach (var song in songs.Where(song => !string.IsNullOrEmpty(song.Album) && !string.IsNullOrEmpty(song.AlbumArtists.FirstOrDefault()))) - { - var result = await GetAlbumResult(song.AlbumArtists.FirstOrDefault(), song.Album, cancellationToken).ConfigureAwait(false); - - if (result != null && result.album != null) - { - return result; - } - } - - if (string.IsNullOrEmpty(albumArtist)) - { - return null; - } - - return await GetAlbumResult(albumArtist, item.Name, cancellationToken); - } - - private async Task GetAlbumResult(string artist, string album, CancellationToken cancellationToken) - { - // Get albu info using artist and album name - var url = LastfmArtistProvider.RootUrl + string.Format("method=album.getInfo&artist={0}&album={1}&api_key={2}&format=json", UrlEncode(artist), UrlEncode(album), LastfmArtistProvider.ApiKey); - - using (var json = await _httpClient.Get(new HttpRequestOptions - { - Url = url, - ResourcePool = LastfmArtistProvider.LastfmResourcePool, - CancellationToken = cancellationToken, - EnableHttpCompression = false - - }).ConfigureAwait(false)) - { - using (var reader = new StreamReader(json)) - { - var jsonText = await reader.ReadToEndAsync().ConfigureAwait(false); - - // Fix their bad json - jsonText = jsonText.Replace("\"#text\"", "\"url\""); - - return _json.DeserializeFromString(jsonText); - } - } - } - - private async Task GetAlbumResult(string musicbraizId, CancellationToken cancellationToken) - { - // Get albu info using artist and album name - var url = LastfmArtistProvider.RootUrl + string.Format("method=album.getInfo&mbid={0}&api_key={1}&format=json", musicbraizId, LastfmArtistProvider.ApiKey); - - using (var json = await _httpClient.Get(new HttpRequestOptions - { - Url = url, - ResourcePool = LastfmArtistProvider.LastfmResourcePool, - CancellationToken = cancellationToken, - EnableHttpCompression = false - - }).ConfigureAwait(false)) - { - return _json.DeserializeFromStream(json); - } - } - - private void ProcessAlbumData(MusicAlbum item, LastfmAlbum data) - { - var overview = data.wiki != null ? data.wiki.content : null; - - if (!item.LockedFields.Contains(MetadataFields.Overview)) - { - item.Overview = overview; - } - - // Only grab the date here if the album doesn't already have one, since id3 tags are preferred - DateTime release; - - if (DateTime.TryParse(data.releasedate, out release)) - { - // Lastfm sends back null as sometimes 1901, other times 0 - if (release.Year > 1901) - { - if (!item.PremiereDate.HasValue) - { - item.PremiereDate = release; - } - - if (!item.ProductionYear.HasValue) - { - item.ProductionYear = release.Year; - } - } - } - - string imageSize; - var url = LastfmHelper.GetImageUrl(data, out imageSize); - - var musicBrainzId = item.GetProviderId(MetadataProviders.MusicBrainzAlbum) ?? - item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup); - - if (!string.IsNullOrEmpty(musicBrainzId) && !string.IsNullOrEmpty(url)) - { - LastfmHelper.SaveImageInfo(_config.ApplicationPaths, _logger, musicBrainzId, url, imageSize); - } - } - - /// - /// Encodes an URL. - /// - /// The name. - /// System.String. - private string UrlEncode(string name) - { - return WebUtility.UrlEncode(name); - } - - public string Name - { - get { return "last.fm"; } - } - - public int Order - { - get - { - // After fanart & audiodb - return 2; - } - } - - public Task GetImageResponse(string url, CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } - } - - #region Result Objects - - public class LastfmStats - { - public string listeners { get; set; } - public string playcount { get; set; } - } - - public class LastfmTag - { - public string name { get; set; } - public string url { get; set; } - } - - - public class LastfmTags - { - public List tag { get; set; } - } - - public class LastfmFormationInfo - { - public string yearfrom { get; set; } - public string yearto { get; set; } - } - - public class LastFmBio - { - public string published { get; set; } - public string summary { get; set; } - public string content { get; set; } - public string placeformed { get; set; } - public string yearformed { get; set; } - public List formationlist { get; set; } - } - - public class LastFmImage - { - public string url { get; set; } - public string size { get; set; } - } - - public class LastfmArtist : IHasLastFmImages - { - public string name { get; set; } - public string mbid { get; set; } - public string url { get; set; } - public string streamable { get; set; } - public string ontour { get; set; } - public LastfmStats stats { get; set; } - public List similar { get; set; } - public LastfmTags tags { get; set; } - public LastFmBio bio { get; set; } - public List image { get; set; } - } - - - public class LastfmAlbum : IHasLastFmImages - { - public string name { get; set; } - public string artist { get; set; } - public string id { get; set; } - public string mbid { get; set; } - public string releasedate { get; set; } - public int listeners { get; set; } - public int playcount { get; set; } - public LastfmTags toptags { get; set; } - public LastFmBio wiki { get; set; } - public List image { get; set; } - } - - public interface IHasLastFmImages - { - List image { get; set; } - } - - public class LastfmGetAlbumResult - { - public LastfmAlbum album { get; set; } - } - - public class LastfmGetArtistResult - { - public LastfmArtist artist { get; set; } - } - - public class Artistmatches - { - public List artist { get; set; } - } - - public class LastfmArtistSearchResult - { - public Artistmatches artistmatches { get; set; } - } - - public class LastfmArtistSearchResults - { - public LastfmArtistSearchResult results { get; set; } - } - - #endregion -} diff --git a/MediaBrowser.Providers/Music/LastfmArtistProvider.cs b/MediaBrowser.Providers/Music/LastfmArtistProvider.cs deleted file mode 100644 index a2aa9d719..000000000 --- a/MediaBrowser.Providers/Music/LastfmArtistProvider.cs +++ /dev/null @@ -1,160 +0,0 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Serialization; -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Providers.Music -{ - public class LastfmArtistProvider : IRemoteMetadataProvider, IHasOrder - { - private readonly IJsonSerializer _json; - private readonly IHttpClient _httpClient; - - internal static readonly SemaphoreSlim LastfmResourcePool = new SemaphoreSlim(4, 4); - - internal const string RootUrl = @"http://ws.audioscrobbler.com/2.0/?"; - internal static string ApiKey = "7b76553c3eb1d341d642755aecc40a33"; - - private readonly IServerConfigurationManager _config; - private readonly ILogger _logger; - - public LastfmArtistProvider(IHttpClient httpClient, IJsonSerializer json, IServerConfigurationManager config, ILogger logger) - { - _httpClient = httpClient; - _json = json; - _config = config; - _logger = logger; - } - - public async Task> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken) - { - return new List(); - } - - public async Task> GetMetadata(ArtistInfo id, CancellationToken cancellationToken) - { - var result = new MetadataResult(); - - var musicBrainzId = id.GetMusicBrainzArtistId(); - - if (!String.IsNullOrWhiteSpace(musicBrainzId)) - { - cancellationToken.ThrowIfCancellationRequested(); - - result.Item = new MusicArtist(); - result.HasMetadata = true; - - await FetchLastfmData(result.Item, musicBrainzId, cancellationToken).ConfigureAwait(false); - } - - return result; - } - - protected async Task FetchLastfmData(MusicArtist item, string musicBrainzId, CancellationToken cancellationToken) - { - // Get artist info with provided id - var url = RootUrl + String.Format("method=artist.getInfo&mbid={0}&api_key={1}&format=json", UrlEncode(musicBrainzId), ApiKey); - - LastfmGetArtistResult result; - - using (var json = await _httpClient.Get(new HttpRequestOptions - { - Url = url, - ResourcePool = LastfmResourcePool, - CancellationToken = cancellationToken, - EnableHttpCompression = false - - }).ConfigureAwait(false)) - { - using (var reader = new StreamReader(json)) - { - var jsonText = await reader.ReadToEndAsync().ConfigureAwait(false); - - // Fix their bad json - jsonText = jsonText.Replace("\"#text\"", "\"url\""); - - result = _json.DeserializeFromString(jsonText); - } - } - - if (result != null && result.artist != null) - { - ProcessArtistData(item, result.artist, musicBrainzId); - } - } - - private void ProcessArtistData(MusicArtist artist, LastfmArtist data, string musicBrainzId) - { - var yearFormed = 0; - - if (data.bio != null) - { - Int32.TryParse(data.bio.yearformed, out yearFormed); - if (!artist.LockedFields.Contains(MetadataFields.Overview)) - { - artist.Overview = (data.bio.content ?? string.Empty).StripHtml(); - } - if (!string.IsNullOrEmpty(data.bio.placeformed) && !artist.LockedFields.Contains(MetadataFields.ProductionLocations)) - { - artist.AddProductionLocation(data.bio.placeformed); - } - } - - if (yearFormed > 0) - { - artist.PremiereDate = new DateTime(yearFormed, 1, 1, 0, 0, 0, DateTimeKind.Utc); - - artist.ProductionYear = yearFormed; - } - - string imageSize; - var url = LastfmHelper.GetImageUrl(data, out imageSize); - - if (!string.IsNullOrEmpty(musicBrainzId) && !string.IsNullOrEmpty(url)) - { - LastfmHelper.SaveImageInfo(_config.ApplicationPaths, _logger, musicBrainzId, url, imageSize); - } - } - - /// - /// Encodes an URL. - /// - /// The name. - /// System.String. - private string UrlEncode(string name) - { - return WebUtility.UrlEncode(name); - } - - public string Name - { - get { return "last.fm"; } - } - - public int Order - { - get - { - // After fanart & audiodb - return 2; - } - } - - public Task GetImageResponse(string url, CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } - } -} diff --git a/MediaBrowser.Providers/Music/LastfmHelper.cs b/MediaBrowser.Providers/Music/LastfmHelper.cs deleted file mode 100644 index 7c83cec6b..000000000 --- a/MediaBrowser.Providers/Music/LastfmHelper.cs +++ /dev/null @@ -1,76 +0,0 @@ -using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.Logging; -using System; -using System.IO; -using System.Linq; - -namespace MediaBrowser.Providers.Music -{ - public static class LastfmHelper - { - public static string GetImageUrl(IHasLastFmImages data, out string size) - { - size = null; - - if (data.image == null) - { - return null; - } - - var validImages = data.image - .Where(i => !string.IsNullOrWhiteSpace(i.url)) - .ToList(); - - var img = validImages - .FirstOrDefault(i => string.Equals(i.size, "mega", StringComparison.OrdinalIgnoreCase)) ?? - data.image.FirstOrDefault(i => string.Equals(i.size, "extralarge", StringComparison.OrdinalIgnoreCase)) ?? - data.image.FirstOrDefault(i => string.Equals(i.size, "large", StringComparison.OrdinalIgnoreCase)) ?? - data.image.FirstOrDefault(i => string.Equals(i.size, "medium", StringComparison.OrdinalIgnoreCase)) ?? - data.image.FirstOrDefault(); - - if (img != null) - { - size = img.size; - return img.url; - } - - return null; - } - - public static void SaveImageInfo(IApplicationPaths appPaths, ILogger logger, string musicBrainzId, string url, string size) - { - if (appPaths == null) - { - throw new ArgumentNullException("appPaths"); - } - if (string.IsNullOrEmpty(musicBrainzId)) - { - throw new ArgumentNullException("musicBrainzId"); - } - if (string.IsNullOrEmpty(url)) - { - throw new ArgumentNullException("url"); - } - - var cachePath = Path.Combine(appPaths.CachePath, "lastfm", musicBrainzId, "image.txt"); - - try - { - if (string.IsNullOrEmpty(url)) - { - File.Delete(cachePath); - } - else - { - Directory.CreateDirectory(Path.GetDirectoryName(cachePath)); - File.WriteAllText(cachePath, url + "|" + size); - } - } - catch (IOException ex) - { - // Don't fail if this is unable to write - logger.ErrorException("Error saving to {0}", ex, cachePath); - } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs b/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs index 07c86ed31..aaa02c720 100644 --- a/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs +++ b/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs @@ -26,7 +26,7 @@ namespace MediaBrowser.Server.Implementations.Collections public override bool IsHiddenFromUser(User user) { - return true; + return !user.Configuration.DisplayCollectionsView; } public override string CollectionType diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs index dd2978b17..73afa22e3 100644 --- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs @@ -10,12 +10,12 @@ using MediaBrowser.Controller.Localization; using MediaBrowser.Model.Channels; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Library; +using MediaBrowser.Model.Querying; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Model.Querying; namespace MediaBrowser.Server.Implementations.Library { @@ -82,7 +82,7 @@ namespace MediaBrowser.Server.Implementations.Library list.Add(await GetUserView(CollectionType.Games, user, string.Empty, cancellationToken).ConfigureAwait(false)); } - if (user.Configuration.DisplayCollectionsView || + if (user.Configuration.DisplayCollectionsView && recursiveChildren.OfType().Any()) { list.Add(await GetUserView(CollectionType.BoxSets, user, CollectionType.BoxSets, cancellationToken).ConfigureAwait(false)); -- cgit v1.2.3 From 51e964dae394ae4a211482d562dbc28e79c00c3c Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 29 Jul 2014 23:31:35 -0400 Subject: support channels with dlna --- MediaBrowser.Api/Playback/BaseStreamingService.cs | 2 +- MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 2 +- MediaBrowser.Api/Playback/Hls/VideoHlsService.cs | 2 +- .../Playback/Progressive/VideoService.cs | 2 +- .../ScheduledTasks/Tasks/ReloadLoggerFileTask.cs | 2 +- MediaBrowser.Common/Net/IServerManager.cs | 6 - .../Channels/IChannelManager.cs | 16 ++ .../Entities/BasePluginFolder.cs | 2 +- MediaBrowser.Controller/Entities/UserView.cs | 31 +++ MediaBrowser.Controller/Library/TVUtils.cs | 89 ++++++++- MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs | 23 +-- .../ContentDirectory/ContentDirectory.cs | 11 +- .../ContentDirectory/ControlHandler.cs | 220 +++++++++++++++------ MediaBrowser.Dlna/Didl/DidlBuilder.cs | 10 +- MediaBrowser.Dlna/Images/logo120.jpg | Bin 6745 -> 8879 bytes MediaBrowser.Dlna/Images/logo120.png | Bin 4124 -> 13621 bytes MediaBrowser.Dlna/Images/logo240.jpg | Bin 0 -> 22073 bytes MediaBrowser.Dlna/Images/logo240.png | Bin 0 -> 33252 bytes MediaBrowser.Dlna/Images/logo48.jpg | Bin 2484 -> 2997 bytes MediaBrowser.Dlna/Images/logo48.png | Bin 1661 -> 3837 bytes MediaBrowser.Dlna/MediaBrowser.Dlna.csproj | 4 + MediaBrowser.Dlna/PlayTo/Device.cs | 4 +- MediaBrowser.Dlna/PlayTo/PlayToManager.cs | 67 ++++--- MediaBrowser.Dlna/Profiles/DefaultProfile.cs | 2 + MediaBrowser.Dlna/Profiles/Xml/Android.xml | 2 +- MediaBrowser.Dlna/Profiles/Xml/Default.xml | 2 +- MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml | 2 +- MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml | 2 +- MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml | 2 +- MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml | 2 +- MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml | 2 +- .../Profiles/Xml/Sony Blu-ray Player 2013.xml | 2 +- .../Profiles/Xml/Sony Blu-ray Player.xml | 2 +- .../Profiles/Xml/Sony Bravia (2010).xml | 2 +- .../Profiles/Xml/Sony Bravia (2011).xml | 2 +- .../Profiles/Xml/Sony Bravia (2012).xml | 2 +- .../Profiles/Xml/Sony Bravia (2013).xml | 2 +- .../Profiles/Xml/Sony PlayStation 3.xml | 2 +- MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml | 2 +- MediaBrowser.Dlna/Profiles/Xml/Windows 8 RT.xml | 2 +- MediaBrowser.Dlna/Profiles/Xml/Windows Phone.xml | 2 +- MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml | 2 +- MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml | 2 +- MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml | 2 +- MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs | 18 ++ MediaBrowser.Dlna/Ssdp/SsdpHandler.cs | 9 +- MediaBrowser.Model/Dlna/SortCriteria.cs | 8 +- .../Channels/ChannelDownloadScheduledTask.cs | 2 +- .../Channels/ChannelManager.cs | 67 ++++++- .../EntryPoints/ExternalPortForwarding.cs | 5 - .../EntryPoints/UdpServerEntryPoint.cs | 7 +- .../Library/Resolvers/Audio/MusicAlbumResolver.cs | 43 ++-- .../Library/Resolvers/Audio/MusicArtistResolver.cs | 8 +- .../Library/Resolvers/TV/SeriesResolver.cs | 18 +- .../Library/UserViewManager.cs | 42 +++- .../LiveTv/CleanDatabaseScheduledTask.cs | 2 +- .../Localization/JavaScript/javascript.json | 1 - .../Localization/JavaScript/kk.json | 8 +- .../Localization/JavaScript/pt_PT.json | 2 +- .../Localization/JavaScript/ru.json | 6 +- .../Localization/Server/ar.json | 12 +- .../Localization/Server/ca.json | 12 +- .../Localization/Server/cs.json | 12 +- .../Localization/Server/da.json | 12 +- .../Localization/Server/de.json | 12 +- .../Localization/Server/el.json | 12 +- .../Localization/Server/en_GB.json | 12 +- .../Localization/Server/en_US.json | 12 +- .../Localization/Server/es.json | 12 +- .../Localization/Server/es_MX.json | 14 +- .../Localization/Server/fr.json | 12 +- .../Localization/Server/he.json | 12 +- .../Localization/Server/it.json | 12 +- .../Localization/Server/kk.json | 26 ++- .../Localization/Server/ko.json | 12 +- .../Localization/Server/ms.json | 12 +- .../Localization/Server/nb.json | 12 +- .../Localization/Server/nl.json | 46 +++-- .../Localization/Server/pl.json | 12 +- .../Localization/Server/pt_BR.json | 22 ++- .../Localization/Server/pt_PT.json | 44 +++-- .../Localization/Server/ru.json | 28 ++- .../Localization/Server/sv.json | 12 +- .../Localization/Server/vi.json | 12 +- .../Localization/Server/zh_TW.json | 12 +- .../ServerManager/ServerManager.cs | 9 - .../Session/WebSocketController.cs | 25 +++ .../Udp/UdpServer.cs | 14 +- .../Native/ServerAuthorization.cs | 2 +- MediaBrowser.ServerApplication/ApplicationHost.cs | 14 +- .../FFMpeg/FFMpegDownloadInfo.cs | 8 +- .../FFMpeg/FFMpegDownloader.cs | 8 +- MediaBrowser.Tests/Resolvers/TvUtilTests.cs | 2 + 93 files changed, 951 insertions(+), 320 deletions(-) create mode 100644 MediaBrowser.Dlna/Images/logo240.jpg create mode 100644 MediaBrowser.Dlna/Images/logo240.png (limited to 'MediaBrowser.Server.Implementations/Library/UserViewManager.cs') diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 5cae4c3f7..df85ca3cb 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -626,7 +626,7 @@ namespace MediaBrowser.Api.Playback /// The state. /// The output video codec. /// System.String. - protected string GetInternalGraphicalSubtitleParam(StreamState state, string outputVideoCodec) + protected string GetGraphicalSubtitleParam(StreamState state, string outputVideoCodec) { var outputSizeParam = string.Empty; diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index fc1c627e9..ccaa918ec 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -522,7 +522,7 @@ namespace MediaBrowser.Api.Playback.Hls // This is for internal graphical subs if (hasGraphicalSubs) { - args += GetInternalGraphicalSubtitleParam(state, codec); + args += GetGraphicalSubtitleParam(state, codec); } return args; diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index 28c0219fc..1dffa5411 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -186,7 +186,7 @@ namespace MediaBrowser.Api.Playback.Hls // This is for internal graphical subs if (hasGraphicalSubs) { - args += GetInternalGraphicalSubtitleParam(state, codec); + args += GetGraphicalSubtitleParam(state, codec); } return args; diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index bedacc0d2..cdaa99130 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -167,7 +167,7 @@ namespace MediaBrowser.Api.Playback.Progressive // This is for internal graphical subs if (hasGraphicalSubs) { - args += GetInternalGraphicalSubtitleParam(state, codec); + args += GetGraphicalSubtitleParam(state, codec); } return args; diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/ReloadLoggerFileTask.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/ReloadLoggerFileTask.cs index 38b6b4ad6..78f60632f 100644 --- a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/ReloadLoggerFileTask.cs +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/ReloadLoggerFileTask.cs @@ -94,7 +94,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks public bool IsHidden { - get { return false; } + get { return true; } } public bool IsEnabled diff --git a/MediaBrowser.Common/Net/IServerManager.cs b/MediaBrowser.Common/Net/IServerManager.cs index f6ac0ab68..84e578579 100644 --- a/MediaBrowser.Common/Net/IServerManager.cs +++ b/MediaBrowser.Common/Net/IServerManager.cs @@ -10,12 +10,6 @@ namespace MediaBrowser.Common.Net /// public interface IServerManager : IDisposable { - /// - /// Gets the web socket port number. - /// - /// The web socket port number. - int WebSocketPortNumber { get; } - /// /// Starts this instance. /// diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs index 744eab96e..252e2aee5 100644 --- a/MediaBrowser.Controller/Channels/IChannelManager.cs +++ b/MediaBrowser.Controller/Channels/IChannelManager.cs @@ -43,6 +43,14 @@ namespace MediaBrowser.Controller.Channels /// Channel. Channel GetChannel(string id); + /// + /// Gets the channels internal. + /// + /// The query. + /// The cancellation token. + /// Task<QueryResult<Channel>>. + Task> GetChannelsInternal(ChannelQuery query, CancellationToken cancellationToken); + /// /// Gets the channels. /// @@ -75,6 +83,14 @@ namespace MediaBrowser.Controller.Channels /// Task{QueryResult{BaseItemDto}}. Task> GetChannelItems(ChannelItemQuery query, CancellationToken cancellationToken); + /// + /// Gets the channel items internal. + /// + /// The query. + /// The cancellation token. + /// Task<QueryResult<BaseItem>>. + Task> GetChannelItemsInternal(ChannelItemQuery query, CancellationToken cancellationToken); + /// /// Gets the cached channel item media sources. /// diff --git a/MediaBrowser.Controller/Entities/BasePluginFolder.cs b/MediaBrowser.Controller/Entities/BasePluginFolder.cs index e1383923f..fa2b49a60 100644 --- a/MediaBrowser.Controller/Entities/BasePluginFolder.cs +++ b/MediaBrowser.Controller/Entities/BasePluginFolder.cs @@ -14,7 +14,7 @@ namespace MediaBrowser.Controller.Entities public virtual string CollectionType { - get { return Model.Entities.CollectionType.BoxSets; } + get { return null; } } } } diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index 619a497f5..be426d99e 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -1,6 +1,7 @@ using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Model.Entities; +using MoreLinq; using System; using System.Collections.Generic; using System.Linq; @@ -29,6 +30,10 @@ namespace MediaBrowser.Controller.Entities case CollectionType.Trailers: return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)) .OfType(); + case CollectionType.Movies: + return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)) + .Where(i => i is Movie || i is BoxSet) + .DistinctBy(i => i.Id); default: return mediaFolders.SelectMany(i => i.GetChildren(user, includeLinkedChildren)); } @@ -70,4 +75,30 @@ namespace MediaBrowser.Controller.Entities return standaloneTypes.Contains(collectionFolder.CollectionType ?? string.Empty); } } + + public class SpecialFolder : Folder + { + public SpecialFolderType SpecialFolderType { get; set; } + public string ItemTypeName { get; set; } + public string ParentId { get; set; } + + public override IEnumerable GetChildren(User user, bool includeLinkedChildren) + { + var parent = (Folder)LibraryManager.GetItemById(new Guid(ParentId)); + + if (SpecialFolderType == SpecialFolderType.ItemsByType) + { + var items = parent.GetRecursiveChildren(user, includeLinkedChildren); + + return items.Where(i => string.Equals(i.GetType().Name, ItemTypeName, StringComparison.OrdinalIgnoreCase)); + } + + return new List(); + } + } + + public enum SpecialFolderType + { + ItemsByType = 1 + } } diff --git a/MediaBrowser.Controller/Library/TVUtils.cs b/MediaBrowser.Controller/Library/TVUtils.cs index 86699272e..6ef1fb6f7 100644 --- a/MediaBrowser.Controller/Library/TVUtils.cs +++ b/MediaBrowser.Controller/Library/TVUtils.cs @@ -8,6 +8,7 @@ using System.Globalization; using System.IO; using System.Linq; using System.Text.RegularExpressions; +using MediaBrowser.Model.Logging; namespace MediaBrowser.Controller.Library { @@ -136,6 +137,16 @@ namespace MediaBrowser.Controller.Library return val; } + if (filename.StartsWith("s", StringComparison.OrdinalIgnoreCase)) + { + var testFilename = filename.Substring(1); + + if (int.TryParse(testFilename, NumberStyles.Integer, CultureInfo.InvariantCulture, out val)) + { + return val; + } + } + // Look for one of the season folder names foreach (var name in SeasonFolderNames) { @@ -182,7 +193,7 @@ namespace MediaBrowser.Controller.Library return null; } - return int.Parse(path.Substring(numericStart, length)); + return int.Parse(path.Substring(numericStart, length), CultureInfo.InvariantCulture); } /// @@ -194,21 +205,59 @@ namespace MediaBrowser.Controller.Library /// true if [is season folder] [the specified path]; otherwise, false. private static bool IsSeasonFolder(string path, IDirectoryService directoryService, IFileSystem fileSystem) { + var seasonNumber = GetSeasonNumberFromPath(path); + var hasSeasonNumber = seasonNumber != null; + + if (!hasSeasonNumber) + { + return false; + } + // It's a season folder if it's named as such and does not contain any audio files, apart from theme.mp3 - return GetSeasonNumberFromPath(path) != null && - !directoryService.GetFiles(path) - .Any(i => EntityResolutionHelper.IsAudioFile(i.FullName) && !string.Equals(fileSystem.GetFileNameWithoutExtension(i), BaseItem.ThemeSongFilename)); + foreach (var fileSystemInfo in directoryService.GetFileSystemEntries(path)) + { + var attributes = fileSystemInfo.Attributes; + + if ((attributes & FileAttributes.Hidden) == FileAttributes.Hidden) + { + continue; + } + + if ((attributes & FileAttributes.System) == FileAttributes.System) + { + continue; + } + + if ((attributes & FileAttributes.Directory) == FileAttributes.Directory) + { + //if (IsBadFolder(fileSystemInfo.Name)) + //{ + // return false; + //} + } + else + { + if (EntityResolutionHelper.IsAudioFile(fileSystemInfo.FullName) && + !string.Equals(fileSystem.GetFileNameWithoutExtension(fileSystemInfo), BaseItem.ThemeSongFilename)) + { + return false; + } + } + } + + return true; } /// /// Determines whether [is series folder] [the specified path]. /// /// The path. - /// if set to true [consider seasonless series]. + /// if set to true [consider seasonless entries]. /// The file system children. /// The directory service. + /// The file system. /// true if [is series folder] [the specified path]; otherwise, false. - public static bool IsSeriesFolder(string path, bool considerSeasonlessSeries, IEnumerable fileSystemChildren, IDirectoryService directoryService, IFileSystem fileSystem) + public static bool IsSeriesFolder(string path, bool considerSeasonlessEntries, IEnumerable fileSystemChildren, IDirectoryService directoryService, IFileSystem fileSystem, ILogger logger) { // A folder with more than 3 non-season folders in will not becounted as a series var nonSeriesFolders = 0; @@ -231,15 +280,20 @@ namespace MediaBrowser.Controller.Library { if (IsSeasonFolder(child.FullName, directoryService, fileSystem)) { + logger.Debug("{0} is a series because of season folder {1}.", path, child.FullName); return true; } - if (!EntityResolutionHelper.IgnoreFolders.Contains(child.Name, StringComparer.OrdinalIgnoreCase)) + + if (IsBadFolder(child.Name)) { + logger.Debug("Invalid folder under series: {0}", child.FullName); + nonSeriesFolders++; } if (nonSeriesFolders >= 3) { + logger.Debug("{0} not a series due to 3 or more invalid folders.", path); return false; } } @@ -249,7 +303,7 @@ namespace MediaBrowser.Controller.Library if (EntityResolutionHelper.IsVideoFile(fullName) || EntityResolutionHelper.IsVideoPlaceHolder(fullName)) { - if (GetEpisodeNumberFromFile(fullName, considerSeasonlessSeries).HasValue) + if (GetEpisodeNumberFromFile(fullName, considerSeasonlessEntries).HasValue) { return true; } @@ -257,9 +311,28 @@ namespace MediaBrowser.Controller.Library } } + logger.Debug("{0} is not a series folder.", path); return false; } + private static bool IsBadFolder(string name) + { + if (string.Equals(name, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + if (string.Equals(name, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + if (string.Equals(name, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + return !EntityResolutionHelper.IgnoreFolders.Contains(name, StringComparer.OrdinalIgnoreCase); + } + /// /// Episodes the number from file. /// diff --git a/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs b/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs index cc5cb1b1a..78db68f63 100644 --- a/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs +++ b/MediaBrowser.Dlna/Channels/DlnaChannelFactory.cs @@ -45,7 +45,7 @@ namespace MediaBrowser.Dlna.Channels _localServersLookup = localServersLookup; _deviceDiscovery = deviceDiscovery; - deviceDiscovery.DeviceDiscovered += deviceDiscovery_DeviceDiscovered; + //deviceDiscovery.DeviceDiscovered += deviceDiscovery_DeviceDiscovered; deviceDiscovery.DeviceLeft += deviceDiscovery_DeviceLeft; } @@ -196,25 +196,16 @@ namespace MediaBrowser.Dlna.Channels public class ServerChannel : IChannel, IFactoryChannel { - private readonly List _servers = new List(); private readonly IHttpClient _httpClient; private readonly ILogger _logger; - private readonly string _controlUrl; + public string ControlUrl { get; set; } + public List Servers { get; set; } - /// - /// Prevents core from throwing an exception - /// - public ServerChannel() + public ServerChannel(IHttpClient httpClient, ILogger logger) { - - } - - public ServerChannel(List servers, IHttpClient httpClient, ILogger logger, string controlUrl) - { - _servers = servers; _httpClient = httpClient; _logger = logger; - _controlUrl = controlUrl; + Servers = new List(); } public string Name @@ -272,7 +263,7 @@ namespace MediaBrowser.Dlna.Channels if (string.IsNullOrWhiteSpace(query.FolderId)) { - items = _servers.Select(i => new ChannelItemInfo + items = Servers.Select(i => new ChannelItemInfo { FolderType = ChannelFolderType.Container, Id = GetServerId(i), @@ -291,7 +282,7 @@ namespace MediaBrowser.Dlna.Channels Limit = query.Limit, StartIndex = query.StartIndex, ParentId = folderId, - ContentDirectoryUrl = _controlUrl + ContentDirectoryUrl = ControlUrl }, cancellationToken).ConfigureAwait(false); diff --git a/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs b/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs index f594b4471..f5731b893 100644 --- a/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs +++ b/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs @@ -1,4 +1,5 @@ using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; @@ -21,6 +22,8 @@ namespace MediaBrowser.Dlna.ContentDirectory private readonly IDlnaManager _dlna; private readonly IServerConfigurationManager _config; private readonly IUserManager _userManager; + private readonly IUserViewManager _userViewManager; + private readonly IChannelManager _channelManager; public ContentDirectory(IDlnaManager dlna, IUserDataManager userDataManager, @@ -29,7 +32,7 @@ namespace MediaBrowser.Dlna.ContentDirectory IServerConfigurationManager config, IUserManager userManager, ILogger logger, - IHttpClient httpClient) + IHttpClient httpClient, IUserViewManager userViewManager, IChannelManager channelManager) : base(logger, httpClient) { _dlna = dlna; @@ -38,6 +41,8 @@ namespace MediaBrowser.Dlna.ContentDirectory _libraryManager = libraryManager; _config = config; _userManager = userManager; + _userViewManager = userViewManager; + _channelManager = channelManager; } private int SystemUpdateId @@ -73,7 +78,9 @@ namespace MediaBrowser.Dlna.ContentDirectory _userDataManager, user, SystemUpdateId, - _config) + _config, + _userViewManager, + _channelManager) .ProcessControlRequest(request); } diff --git a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs index b4f918e68..1553435ae 100644 --- a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs +++ b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs @@ -1,4 +1,5 @@ using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; @@ -9,8 +10,10 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Dlna.Didl; using MediaBrowser.Dlna.Server; using MediaBrowser.Dlna.Service; +using MediaBrowser.Model.Channels; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Library; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Querying; using System; @@ -19,6 +22,7 @@ using System.Globalization; using System.Linq; using System.Text; using System.Threading; +using System.Threading.Tasks; using System.Xml; namespace MediaBrowser.Dlna.ContentDirectory @@ -40,14 +44,18 @@ namespace MediaBrowser.Dlna.ContentDirectory private readonly DidlBuilder _didlBuilder; private readonly DeviceProfile _profile; + private readonly IUserViewManager _userViewManager; + private readonly IChannelManager _channelManager; - public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config) + public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, IUserViewManager userViewManager, IChannelManager channelManager) : base(config, logger) { _libraryManager = libraryManager; _userDataManager = userDataManager; _user = user; _systemUpdateId = systemUpdateId; + _userViewManager = userViewManager; + _channelManager = channelManager; _profile = profile; _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress); @@ -69,7 +77,7 @@ namespace MediaBrowser.Dlna.ContentDirectory return HandleGetSystemUpdateID(); if (string.Equals(methodName, "Browse", StringComparison.OrdinalIgnoreCase)) - return HandleBrowse(methodParams, user, deviceId); + return HandleBrowse(methodParams, user, deviceId).Result; if (string.Equals(methodName, "X_GetFeatureList", StringComparison.OrdinalIgnoreCase)) return HandleXGetFeatureList(); @@ -78,7 +86,7 @@ namespace MediaBrowser.Dlna.ContentDirectory return HandleXSetBookmark(methodParams, user); if (string.Equals(methodName, "Search", StringComparison.OrdinalIgnoreCase)) - return HandleSearch(methodParams, user, deviceId); + return HandleSearch(methodParams, user, deviceId).Result; throw new ResourceNotFoundException("Unexpected control request name: " + methodName); } @@ -141,7 +149,7 @@ namespace MediaBrowser.Dlna.ContentDirectory return builder.ToString(); } - private IEnumerable> HandleBrowse(Headers sparams, User user, string deviceId) + private async Task>> HandleBrowse(Headers sparams, User user, string deviceId) { var id = sparams["ObjectID"]; var flag = sparams["BrowseFlag"]; @@ -149,16 +157,20 @@ namespace MediaBrowser.Dlna.ContentDirectory var sortCriteria = new SortCriteria(sparams.GetValueOrDefault("SortCriteria", "")); var provided = 0; - var requested = 0; - var start = 0; - if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out requested) && requested <= 0) + int? requested = 0; + int? start = 0; + + int requestedVal; + if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out requestedVal) && requestedVal > 0) { - requested = 0; + requested = requestedVal; } - if (sparams.ContainsKey("StartingIndex") && int.TryParse(sparams["StartingIndex"], out start) && start <= 0) + + int startVal; + if (sparams.ContainsKey("StartingIndex") && int.TryParse(sparams["StartingIndex"], out startVal) && startVal > 0) { - start = 0; + start = startVal; } //var root = GetItem(id) as IMediaFolder; @@ -173,34 +185,26 @@ namespace MediaBrowser.Dlna.ContentDirectory var folder = (Folder)GetItemFromObjectId(id, user); - var children = GetChildrenSorted(folder, user, sortCriteria).ToList(); + var childrenResult = (await GetChildrenSorted(folder, user, sortCriteria, start, requested).ConfigureAwait(false)); - var totalCount = children.Count; + var totalCount = childrenResult.TotalRecordCount; if (string.Equals(flag, "BrowseMetadata")) { - result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, folder, children.Count, filter)); + result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, folder, totalCount, filter)); provided++; } else { - if (start > 0) - { - children = children.Skip(start).ToList(); - } - if (requested > 0) - { - children = children.Take(requested).ToList(); - } - - provided = children.Count; + provided = childrenResult.Items.Length; - foreach (var i in children) + foreach (var i in childrenResult.Items) { if (i.IsFolder) { var f = (Folder)i; - var childCount = GetChildrenSorted(f, user, sortCriteria).Count(); + var childCount = (await GetChildrenSorted(f, user, sortCriteria, null, 0).ConfigureAwait(false)) + .TotalRecordCount; result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, f, childCount, filter)); } @@ -222,7 +226,7 @@ namespace MediaBrowser.Dlna.ContentDirectory }; } - private IEnumerable> HandleSearch(Headers sparams, User user, string deviceId) + private async Task>> HandleSearch(Headers sparams, User user, string deviceId) { var searchCriteria = new SearchCriteria(sparams.GetValueOrDefault("SearchCriteria", "")); var sortCriteria = new SortCriteria(sparams.GetValueOrDefault("SortCriteria", "")); @@ -230,16 +234,19 @@ namespace MediaBrowser.Dlna.ContentDirectory // sort example: dc:title, dc:date - var requested = 0; - var start = 0; + int? requested = 0; + int? start = 0; - if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out requested) && requested <= 0) + int requestedVal; + if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out requestedVal) && requestedVal > 0) { - requested = 0; + requested = requestedVal; } - if (sparams.ContainsKey("StartingIndex") && int.TryParse(sparams["StartingIndex"], out start) && start <= 0) + + int startVal; + if (sparams.ContainsKey("StartingIndex") && int.TryParse(sparams["StartingIndex"], out startVal) && startVal > 0) { - start = 0; + start = startVal; } //var root = GetItem(id) as IMediaFolder; @@ -259,27 +266,19 @@ namespace MediaBrowser.Dlna.ContentDirectory var folder = (Folder)GetItemFromObjectId(sparams["ContainerID"], user); - var children = GetChildrenSorted(folder, user, searchCriteria, sortCriteria).ToList(); + var childrenResult = (await GetChildrenSorted(folder, user, searchCriteria, sortCriteria, start, requested).ConfigureAwait(false)); - var totalCount = children.Count; + var totalCount = childrenResult.TotalRecordCount; - if (start > 0) - { - children = children.Skip(start).ToList(); - } - if (requested > 0) - { - children = children.Take(requested).ToList(); - } - - var provided = children.Count; + var provided = childrenResult.Items.Length; - foreach (var i in children) + foreach (var i in childrenResult.Items) { if (i.IsFolder) { var f = (Folder)i; - var childCount = GetChildrenSorted(f, user, searchCriteria, sortCriteria).Count(); + var childCount = (await GetChildrenSorted(f, user, searchCriteria, sortCriteria, null, 0).ConfigureAwait(false)) + .TotalRecordCount; result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, f, childCount, filter)); } @@ -300,15 +299,16 @@ namespace MediaBrowser.Dlna.ContentDirectory }; } - private IEnumerable GetChildrenSorted(Folder folder, User user, SearchCriteria search, SortCriteria sort) + private async Task> GetChildrenSorted(Folder folder, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit) { if (search.SearchType == SearchType.Unknown) { - return GetChildrenSorted(folder, user, sort); + return await GetChildrenSorted(folder, user, sort, startIndex, limit).ConfigureAwait(false); } - var items = folder.GetRecursiveChildren(user); - items = FilterUnsupportedContent(items); + var result = await GetChildrenSorted(folder, user, sort, null, null).ConfigureAwait(false); + + var items = FilterUnsupportedContent(result.Items); if (search.SearchType == SearchType.Audio) { @@ -324,12 +324,123 @@ namespace MediaBrowser.Dlna.ContentDirectory } else if (search.SearchType == SearchType.Playlist) { + } - return SortItems(items, user, sort); + items = SortItems(items, user, sort); + + return ToResult(items, startIndex, limit); + } + + private async Task> GetChildrenSorted(Folder folder, User user, SortCriteria sort, int? startIndex, int? limit) + { + if (folder is UserRootFolder) + { + var result = await _userViewManager.GetUserViews(new UserViewQuery + { + UserId = user.Id.ToString("N") + + }, CancellationToken.None).ConfigureAwait(false); + + return ToResult(result, startIndex, limit); + } + + var view = folder as UserView; + + if (view != null) + { + var result = await GetUserViewChildren(view, user, sort).ConfigureAwait(false); + + return ToResult(result, startIndex, limit); + } + + var channel = folder as Channel; + + if (channel != null) + { + return await _channelManager.GetChannelItemsInternal(new ChannelItemQuery + { + ChannelId = channel.Id.ToString("N"), + Limit = limit, + StartIndex = startIndex, + UserId = user.Id.ToString("N") + + }, CancellationToken.None); + } + + var channelFolderItem = folder as ChannelFolderItem; + + if (channelFolderItem != null) + { + return await _channelManager.GetChannelItemsInternal(new ChannelItemQuery + { + ChannelId = channelFolderItem.ChannelId, + FolderId = channelFolderItem.Id.ToString("N"), + Limit = limit, + StartIndex = startIndex, + UserId = user.Id.ToString("N") + + }, CancellationToken.None); + } + + return ToResult(GetPlainFolderChildrenSorted(folder, user, sort), startIndex, limit); + } + + private QueryResult ToResult(IEnumerable items, int? startIndex, int? limit) + { + var list = items.ToArray(); + var totalCount = list.Length; + + if (startIndex.HasValue) + { + list = list.Skip(startIndex.Value).ToArray(); + } + + if (limit.HasValue) + { + list = list.Take(limit.Value).ToArray(); + } + + return new QueryResult + { + Items = list, + TotalRecordCount = totalCount + }; + } + + private async Task> GetUserViewChildren(UserView folder, User user, SortCriteria sort) + { + if (string.Equals(folder.ViewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase)) + { + return new List(); + } + if (string.Equals(folder.ViewType, CollectionType.Channels, StringComparison.OrdinalIgnoreCase)) + { + var result = await _channelManager.GetChannelsInternal(new ChannelQuery() + { + UserId = user.Id.ToString("N") + + }, CancellationToken.None).ConfigureAwait(false); + + return result.Items; + } + if (string.Equals(folder.ViewType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase)) + { + return SortItems(folder.GetChildren(user, true).OfType(), user, sort); + } + if (string.Equals(folder.ViewType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase)) + { + return GetPlainFolderChildrenSorted(folder, user, sort); + } + if (string.Equals(folder.ViewType, CollectionType.Music, StringComparison.OrdinalIgnoreCase)) + { + return SortItems(folder.GetChildren(user, true).OfType(), user, sort); + } + + return GetPlainFolderChildrenSorted(folder, user, sort); } - private IEnumerable GetChildrenSorted(Folder folder, User user, SortCriteria sort) + private IEnumerable GetPlainFolderChildrenSorted(Folder folder, User user, SortCriteria sort) { var items = folder.GetChildren(user, true); @@ -345,7 +456,7 @@ namespace MediaBrowser.Dlna.ContentDirectory private IEnumerable SortItems(IEnumerable items, User user, SortCriteria sort) { - return _libraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending); + return _libraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, sort.SortOrder); } private IEnumerable FilterUnsupportedContent(IEnumerable items) @@ -353,14 +464,12 @@ namespace MediaBrowser.Dlna.ContentDirectory return items.Where(i => { // Unplayable - // TODO: Display and prevent playback with restricted flag? if (i.LocationType == LocationType.Virtual) { return false; } // Unplayable - // TODO: Display and prevent playback with restricted flag? var supportsPlaceHolder = i as ISupportsPlaceHolders; if (supportsPlaceHolder != null && supportsPlaceHolder.IsPlaceHolder) { @@ -368,7 +477,6 @@ namespace MediaBrowser.Dlna.ContentDirectory } // Upnp renderers won't understand these - // TODO: Display and prevent playback with restricted flag? if (i is Game || i is Book) { return false; diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs index 739793c03..649ba2c8f 100644 --- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs +++ b/MediaBrowser.Dlna/Didl/DidlBuilder.cs @@ -1,4 +1,5 @@ using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -612,6 +613,13 @@ namespace MediaBrowser.Dlna.Didl { return GetImageInfo(item, ImageType.Thumb); } + if (item.HasImage(ImageType.Backdrop)) + { + if (item is Channel) + { + return GetImageInfo(item, ImageType.Backdrop); + } + } if (item is Audio || item is Episode) { @@ -633,7 +641,7 @@ namespace MediaBrowser.Dlna.Didl try { - tag = _imageProcessor.GetImageCacheTag(item, ImageType.Primary); + tag = _imageProcessor.GetImageCacheTag(item, type); } catch { diff --git a/MediaBrowser.Dlna/Images/logo120.jpg b/MediaBrowser.Dlna/Images/logo120.jpg index 1de803c8f..394c8e137 100644 Binary files a/MediaBrowser.Dlna/Images/logo120.jpg and b/MediaBrowser.Dlna/Images/logo120.jpg differ diff --git a/MediaBrowser.Dlna/Images/logo120.png b/MediaBrowser.Dlna/Images/logo120.png index 2dd04d468..97bdef818 100644 Binary files a/MediaBrowser.Dlna/Images/logo120.png and b/MediaBrowser.Dlna/Images/logo120.png differ diff --git a/MediaBrowser.Dlna/Images/logo240.jpg b/MediaBrowser.Dlna/Images/logo240.jpg new file mode 100644 index 000000000..c80552351 Binary files /dev/null and b/MediaBrowser.Dlna/Images/logo240.jpg differ diff --git a/MediaBrowser.Dlna/Images/logo240.png b/MediaBrowser.Dlna/Images/logo240.png new file mode 100644 index 000000000..532f12317 Binary files /dev/null and b/MediaBrowser.Dlna/Images/logo240.png differ diff --git a/MediaBrowser.Dlna/Images/logo48.jpg b/MediaBrowser.Dlna/Images/logo48.jpg index f1e7302aa..52b985354 100644 Binary files a/MediaBrowser.Dlna/Images/logo48.jpg and b/MediaBrowser.Dlna/Images/logo48.jpg differ diff --git a/MediaBrowser.Dlna/Images/logo48.png b/MediaBrowser.Dlna/Images/logo48.png index 3b13d141c..29d4a0528 100644 Binary files a/MediaBrowser.Dlna/Images/logo48.png and b/MediaBrowser.Dlna/Images/logo48.png differ diff --git a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj index 461470b7a..962c2a211 100644 --- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj +++ b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj @@ -184,6 +184,10 @@ + + + +