diff options
| author | Luke Pulverenti <luke.pulverenti@gmail.com> | 2014-03-15 18:52:43 -0400 |
|---|---|---|
| committer | Luke Pulverenti <luke.pulverenti@gmail.com> | 2014-03-15 18:52:43 -0400 |
| commit | bf30936550a0b9be69e646a1b27988914ce9ec4a (patch) | |
| tree | 01f492d79e714ff2efff69a54a514c449716def5 /MediaBrowser.Controller/Entities | |
| parent | d7cfa0d22cad210fb37dd8aa6bcf41b416129e58 (diff) | |
#712 - Support grouping multiple versions of a movie
Diffstat (limited to 'MediaBrowser.Controller/Entities')
| -rw-r--r-- | MediaBrowser.Controller/Entities/BaseItem.cs | 77 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/Folder.cs | 112 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/Video.cs | 161 |
3 files changed, 260 insertions, 90 deletions
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index e0c792307e..be64d20c33 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -955,6 +955,83 @@ namespace MediaBrowser.Controller.Entities } /// <summary> + /// Gets the linked child. + /// </summary> + /// <param name="info">The info.</param> + /// <returns>BaseItem.</returns> + protected BaseItem GetLinkedChild(LinkedChild info) + { + // First get using the cached Id + if (info.ItemId.HasValue) + { + if (info.ItemId.Value == Guid.Empty) + { + return null; + } + + var itemById = LibraryManager.GetItemById(info.ItemId.Value); + + if (itemById != null) + { + return itemById; + } + } + + var item = FindLinkedChild(info); + + // If still null, log + if (item == null) + { + // Don't keep searching over and over + info.ItemId = Guid.Empty; + } + else + { + // Cache the id for next time + info.ItemId = item.Id; + } + + return item; + } + + private BaseItem FindLinkedChild(LinkedChild info) + { + if (!string.IsNullOrEmpty(info.Path)) + { + var itemByPath = LibraryManager.RootFolder.FindByPath(info.Path); + + if (itemByPath == null) + { + Logger.Warn("Unable to find linked item at path {0}", info.Path); + } + + return itemByPath; + } + + if (!string.IsNullOrWhiteSpace(info.ItemName) && !string.IsNullOrWhiteSpace(info.ItemType)) + { + return LibraryManager.RootFolder.RecursiveChildren.FirstOrDefault(i => + { + if (string.Equals(i.Name, info.ItemName, StringComparison.OrdinalIgnoreCase)) + { + if (string.Equals(i.GetType().Name, info.ItemType, StringComparison.OrdinalIgnoreCase)) + { + if (info.ItemYear.HasValue) + { + return info.ItemYear.Value == (i.ProductionYear ?? -1); + } + return true; + } + } + + return false; + }); + } + + return null; + } + + /// <summary> /// Adds a person to the item /// </summary> /// <param name="person">The person.</param> diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index ee371680ef..45daaba0b2 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -354,20 +354,45 @@ namespace MediaBrowser.Controller.Entities private bool IsValidFromResolver(BaseItem current, BaseItem newItem) { - var currentAsPlaceHolder = current as ISupportsPlaceHolders; + var currentAsVideo = current as Video; - if (currentAsPlaceHolder != null) + if (currentAsVideo != null) { - var newHasPlaceHolder = newItem as ISupportsPlaceHolders; + var newAsVideo = newItem as Video; - if (newHasPlaceHolder != null) + if (newAsVideo != null) { - if (currentAsPlaceHolder.IsPlaceHolder != newHasPlaceHolder.IsPlaceHolder) + if (currentAsVideo.IsPlaceHolder != newAsVideo.IsPlaceHolder) + { + return false; + } + if (currentAsVideo.IsMultiPart != newAsVideo.IsMultiPart) + { + return false; + } + if (currentAsVideo.HasLocalAlternateVersions != newAsVideo.HasLocalAlternateVersions) { return false; } } } + else + { + var currentAsPlaceHolder = current as ISupportsPlaceHolders; + + if (currentAsPlaceHolder != null) + { + var newHasPlaceHolder = newItem as ISupportsPlaceHolders; + + if (newHasPlaceHolder != null) + { + if (currentAsPlaceHolder.IsPlaceHolder != newHasPlaceHolder.IsPlaceHolder) + { + return false; + } + } + } + } return current.IsInMixedFolder == newItem.IsInMixedFolder; } @@ -898,83 +923,6 @@ namespace MediaBrowser.Controller.Entities .Where(i => i != null); } - /// <summary> - /// Gets the linked child. - /// </summary> - /// <param name="info">The info.</param> - /// <returns>BaseItem.</returns> - private BaseItem GetLinkedChild(LinkedChild info) - { - // First get using the cached Id - if (info.ItemId.HasValue) - { - if (info.ItemId.Value == Guid.Empty) - { - return null; - } - - var itemById = LibraryManager.GetItemById(info.ItemId.Value); - - if (itemById != null) - { - return itemById; - } - } - - var item = FindLinkedChild(info); - - // If still null, log - if (item == null) - { - // Don't keep searching over and over - info.ItemId = Guid.Empty; - } - else - { - // Cache the id for next time - info.ItemId = item.Id; - } - - return item; - } - - private BaseItem FindLinkedChild(LinkedChild info) - { - if (!string.IsNullOrEmpty(info.Path)) - { - var itemByPath = LibraryManager.RootFolder.FindByPath(info.Path); - - if (itemByPath == null) - { - Logger.Warn("Unable to find linked item at path {0}", info.Path); - } - - return itemByPath; - } - - if (!string.IsNullOrWhiteSpace(info.ItemName) && !string.IsNullOrWhiteSpace(info.ItemType)) - { - return LibraryManager.RootFolder.RecursiveChildren.FirstOrDefault(i => - { - if (string.Equals(i.Name, info.ItemName, StringComparison.OrdinalIgnoreCase)) - { - if (string.Equals(i.GetType().Name, info.ItemType, StringComparison.OrdinalIgnoreCase)) - { - if (info.ItemYear.HasValue) - { - return info.ItemYear.Value == (i.ProductionYear ?? -1); - } - return true; - } - } - - return false; - }); - } - - return null; - } - protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken) { var changesFound = false; diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index 10034d7e5f..e30458dd8f 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -19,15 +19,63 @@ namespace MediaBrowser.Controller.Entities public class Video : BaseItem, IHasMediaStreams, IHasAspectRatio, IHasTags, ISupportsPlaceHolders { public bool IsMultiPart { get; set; } + public bool HasLocalAlternateVersions { get; set; } public List<Guid> AdditionalPartIds { get; set; } + public List<Guid> AlternateVersionIds { get; set; } public Video() { PlayableStreamFileNames = new List<string>(); AdditionalPartIds = new List<Guid>(); + AlternateVersionIds = new List<Guid>(); Tags = new List<string>(); SubtitleFiles = new List<string>(); + LinkedAlternateVersions = new List<LinkedChild>(); + } + + [IgnoreDataMember] + public bool HasAlternateVersions + { + get + { + return HasLocalAlternateVersions || LinkedAlternateVersions.Count > 0; + } + } + + public List<LinkedChild> LinkedAlternateVersions { get; set; } + + /// <summary> + /// Gets the linked children. + /// </summary> + /// <returns>IEnumerable{BaseItem}.</returns> + public IEnumerable<BaseItem> GetAlternateVersions() + { + var filesWithinSameDirectory = AlternateVersionIds + .Select(i => LibraryManager.GetItemById(i)) + .Where(i => i != null) + .OfType<Video>(); + + var linkedVersions = LinkedAlternateVersions + .Select(GetLinkedChild) + .Where(i => i != null) + .OfType<Video>(); + + return filesWithinSameDirectory.Concat(linkedVersions) + .OrderBy(i => i.SortName); + } + + /// <summary> + /// Gets the additional parts. + /// </summary> + /// <returns>IEnumerable{Video}.</returns> + public IEnumerable<Video> GetAdditionalParts() + { + return AdditionalPartIds + .Select(i => LibraryManager.GetItemById(i)) + .Where(i => i != null) + .OfType<Video>() + .OrderBy(i => i.SortName); } /// <summary> @@ -43,13 +91,13 @@ namespace MediaBrowser.Controller.Entities public bool HasSubtitles { get; set; } public bool IsPlaceHolder { get; set; } - + /// <summary> /// Gets or sets the tags. /// </summary> /// <value>The tags.</value> public List<string> Tags { get; set; } - + /// <summary> /// Gets or sets the video bit rate. /// </summary> @@ -167,22 +215,53 @@ namespace MediaBrowser.Controller.Entities { var hasChanges = await base.RefreshedOwnedItems(options, fileSystemChildren, cancellationToken).ConfigureAwait(false); - // Must have a parent to have additional parts + // Must have a parent to have additional parts or alternate versions // In other words, it must be part of the Parent/Child tree // The additional parts won't have additional parts themselves - if (IsMultiPart && LocationType == LocationType.FileSystem && Parent != null) + if (LocationType == LocationType.FileSystem && Parent != null) { - var additionalPartsChanged = await RefreshAdditionalParts(options, fileSystemChildren, cancellationToken).ConfigureAwait(false); + if (IsMultiPart) + { + var additionalPartsChanged = await RefreshAdditionalParts(options, fileSystemChildren, cancellationToken).ConfigureAwait(false); - if (additionalPartsChanged) + if (additionalPartsChanged) + { + hasChanges = true; + } + } + else { - hasChanges = true; + RefreshLinkedAlternateVersions(); + + if (HasLocalAlternateVersions) + { + var additionalPartsChanged = await RefreshAlternateVersionsWithinSameDirectory(options, fileSystemChildren, cancellationToken).ConfigureAwait(false); + + if (additionalPartsChanged) + { + hasChanges = true; + } + } } } return hasChanges; } + private bool RefreshLinkedAlternateVersions() + { + foreach (var child in LinkedAlternateVersions) + { + // Reset the cached value + if (child.ItemId.HasValue && child.ItemId.Value == Guid.Empty) + { + child.ItemId = null; + } + } + + return false; + } + /// <summary> /// Refreshes the additional parts. /// </summary> @@ -223,7 +302,7 @@ namespace MediaBrowser.Controller.Entities { if ((i.Attributes & FileAttributes.Directory) == FileAttributes.Directory) { - return !string.Equals(i.FullName, path, StringComparison.OrdinalIgnoreCase) && EntityResolutionHelper.IsVideoFile(i.FullName) && EntityResolutionHelper.IsMultiPartFile(i.Name); + return !string.Equals(i.FullName, path, StringComparison.OrdinalIgnoreCase) && EntityResolutionHelper.IsMultiPartFolder(i.FullName) && EntityResolutionHelper.IsMultiPartFile(i.Name); } return false; @@ -258,6 +337,72 @@ namespace MediaBrowser.Controller.Entities }).OrderBy(i => i.Path).ToList(); } + private async Task<bool> RefreshAlternateVersionsWithinSameDirectory(MetadataRefreshOptions options, IEnumerable<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken) + { + var newItems = LoadAlternateVersionsWithinSameDirectory(fileSystemChildren, options.DirectoryService).ToList(); + + var newItemIds = newItems.Select(i => i.Id).ToList(); + + var itemsChanged = !AlternateVersionIds.SequenceEqual(newItemIds); + + var tasks = newItems.Select(i => i.RefreshMetadata(options, cancellationToken)); + + await Task.WhenAll(tasks).ConfigureAwait(false); + + AlternateVersionIds = newItemIds; + + return itemsChanged; + } + + /// <summary> + /// Loads the additional parts. + /// </summary> + /// <returns>IEnumerable{Video}.</returns> + private IEnumerable<Video> LoadAlternateVersionsWithinSameDirectory(IEnumerable<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService) + { + IEnumerable<FileSystemInfo> files; + + var path = Path; + var currentFilename = System.IO.Path.GetFileNameWithoutExtension(path) ?? string.Empty; + + // Only support this for video files. For folder rips, they'll have to use the linking feature + if (VideoType == VideoType.VideoFile || VideoType == VideoType.Iso) + { + files = fileSystemChildren.Where(i => + { + if ((i.Attributes & FileAttributes.Directory) == FileAttributes.Directory) + { + return false; + } + + return !string.Equals(i.FullName, path, StringComparison.OrdinalIgnoreCase) && + EntityResolutionHelper.IsVideoFile(i.FullName) && + i.Name.StartsWith(currentFilename, StringComparison.OrdinalIgnoreCase); + }); + } + else + { + files = new List<FileSystemInfo>(); + } + + return LibraryManager.ResolvePaths<Video>(files, directoryService, null).Select(video => + { + // Try to retrieve it from the db. If we don't find it, use the resolved version + var dbItem = LibraryManager.GetItemById(video.Id) as Video; + + if (dbItem != null) + { + video = dbItem; + } + + video.ImageInfos = ImageInfos; + + return video; + + // Sort them so that the list can be easily compared for changes + }).OrderBy(i => i.Path).ToList(); + } + public override IEnumerable<string> GetDeletePaths() { if (!IsInMixedFolder) |
