From 6f45ea08237eefde317088459c4a87669be981f4 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 1 Sep 2014 16:10:54 -0400 Subject: [PATCH] fixes #912 - Add special views for Dlna --- MediaBrowser.Api/Images/ImageService.cs | 6 +- MediaBrowser.Api/MediaBrowser.Api.csproj | 2 +- .../Playback/BaseStreamingService.cs | 2 + .../BaseProgressiveStreamingService.cs | 27 +- MediaBrowser.Api/Playback/StreamState.cs | 1 + MediaBrowser.Api/TvShowsService.cs | 136 +--- ...MediaBrowser.Common.Implementations.csproj | 2 +- .../MediaBrowser.Common.csproj | 2 +- MediaBrowser.Controller/Channels/Channel.cs | 30 + .../Channels/ChannelFolderItem.cs | 30 + .../Channels/ChannelVideoItem.cs | 2 - .../Drawing/ImageProcessingOptions.cs | 4 +- MediaBrowser.Controller/Entities/BaseItem.cs | 2 + MediaBrowser.Controller/Entities/Folder.cs | 30 +- .../Entities/UserItemsQuery.cs | 35 + .../Entities/UserRootFolder.cs | 13 + MediaBrowser.Controller/Entities/UserView.cs | 80 +-- .../Entities/UserViewBuilder.cs | 614 ++++++++++++++++++ .../LiveTv/ILiveTvManager.cs | 9 + .../MediaBrowser.Controller.csproj | 5 +- .../TV/ITVSeriesManager.cs | 24 + .../ContentDirectory/ContentDirectory.cs | 11 +- .../ContentDirectory/ControlHandler.cs | 249 ++----- MediaBrowser.Dlna/Didl/DidlBuilder.cs | 52 +- MediaBrowser.Dlna/Main/DlnaEntryPoint.cs | 9 +- MediaBrowser.Dlna/PlayTo/PlayToController.cs | 6 +- MediaBrowser.Dlna/PlayTo/PlayToManager.cs | 7 +- MediaBrowser.Dlna/Ssdp/Datagram.cs | 14 +- MediaBrowser.Dlna/Ssdp/SsdpHandler.cs | 16 +- .../MediaBrowser.MediaEncoding.csproj | 2 +- MediaBrowser.Model/Entities/CollectionType.cs | 21 + MediaBrowser.Model/MediaBrowser.Model.csproj | 2 +- .../MediaBrowser.Providers.csproj | 2 +- .../Connect/ConnectManager.cs | 2 +- .../Drawing/ImageProcessor.cs | 14 +- .../LiveTv/LiveTvManager.cs | 23 +- .../Localization/Server/server.json | 18 + ...MediaBrowser.Server.Implementations.csproj | 5 +- .../TV/TVSeriesManager.cs | 202 ++++++ .../ApplicationHost.cs | 15 +- .../LibraryViewer.cs | 76 ++- MediaBrowser.ServerApplication/MainStartup.cs | 2 +- .../ServerNotifyIcon.cs | 8 +- .../MediaBrowser.WebDashboard.csproj | 2 +- 44 files changed, 1329 insertions(+), 485 deletions(-) create mode 100644 MediaBrowser.Controller/Entities/UserItemsQuery.cs create mode 100644 MediaBrowser.Controller/Entities/UserViewBuilder.cs create mode 100644 MediaBrowser.Controller/TV/ITVSeriesManager.cs create mode 100644 MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index 2213a5af1b..2aa6800612 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -39,8 +39,8 @@ namespace MediaBrowser.Api.Images [Route("/Items/{Id}/Images/{Type}", "GET")] [Route("/Items/{Id}/Images/{Type}/{Index}", "GET")] - [Route("/Items/{Id}/Images/{Type}/{Index}/{Tag}/{Format}/{MaxWidth}/{MaxHeight}", "GET")] - [Route("/Items/{Id}/Images/{Type}/{Index}/{Tag}/{Format}/{MaxWidth}/{MaxHeight}", "HEAD")] + [Route("/Items/{Id}/Images/{Type}/{Index}/{Tag}/{Format}/{MaxWidth}/{MaxHeight}/{PercentPlayed}", "GET")] + [Route("/Items/{Id}/Images/{Type}/{Index}/{Tag}/{Format}/{MaxWidth}/{MaxHeight}/{PercentPlayed}", "HEAD")] [Api(Description = "Gets an item image")] public class GetItemImage : ImageRequest { @@ -583,7 +583,7 @@ namespace MediaBrowser.Api.Images Width = request.Width, OutputFormat = request.Format, AddPlayedIndicator = request.AddPlayedIndicator, - PercentPlayed = request.PercentPlayed, + PercentPlayed = request.PercentPlayed ?? 0, UnplayedCount = request.UnplayedCount, BackgroundColor = request.BackgroundColor }; diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 5cb9ebb1b0..b0131b5c26 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -169,7 +169,7 @@ - + - + \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs b/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs new file mode 100644 index 0000000000..e71a5d5142 --- /dev/null +++ b/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs @@ -0,0 +1,202 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.TV; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Querying; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MediaBrowser.Server.Implementations.TV +{ + public class TVSeriesManager : ITVSeriesManager + { + private readonly IUserManager _userManager; + private readonly IUserDataManager _userDataManager; + private readonly ILibraryManager _libraryManager; + + public TVSeriesManager(IUserManager userManager, IUserDataManager userDataManager, ILibraryManager libraryManager) + { + _userManager = userManager; + _userDataManager = userDataManager; + _libraryManager = libraryManager; + } + + public QueryResult GetNextUp(NextUpQuery request) + { + var user = _userManager.GetUserById(new Guid(request.UserId)); + + if (user == null) + { + throw new ArgumentException("User not found"); + } + + var parentIds = string.IsNullOrEmpty(request.ParentId) + ? new string[] { } + : new[] { request.ParentId }; + + var items = GetAllLibraryItems(user, parentIds) + .OfType(); + + // Avoid implicitly captured closure + var episodes = GetNextUpEpisodes(request, user, items); + + return GetResult(episodes, null, request); + } + + public QueryResult GetNextUp(NextUpQuery request, IEnumerable parentsFolders) + { + var user = _userManager.GetUserById(new Guid(request.UserId)); + + if (user == null) + { + throw new ArgumentException("User not found"); + } + + var items = parentsFolders.SelectMany(i => i.GetRecursiveChildren(user)) + .OfType(); + + // Avoid implicitly captured closure + var episodes = GetNextUpEpisodes(request, user, items); + + return GetResult(episodes, null, request); + } + + private IEnumerable GetAllLibraryItems(User user, string[] parentIds) + { + if (parentIds.Length > 0) + { + return parentIds.SelectMany(i => + { + var folder = (Folder)_libraryManager.GetItemById(new Guid(i)); + + return folder.GetRecursiveChildren(user); + + }); + } + + if (user == null) + { + throw new ArgumentException("User not found"); + } + + return user.RootFolder.GetRecursiveChildren(user); + } + + public IEnumerable GetNextUpEpisodes(NextUpQuery request, User user, IEnumerable series) + { + // Avoid implicitly captured closure + var currentUser = user; + + return FilterSeries(request, series) + .AsParallel() + .Select(i => GetNextUp(i, currentUser)) + .Where(i => i.Item1 != null) + .OrderByDescending(i => + { + var episode = i.Item1; + + var seriesUserData = _userDataManager.GetUserData(user.Id, episode.Series.GetUserDataKey()); + + if (seriesUserData.IsFavorite) + { + return 2; + } + + if (seriesUserData.Likes.HasValue) + { + return seriesUserData.Likes.Value ? 1 : -1; + } + + return 0; + }) + .ThenByDescending(i => i.Item2) + .ThenByDescending(i => i.Item1.PremiereDate ?? DateTime.MinValue) + .Select(i => i.Item1); + } + + /// + /// Gets the next up. + /// + /// The series. + /// The user. + /// Task{Episode}. + private Tuple GetNextUp(Series series, User user) + { + // Get them in display order, then reverse + var allEpisodes = series.GetSeasons(user, true, true) + .SelectMany(i => i.GetEpisodes(user, true, true)) + .Reverse() + .ToList(); + + Episode lastWatched = null; + var lastWatchedDate = DateTime.MinValue; + Episode nextUp = null; + + // Go back starting with the most recent episodes + foreach (var episode in allEpisodes) + { + var userData = _userDataManager.GetUserData(user.Id, episode.GetUserDataKey()); + + if (userData.Played) + { + if (lastWatched != null || nextUp == null) + { + break; + } + + lastWatched = episode; + lastWatchedDate = userData.LastPlayedDate ?? DateTime.MinValue; + } + else + { + if (episode.LocationType != LocationType.Virtual) + { + nextUp = episode; + } + } + } + + if (lastWatched != null) + { + return new Tuple(nextUp, lastWatchedDate); + } + + return new Tuple(null, lastWatchedDate); + } + + private IEnumerable FilterSeries(NextUpQuery request, IEnumerable items) + { + if (!string.IsNullOrWhiteSpace(request.SeriesId)) + { + var id = new Guid(request.SeriesId); + + items = items.Where(i => i.Id == id); + } + + return items; + } + + private QueryResult GetResult(IEnumerable items, int? totalRecordLimit, NextUpQuery query) + { + var itemsArray = totalRecordLimit.HasValue ? items.Take(totalRecordLimit.Value).ToArray() : items.ToArray(); + var totalCount = itemsArray.Length; + + if (query.Limit.HasValue) + { + itemsArray = itemsArray.Skip(query.StartIndex ?? 0).Take(query.Limit.Value).ToArray(); + } + else if (query.StartIndex.HasValue) + { + itemsArray = itemsArray.Skip(query.StartIndex.Value).ToArray(); + } + + return new QueryResult + { + TotalRecordCount = totalCount, + Items = itemsArray + }; + } + } +} diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 51dccddff1..e97c212042 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -38,6 +38,7 @@ using MediaBrowser.Controller.Sorting; using MediaBrowser.Controller.Subtitles; using MediaBrowser.Controller.Sync; using MediaBrowser.Controller.Themes; +using MediaBrowser.Controller.TV; using MediaBrowser.Dlna; using MediaBrowser.Dlna.ConnectionManager; using MediaBrowser.Dlna.ContentDirectory; @@ -79,6 +80,7 @@ using MediaBrowser.Server.Implementations.ServerManager; using MediaBrowser.Server.Implementations.Session; using MediaBrowser.Server.Implementations.Sync; using MediaBrowser.Server.Implementations.Themes; +using MediaBrowser.Server.Implementations.TV; using MediaBrowser.ServerApplication.FFMpeg; using MediaBrowser.ServerApplication.IO; using MediaBrowser.ServerApplication.Native; @@ -213,10 +215,11 @@ namespace MediaBrowser.ServerApplication private ISubtitleManager SubtitleManager { get; set; } private IChapterManager ChapterManager { get; set; } - private IUserViewManager UserViewManager { get; set; } + internal IUserViewManager UserViewManager { get; set; } private IAuthenticationRepository AuthenticationRepository { get; set; } private ISyncRepository SyncRepository { get; set; } + private ITVSeriesManager TVSeriesManager { get; set; } /// /// Initializes a new instance of the class. @@ -466,6 +469,9 @@ namespace MediaBrowser.ServerApplication ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, Logger, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, LocalizationManager); RegisterSingleInstance(ChannelManager); + TVSeriesManager = new TVSeriesManager(UserManager, UserDataManager, LibraryManager); + RegisterSingleInstance(TVSeriesManager); + var appThemeManager = new AppThemeManager(ApplicationPaths, FileSystemManager, JsonSerializer, Logger); RegisterSingleInstance(appThemeManager); @@ -487,7 +493,7 @@ namespace MediaBrowser.ServerApplication UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, FileSystemManager, UserManager, ChannelManager, LiveTvManager, ApplicationPaths, playlistManager); RegisterSingleInstance(UserViewManager); - var contentDirectory = new ContentDirectory(dlnaManager, UserDataManager, ImageProcessor, LibraryManager, ServerConfigurationManager, UserManager, LogManager.GetLogger("UpnpContentDirectory"), HttpClient, UserViewManager, ChannelManager); + var contentDirectory = new ContentDirectory(dlnaManager, UserDataManager, ImageProcessor, LibraryManager, ServerConfigurationManager, UserManager, LogManager.GetLogger("UpnpContentDirectory"), HttpClient); RegisterSingleInstance(contentDirectory); NotificationManager = new NotificationManager(LogManager, UserManager, ServerConfigurationManager); @@ -682,9 +688,10 @@ namespace MediaBrowser.ServerApplication Folder.UserManager = UserManager; BaseItem.FileSystem = FileSystemManager; BaseItem.UserDataManager = UserDataManager; - ChannelVideoItem.ChannelManager = ChannelManager; + BaseItem.ChannelManager = ChannelManager; BaseItem.LiveTvManager = LiveTvManager; - UserView.UserViewManager = UserViewManager; + Folder.UserViewManager = UserViewManager; + UserView.TVSeriesManager = TVSeriesManager; } /// diff --git a/MediaBrowser.ServerApplication/LibraryViewer.cs b/MediaBrowser.ServerApplication/LibraryViewer.cs index 26cf243daf..e89cbd0c00 100644 --- a/MediaBrowser.ServerApplication/LibraryViewer.cs +++ b/MediaBrowser.ServerApplication/LibraryViewer.cs @@ -1,6 +1,9 @@ -using MediaBrowser.Controller.Entities; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; +using MediaBrowser.Model.Library; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Serialization; using System; @@ -16,17 +19,17 @@ namespace MediaBrowser.ServerApplication { private readonly IJsonSerializer _jsonSerializer; private readonly ILibraryManager _libraryManager; - private readonly IItemRepository _itemRepository; + private readonly IUserViewManager _userViewManager; private User _currentUser; - public LibraryViewer(IJsonSerializer jsonSerializer, IUserManager userManager, ILibraryManager libraryManager, IItemRepository itemRepo) + public LibraryViewer(IJsonSerializer jsonSerializer, IUserManager userManager, ILibraryManager libraryManager, IUserViewManager userViewManager) { InitializeComponent(); _jsonSerializer = jsonSerializer; _libraryManager = libraryManager; - _itemRepository = itemRepo; + _userViewManager = userViewManager; foreach (var user in userManager.Users) selectUser.Items.Add(user); @@ -72,23 +75,50 @@ namespace MediaBrowser.ServerApplication LoadTree(); } + private IEnumerable GetItems(Folder parent, User user) + { + if (parent == null) + { + var task = _userViewManager.GetUserViews(new UserViewQuery + { + UserId = user.Id.ToString("N") + + }, CancellationToken.None); + + task.RunSynchronously(); + + return task.Result; + } + else + { + var task = parent.GetUserItems(new UserItemsQuery + { + User = user, + SortBy = new[] { ItemSortBy.SortName } + + }); + + task.RunSynchronously(); + + return task.Result.Items; + } + } + private void LoadTree() { treeView1.Nodes.Clear(); var isPhysical = _currentUser.Name == "Physical"; - IEnumerable children = isPhysical ? new[] { _libraryManager.RootFolder } : _libraryManager.RootFolder.GetChildren(_currentUser, true); - children = OrderByName(children, _currentUser); + IEnumerable children = isPhysical ? new[] { _libraryManager.RootFolder } : GetItems(null, _currentUser); - foreach (Folder folder in children) + foreach (var folder in children.OfType()) { - var currentFolder = folder; var node = new TreeNode { Tag = currentFolder }; - var subChildren = isPhysical ? currentFolder.Children : currentFolder.GetChildren(_currentUser, true); - subChildren = OrderByName(subChildren, _currentUser); + var subChildren = isPhysical ? currentFolder.Children.OrderBy(i => i.SortName) : GetItems(currentFolder, _currentUser); + AddChildren(node, subChildren, _currentUser, isPhysical); node.Text = currentFolder.Name + " (" + node.Nodes.Count + ")"; @@ -111,9 +141,9 @@ namespace MediaBrowser.ServerApplication var subFolder = item as Folder; if (subFolder != null) { - var subChildren = isPhysical ? subFolder.Children : subFolder.GetChildren(_currentUser, true); + var subChildren = isPhysical ? subFolder.Children.OrderBy(i => i.SortName) : GetItems(subFolder, _currentUser); - AddChildren(node, OrderBy(subChildren, user, ItemSortBy.SortName), user, isPhysical); + AddChildren(node, subChildren, user, isPhysical); node.Text = item.Name + " (" + node.Nodes.Count + ")"; } else @@ -124,28 +154,6 @@ namespace MediaBrowser.ServerApplication } } - /// - /// Orders the name of the by. - /// - /// The items. - /// The user. - /// IEnumerable{BaseItem}. - private IEnumerable OrderByName(IEnumerable items, User user) - { - return OrderBy(items, user, ItemSortBy.SortName); - } - - /// - /// Orders the name of the by. - /// - /// The items. - /// The user. - /// IEnumerable{BaseItem}. - private IEnumerable OrderBy(IEnumerable items, User user, string order) - { - return _libraryManager.Sort(items, user, new[] { order }, SortOrder.Ascending); - } - /// /// The INDEN t_ STRING /// diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs index 81c04060a3..6e6377a643 100644 --- a/MediaBrowser.ServerApplication/MainStartup.cs +++ b/MediaBrowser.ServerApplication/MainStartup.cs @@ -249,7 +249,7 @@ namespace MediaBrowser.ServerApplication { //Application.EnableVisualStyles(); //Application.SetCompatibleTextRenderingDefault(false); - _serverNotifyIcon = new ServerNotifyIcon(_appHost.LogManager, _appHost, _appHost.ServerConfigurationManager, _appHost.UserManager, _appHost.LibraryManager, _appHost.JsonSerializer, _appHost.ItemRepository, _appHost.LocalizationManager); + _serverNotifyIcon = new ServerNotifyIcon(_appHost.LogManager, _appHost, _appHost.ServerConfigurationManager, _appHost.UserManager, _appHost.LibraryManager, _appHost.JsonSerializer, _appHost.LocalizationManager, _appHost.UserViewManager); Application.Run(); } diff --git a/MediaBrowser.ServerApplication/ServerNotifyIcon.cs b/MediaBrowser.ServerApplication/ServerNotifyIcon.cs index 47a4be8e36..8740b707b8 100644 --- a/MediaBrowser.ServerApplication/ServerNotifyIcon.cs +++ b/MediaBrowser.ServerApplication/ServerNotifyIcon.cs @@ -39,7 +39,7 @@ namespace MediaBrowser.ServerApplication private readonly IUserManager _userManager; private readonly ILibraryManager _libraryManager; private readonly IJsonSerializer _jsonSerializer; - private readonly IItemRepository _itemRepository; + private readonly IUserViewManager _userViewManager; private readonly ILocalizationManager _localization; private LogForm _logForm; @@ -61,11 +61,11 @@ namespace MediaBrowser.ServerApplication IServerConfigurationManager configurationManager, IUserManager userManager, ILibraryManager libraryManager, IJsonSerializer jsonSerializer, - IItemRepository itemRepo, ILocalizationManager localization) + ILocalizationManager localization, IUserViewManager userViewManager) { _logger = logManager.GetLogger("MainWindow"); - _itemRepository = itemRepo; _localization = localization; + _userViewManager = userViewManager; _appHost = appHost; _logManager = logManager; _configurationManager = configurationManager; @@ -318,7 +318,7 @@ namespace MediaBrowser.ServerApplication void cmdLibraryExplorer_Click(object sender, EventArgs e) { - new LibraryViewer(_jsonSerializer, _userManager, _libraryManager, _itemRepository).Show(); + new LibraryViewer(_jsonSerializer, _userManager, _libraryManager, _userViewManager).Show(); } void cmdRestart_Click(object sender, EventArgs e) diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 32cb882180..864a2f70fb 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -2136,7 +2136,7 @@ - +