From a19bfc8f07790c9108e09606b590b78fbcb512e6 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 5 Jul 2013 09:47:10 -0400 Subject: [PATCH] Added support for linked children --- .../UserLibrary/BaseItemsByNameService.cs | 2 +- MediaBrowser.Api/UserLibrary/ItemsService.cs | 4 +- MediaBrowser.Controller/Dto/DtoBuilder.cs | 15 +-- MediaBrowser.Controller/Entities/BaseItem.cs | 30 ++--- MediaBrowser.Controller/Entities/Folder.cs | 116 ++++++++++++++++-- .../Entities/LinkedChild.cs | 38 ++++++ .../Entities/Movies/BoxSet.cs | 8 +- .../MediaBrowser.Controller.csproj | 1 + MediaBrowser.Model/Dto/ImageOptions.cs | 1 + .../EntryPoints/LibraryChangedNotifier.cs | 2 +- MediaBrowser.ServerApplication/App.xaml.cs | 9 +- .../LibraryExplorer.xaml.cs | 12 +- 12 files changed, 194 insertions(+), 44 deletions(-) create mode 100644 MediaBrowser.Controller/Entities/LinkedChild.cs diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs index b93d339ced..df728ee0d3 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs @@ -73,7 +73,7 @@ namespace MediaBrowser.Api.UserLibrary if (request.UserId.HasValue) { - items = request.Recursive ? folder.GetRecursiveChildren(user) : folder.GetChildren(user); + items = request.Recursive ? folder.GetRecursiveChildren(user) : folder.GetChildren(user, true); } else { diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index 10e56af52c..8497901f63 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -305,7 +305,7 @@ namespace MediaBrowser.Api.UserLibrary return ((Folder)item).GetRecursiveChildren(user); } - return ((Folder)item).GetChildren(user, request.IndexBy); + return ((Folder)item).GetChildren(user, true, request.IndexBy); } /// @@ -433,7 +433,7 @@ namespace MediaBrowser.Api.UserLibrary { var item = DtoBuilder.GetItemByClientId(request.AdjacentTo, _userManager, _libraryManager); - var allSiblings = item.Parent.GetChildren(user).OrderBy(i => i.SortName).ToList(); + var allSiblings = item.Parent.GetChildren(user, true).OrderBy(i => i.SortName).ToList(); var index = allSiblings.IndexOf(item); diff --git a/MediaBrowser.Controller/Dto/DtoBuilder.cs b/MediaBrowser.Controller/Dto/DtoBuilder.cs index b321df2d18..b921566aa6 100644 --- a/MediaBrowser.Controller/Dto/DtoBuilder.cs +++ b/MediaBrowser.Controller/Dto/DtoBuilder.cs @@ -127,7 +127,7 @@ namespace MediaBrowser.Controller.Dto var folder = (Folder)item; // Skip sorting since all we want is a count - dto.ChildCount = folder.GetChildren(user).Count(); + dto.ChildCount = folder.GetChildren(user, true).Count(); SetSpecialCounts(folder, user, dto, _userDataRepository); } @@ -555,7 +555,7 @@ namespace MediaBrowser.Controller.Dto double totalPercentPlayed = 0; // Loop through each recursive child - foreach (var child in folder.GetRecursiveChildren(user).Where(i => !i.IsFolder).ToList()) + foreach (var child in folder.GetRecursiveChildren(user, true).Where(i => !i.IsFolder).ToList()) { var userdata = userDataRepository.GetUserData(user.Id, child.GetUserDataKey()); @@ -610,11 +610,6 @@ namespace MediaBrowser.Controller.Dto /// Task. private async Task AttachPeople(BaseItemDto dto, BaseItem item) { - if (item.People == null) - { - return; - } - // Ordering by person type to ensure actors and artists are at the front. // This is taking advantage of the fact that they both begin with A // This should be improved in the future @@ -640,7 +635,7 @@ namespace MediaBrowser.Controller.Dto )).ConfigureAwait(false); - var dictionary = entities.ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase); + var dictionary = entities.Where(i => i != null).ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase); for (var i = 0; i < people.Count; i++) { @@ -698,7 +693,7 @@ namespace MediaBrowser.Controller.Dto )).ConfigureAwait(false); - var dictionary = entities.ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase); + var dictionary = entities.Where(i => i != null).ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase); for (var i = 0; i < studios.Count; i++) { @@ -967,7 +962,7 @@ namespace MediaBrowser.Controller.Dto values.RemoveRange(0, 2); // Get the IndexFolder - var indexFolder = parentFolder.GetChildren(user, indexBy).FirstOrDefault(i => i.Id == indexFolderId) as Folder; + var indexFolder = parentFolder.GetChildren(user, false, indexBy).FirstOrDefault(i => i.Id == indexFolderId) as Folder; // Nested index folder if (values.Count > 0) diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 3202250314..1cbe5b635c 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -617,6 +617,11 @@ namespace MediaBrowser.Controller.Entities try { resolveArgs = ResolveArgs; + + if (!resolveArgs.IsDirectory) + { + return new List(); + } } catch (IOException ex) { @@ -624,11 +629,6 @@ namespace MediaBrowser.Controller.Entities return new List(); } - if (!resolveArgs.IsDirectory) - { - return new List(); - } - var files = new List(); var folder = resolveArgs.GetFileSystemEntryByName(TrailerFolderName); @@ -687,6 +687,11 @@ namespace MediaBrowser.Controller.Entities try { resolveArgs = ResolveArgs; + + if (!resolveArgs.IsDirectory) + { + return new List(); + } } catch (IOException ex) { @@ -694,11 +699,6 @@ namespace MediaBrowser.Controller.Entities return new List(); } - if (!resolveArgs.IsDirectory) - { - return new List(); - } - var files = new List(); var folder = resolveArgs.GetFileSystemEntryByName(ThemeSongsFolderName); @@ -747,6 +747,11 @@ namespace MediaBrowser.Controller.Entities try { resolveArgs = ResolveArgs; + + if (!resolveArgs.IsDirectory) + { + return new List public class Folder : BaseItem { + public Folder() + { + LinkedChildren = new List(); + } + /// /// Gets a value indicating whether this instance is folder. /// @@ -83,6 +89,13 @@ namespace MediaBrowser.Controller.Entities return (userId + DisplayPreferencesId.ToString()).GetMD5(); } + public List LinkedChildren { get; set; } + + protected virtual bool SupportsLinkedChildren + { + get { return false; } + } + /// /// Adds the child. /// @@ -878,10 +891,11 @@ namespace MediaBrowser.Controller.Entities /// Gets allowed children of an item /// /// The user. + /// if set to true [include linked children]. /// The index by. /// IEnumerable{BaseItem}. /// - public virtual IEnumerable GetChildren(User user, string indexBy = null) + public virtual IEnumerable GetChildren(User user, bool includeLinkedChildren, string indexBy = null) { if (user == null) { @@ -889,7 +903,7 @@ namespace MediaBrowser.Controller.Entities } //the true root should return our users root folder children - if (IsPhysicalRoot) return user.RootFolder.GetChildren(user, indexBy); + if (IsPhysicalRoot) return user.RootFolder.GetChildren(user, includeLinkedChildren, indexBy); IEnumerable result = null; @@ -898,24 +912,37 @@ namespace MediaBrowser.Controller.Entities result = GetIndexedChildren(user, indexBy); } + if (result != null) + { + return result; + } + + var children = Children; + + if (includeLinkedChildren) + { + children = children.Concat(GetLinkedChildren()); + } + // If indexed is false or the indexing function is null - return result ?? (Children.Where(c => c.IsVisible(user))); + return children.Where(c => c.IsVisible(user)); } /// /// Gets allowed recursive children of an item /// /// The user. + /// if set to true [include linked children]. /// IEnumerable{BaseItem}. /// - public IEnumerable GetRecursiveChildren(User user) + public IEnumerable GetRecursiveChildren(User user, bool includeLinkedChildren = false) { if (user == null) { throw new ArgumentNullException(); } - foreach (var item in GetChildren(user)) + foreach (var item in GetChildren(user, includeLinkedChildren)) { yield return item; @@ -923,7 +950,7 @@ namespace MediaBrowser.Controller.Entities if (subFolder != null) { - foreach (var subitem in subFolder.GetRecursiveChildren(user)) + foreach (var subitem in subFolder.GetRecursiveChildren(user, includeLinkedChildren)) { yield return subitem; } @@ -931,6 +958,81 @@ namespace MediaBrowser.Controller.Entities } } + /// + /// Gets the linked children. + /// + /// IEnumerable{BaseItem}. + public IEnumerable GetLinkedChildren() + { + return LinkedChildren + .Select(i => LibraryManager.RootFolder.FindByPath(i.Path)) + .Where(i => i != null); + } + + public override async Task RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true, bool resetResolveArgs = true) + { + var changed = await base.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders, resetResolveArgs).ConfigureAwait(false); + + return changed || (SupportsLinkedChildren && RefreshLinkedChildren()); + } + + /// + /// Refreshes the linked children. + /// + /// true if XXXX, false otherwise + private bool RefreshLinkedChildren() + { + ItemResolveArgs resolveArgs; + + try + { + resolveArgs = ResolveArgs; + + if (!resolveArgs.IsDirectory) + { + return false; + } + } + catch (IOException ex) + { + Logger.ErrorException("Error getting ResolveArgs for {0}", ex, Path); + return false; + } + + var currentManualLinks = LinkedChildren.Where(i => i.Type == LinkedChildType.Manual).ToList(); + var currentShortcutLinks = LinkedChildren.Where(i => i.Type == LinkedChildType.Shortcut).ToList(); + + var newShortcutLinks = resolveArgs.FileSystemChildren + .Where(i => (i.Attributes & FileAttributes.Directory) != FileAttributes.Directory && FileSystem.IsShortcut(i.FullName)) + .Select(i => + { + try + { + return new LinkedChild + { + Path = FileSystem.ResolveShortcut(i.FullName), + Type = LinkedChildType.Shortcut + }; + } + catch (IOException ex) + { + Logger.ErrorException("Error resolving shortcut {0}", ex, i.FullName); + return null; + } + }) + .Where(i => i != null) + .ToList(); + + if (!newShortcutLinks.SequenceEqual(currentShortcutLinks)) + { + newShortcutLinks.AddRange(currentManualLinks); + LinkedChildren = newShortcutLinks; + return true; + } + + return false; + } + /// /// Folders need to validate and refresh /// @@ -954,7 +1056,7 @@ namespace MediaBrowser.Controller.Entities public override async Task SetPlayedStatus(User user, bool wasPlayed, IUserDataRepository userManager) { // Sweep through recursively and update status - var tasks = GetRecursiveChildren(user).Where(i => !i.IsFolder).Select(c => c.SetPlayedStatus(user, wasPlayed, userManager)); + var tasks = GetRecursiveChildren(user, true).Where(i => !i.IsFolder).Select(c => c.SetPlayedStatus(user, wasPlayed, userManager)); await Task.WhenAll(tasks).ConfigureAwait(false); } diff --git a/MediaBrowser.Controller/Entities/LinkedChild.cs b/MediaBrowser.Controller/Entities/LinkedChild.cs new file mode 100644 index 0000000000..edc5a7ac88 --- /dev/null +++ b/MediaBrowser.Controller/Entities/LinkedChild.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections; + +namespace MediaBrowser.Controller.Entities +{ + public class LinkedChild + { + public string Path { get; set; } + public LinkedChildType Type { get; set; } + } + + public enum LinkedChildType + { + Manual = 1, + Shortcut = 2 + } + + public class LinkedChildComparer : IComparer + { + public int Compare(object x, object y) + { + var a = (LinkedChild)x; + + var b = (LinkedChild)y; + + if (!string.Equals(a.Path, b.Path, StringComparison.OrdinalIgnoreCase)) + { + return string.Compare(a.Path, b.Path, StringComparison.OrdinalIgnoreCase); + } + if (a.Type != b.Type) + { + return a.Type.CompareTo(b.Type); + } + + return 0; + } + } +} diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index 34f09b4b09..2ee3ccffe9 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -6,6 +6,12 @@ namespace MediaBrowser.Controller.Entities.Movies /// public class BoxSet : Folder { - + protected override bool SupportsLinkedChildren + { + get + { + return true; + } + } } } diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 502c7a7b82..6910722d68 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -78,6 +78,7 @@ + diff --git a/MediaBrowser.Model/Dto/ImageOptions.cs b/MediaBrowser.Model/Dto/ImageOptions.cs index 80614831a2..63c0f70abe 100644 --- a/MediaBrowser.Model/Dto/ImageOptions.cs +++ b/MediaBrowser.Model/Dto/ImageOptions.cs @@ -72,6 +72,7 @@ namespace MediaBrowser.Model.Dto public ImageOptions() { Quality = 100; + EnableImageEnhancers = true; } } } diff --git a/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 71d0144684..02ecb4fca8 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -229,7 +229,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints { var user = _userManager.GetUserById(userId); - var collections = user.RootFolder.GetChildren(user).ToList(); + var collections = user.RootFolder.GetChildren(user, true).ToList(); var allRecursiveChildren = user.RootFolder.GetRecursiveChildren(user).ToDictionary(i => i.Id); diff --git a/MediaBrowser.ServerApplication/App.xaml.cs b/MediaBrowser.ServerApplication/App.xaml.cs index 5ed2537637..db923e5e21 100644 --- a/MediaBrowser.ServerApplication/App.xaml.cs +++ b/MediaBrowser.ServerApplication/App.xaml.cs @@ -255,7 +255,14 @@ namespace MediaBrowser.ServerApplication process.Exited += ProcessExited; - process.Start(); + try + { + process.Start(); + } + catch (Exception ex) + { + MessageBox.Show("There was an error launching your web browser. Please check your defualt browser settings."); + } } /// diff --git a/MediaBrowser.ServerApplication/LibraryExplorer.xaml.cs b/MediaBrowser.ServerApplication/LibraryExplorer.xaml.cs index 577ac93bbb..6dabd85af2 100644 --- a/MediaBrowser.ServerApplication/LibraryExplorer.xaml.cs +++ b/MediaBrowser.ServerApplication/LibraryExplorer.xaml.cs @@ -90,7 +90,7 @@ namespace MediaBrowser.ServerApplication Cursor = Cursors.Wait; await Task.Run(() => { - IEnumerable children = CurrentUser.Name == "Physical" ? _libraryManager.RootFolder.Children : _libraryManager.RootFolder.GetChildren(CurrentUser); + IEnumerable children = CurrentUser.Name == "Physical" ? _libraryManager.RootFolder.Children : _libraryManager.RootFolder.GetChildren(CurrentUser, true); children = OrderByName(children, CurrentUser); foreach (Folder folder in children) @@ -102,7 +102,7 @@ namespace MediaBrowser.ServerApplication var prefs = ddlProfile.SelectedItem != null ? _displayPreferencesManager.GetDisplayPreferences(currentFolder.GetDisplayPreferencesId((ddlProfile.SelectedItem as User).Id)) ?? new DisplayPreferences { SortBy = ItemSortBy.SortName } : new DisplayPreferences { SortBy = ItemSortBy.SortName }; var node = new TreeViewItem { Tag = currentFolder }; - var subChildren = currentFolder.GetChildren(CurrentUser, prefs.IndexBy); + var subChildren = currentFolder.GetChildren(CurrentUser, true, prefs.IndexBy); subChildren = OrderByName(subChildren, CurrentUser); AddChildren(node, subChildren, CurrentUser); node.Header = currentFolder.Name + " (" + @@ -153,8 +153,8 @@ namespace MediaBrowser.ServerApplication if (subFolder != null) { var prefs = _displayPreferencesManager.GetDisplayPreferences(subFolder.GetDisplayPreferencesId(user.Id)); - - AddChildren(node, OrderBy(subFolder.GetChildren(user), user, prefs.SortBy), user); + + AddChildren(node, OrderBy(subFolder.GetChildren(user, true), user, prefs.SortBy), user); node.Header = item.Name + " (" + node.Items.Count + ")"; } else @@ -374,7 +374,7 @@ namespace MediaBrowser.ServerApplication //re-build the current item's children as an index prefs.IndexBy = ddlIndexBy.SelectedItem as string; treeItem.Items.Clear(); - AddChildren(treeItem, OrderBy(folder.GetChildren(CurrentUser, prefs.IndexBy), CurrentUser, prefs.SortBy), CurrentUser); + AddChildren(treeItem, OrderBy(folder.GetChildren(CurrentUser, true, prefs.IndexBy), CurrentUser, prefs.SortBy), CurrentUser); treeItem.Header = folder.Name + "(" + treeItem.Items.Count + ")"; Cursor = Cursors.Arrow; @@ -415,7 +415,7 @@ namespace MediaBrowser.ServerApplication //re-sort prefs.SortBy = ddlSortBy.SelectedItem as string; treeItem.Items.Clear(); - AddChildren(treeItem, OrderBy(folder.GetChildren(CurrentUser, prefs.IndexBy), CurrentUser, prefs.SortBy ?? ItemSortBy.SortName), CurrentUser); + AddChildren(treeItem, OrderBy(folder.GetChildren(CurrentUser, true, prefs.IndexBy), CurrentUser, prefs.SortBy ?? ItemSortBy.SortName), CurrentUser); treeItem.Header = folder.Name + "(" + treeItem.Items.Count + ")"; Cursor = Cursors.Arrow;