aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller/Entities
diff options
context:
space:
mode:
authorLuke Pulverenti <luke.pulverenti@gmail.com>2014-03-15 18:52:43 -0400
committerLuke Pulverenti <luke.pulverenti@gmail.com>2014-03-15 18:52:43 -0400
commitbf30936550a0b9be69e646a1b27988914ce9ec4a (patch)
tree01f492d79e714ff2efff69a54a514c449716def5 /MediaBrowser.Controller/Entities
parentd7cfa0d22cad210fb37dd8aa6bcf41b416129e58 (diff)
#712 - Support grouping multiple versions of a movie
Diffstat (limited to 'MediaBrowser.Controller/Entities')
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs77
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs112
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs161
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)