|
|
|
@ -28,10 +28,9 @@ namespace MediaBrowser.Controller.Entities
|
|
|
|
|
Genres = new List<string>();
|
|
|
|
|
Studios = new List<string>();
|
|
|
|
|
People = new List<PersonInfo>();
|
|
|
|
|
BackdropImagePaths = new List<string>();
|
|
|
|
|
Images = new Dictionary<ImageType, string>();
|
|
|
|
|
ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
|
LockedFields = new List<MetadataFields>();
|
|
|
|
|
ImageInfos = new List<ItemImageInfo>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -48,6 +47,12 @@ namespace MediaBrowser.Controller.Entities
|
|
|
|
|
public const string ThemeVideosFolderName = "backdrops";
|
|
|
|
|
public const string XbmcTrailerFileSuffix = "-trailer";
|
|
|
|
|
|
|
|
|
|
public List<ItemImageInfo> ImageInfos { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets a value indicating whether this instance is in mixed folder.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <value><c>true</c> if this instance is in mixed folder; otherwise, <c>false</c>.</value>
|
|
|
|
|
public bool IsInMixedFolder { get; set; }
|
|
|
|
|
|
|
|
|
|
private string _name;
|
|
|
|
@ -160,15 +165,8 @@ namespace MediaBrowser.Controller.Entities
|
|
|
|
|
public string PrimaryImagePath
|
|
|
|
|
{
|
|
|
|
|
get { return this.GetImagePath(ImageType.Primary); }
|
|
|
|
|
set { this.SetImagePath(ImageType.Primary, value); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the images.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <value>The images.</value>
|
|
|
|
|
public Dictionary<ImageType, string> Images { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the date created.
|
|
|
|
|
/// </summary>
|
|
|
|
@ -349,12 +347,6 @@ namespace MediaBrowser.Controller.Entities
|
|
|
|
|
/// <value>The display type of the media.</value>
|
|
|
|
|
public string DisplayMediaType { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the backdrop image paths.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <value>The backdrop image paths.</value>
|
|
|
|
|
public List<string> BackdropImagePaths { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the official rating.
|
|
|
|
|
/// </summary>
|
|
|
|
@ -1162,43 +1154,31 @@ namespace MediaBrowser.Controller.Entities
|
|
|
|
|
/// <exception cref="System.ArgumentException">Backdrops should be accessed using Item.Backdrops</exception>
|
|
|
|
|
public bool HasImage(ImageType type, int imageIndex)
|
|
|
|
|
{
|
|
|
|
|
if (type == ImageType.Backdrop)
|
|
|
|
|
{
|
|
|
|
|
return BackdropImagePaths.Count > imageIndex;
|
|
|
|
|
}
|
|
|
|
|
if (type == ImageType.Screenshot)
|
|
|
|
|
{
|
|
|
|
|
var hasScreenshots = this as IHasScreenshots;
|
|
|
|
|
return hasScreenshots != null && hasScreenshots.ScreenshotImagePaths.Count > imageIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return !string.IsNullOrEmpty(this.GetImagePath(type));
|
|
|
|
|
return GetImageInfo(type, imageIndex) != null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SetImagePath(ImageType type, int index, string path)
|
|
|
|
|
public void SetImagePath(ImageType type, int index, FileInfo file)
|
|
|
|
|
{
|
|
|
|
|
if (type == ImageType.Backdrop)
|
|
|
|
|
if (type == ImageType.Chapter)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentException("Backdrops should be accessed using Item.Backdrops");
|
|
|
|
|
}
|
|
|
|
|
if (type == ImageType.Screenshot)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentException("Screenshots should be accessed using Item.Screenshots");
|
|
|
|
|
throw new ArgumentException("Cannot set chapter images using SetImagePath");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var typeKey = type;
|
|
|
|
|
var image = GetImageInfo(type, index);
|
|
|
|
|
|
|
|
|
|
// If it's null remove the key from the dictionary
|
|
|
|
|
if (string.IsNullOrEmpty(path))
|
|
|
|
|
if (image == null)
|
|
|
|
|
{
|
|
|
|
|
if (Images.ContainsKey(typeKey))
|
|
|
|
|
ImageInfos.Add(new ItemImageInfo
|
|
|
|
|
{
|
|
|
|
|
Images.Remove(typeKey);
|
|
|
|
|
}
|
|
|
|
|
Path = file.FullName,
|
|
|
|
|
Type = type,
|
|
|
|
|
DateModified = FileSystem.GetLastWriteTimeUtc(file)
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Images[typeKey] = path;
|
|
|
|
|
image.Path = file.FullName;
|
|
|
|
|
image.DateModified = FileSystem.GetLastWriteTimeUtc(file);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1208,66 +1188,23 @@ namespace MediaBrowser.Controller.Entities
|
|
|
|
|
/// <param name="type">The type.</param>
|
|
|
|
|
/// <param name="index">The index.</param>
|
|
|
|
|
/// <returns>Task.</returns>
|
|
|
|
|
public Task DeleteImage(ImageType type, int? index)
|
|
|
|
|
{
|
|
|
|
|
if (type == ImageType.Backdrop)
|
|
|
|
|
public Task DeleteImage(ImageType type, int index)
|
|
|
|
|
{
|
|
|
|
|
if (!index.HasValue)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentException("Please specify a backdrop image index to delete.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var file = BackdropImagePaths[index.Value];
|
|
|
|
|
var info = GetImageInfo(type, index);
|
|
|
|
|
|
|
|
|
|
BackdropImagePaths.Remove(file);
|
|
|
|
|
|
|
|
|
|
// Delete the source file
|
|
|
|
|
DeleteImagePath(file);
|
|
|
|
|
}
|
|
|
|
|
else if (type == ImageType.Screenshot)
|
|
|
|
|
{
|
|
|
|
|
if (!index.HasValue)
|
|
|
|
|
if (info == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentException("Please specify a screenshot image index to delete.");
|
|
|
|
|
// Nothing to do
|
|
|
|
|
return Task.FromResult(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var hasScreenshots = (IHasScreenshots)this;
|
|
|
|
|
var file = hasScreenshots.ScreenshotImagePaths[index.Value];
|
|
|
|
|
|
|
|
|
|
hasScreenshots.ScreenshotImagePaths.Remove(file);
|
|
|
|
|
|
|
|
|
|
// Delete the source file
|
|
|
|
|
DeleteImagePath(file);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Delete the source file
|
|
|
|
|
DeleteImagePath(this.GetImagePath(type));
|
|
|
|
|
|
|
|
|
|
// Remove it from the item
|
|
|
|
|
this.SetImagePath(type, null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Refresh metadata
|
|
|
|
|
// Need to disable slow providers or the image might get re-downloaded
|
|
|
|
|
return RefreshMetadata(new MetadataRefreshOptions
|
|
|
|
|
{
|
|
|
|
|
ForceSave = true,
|
|
|
|
|
ImageRefreshMode = ImageRefreshMode.ValidationOnly,
|
|
|
|
|
MetadataRefreshMode = MetadataRefreshMode.None
|
|
|
|
|
|
|
|
|
|
}, CancellationToken.None);
|
|
|
|
|
}
|
|
|
|
|
ImageInfos.Remove(info);
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Deletes the image path.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="path">The path.</param>
|
|
|
|
|
private void DeleteImagePath(string path)
|
|
|
|
|
{
|
|
|
|
|
var currentFile = new FileInfo(path);
|
|
|
|
|
// Delete the source file
|
|
|
|
|
var currentFile = new FileInfo(info.Path);
|
|
|
|
|
|
|
|
|
|
// This will fail if the file is hidden
|
|
|
|
|
// Deletion will fail if the file is hidden so remove the attribute first
|
|
|
|
|
if (currentFile.Exists)
|
|
|
|
|
{
|
|
|
|
|
if ((currentFile.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
|
|
|
|
@ -1277,6 +1214,8 @@ namespace MediaBrowser.Controller.Entities
|
|
|
|
|
|
|
|
|
|
currentFile.Delete();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return LibraryManager.UpdateItem(this, ItemUpdateType.ImageUpdate, CancellationToken.None);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -1284,132 +1223,110 @@ namespace MediaBrowser.Controller.Entities
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool ValidateImages()
|
|
|
|
|
{
|
|
|
|
|
var changed = false;
|
|
|
|
|
|
|
|
|
|
// Only validate paths from the same directory - need to copy to a list because we are going to potentially modify the collection below
|
|
|
|
|
var deletedKeys = Images
|
|
|
|
|
.Where(image => !File.Exists(image.Value))
|
|
|
|
|
.Select(i => i.Key)
|
|
|
|
|
var deletedImages = ImageInfos
|
|
|
|
|
.Where(image => !File.Exists(image.Path))
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
// Now remove them from the dictionary
|
|
|
|
|
foreach (var key in deletedKeys)
|
|
|
|
|
if (deletedImages.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
Images.Remove(key);
|
|
|
|
|
changed = true;
|
|
|
|
|
ImageInfos = ImageInfos.Except(deletedImages).ToList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ValidateBackdrops())
|
|
|
|
|
{
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
|
|
|
|
if (ValidateScreenshots())
|
|
|
|
|
{
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return changed;
|
|
|
|
|
return deletedImages.Count > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Validates that backdrops within the item are still on the file system
|
|
|
|
|
/// Gets the image path.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private bool ValidateBackdrops()
|
|
|
|
|
{
|
|
|
|
|
var changed = false;
|
|
|
|
|
|
|
|
|
|
// Only validate paths from the same directory - need to copy to a list because we are going to potentially modify the collection below
|
|
|
|
|
var deletedImages = BackdropImagePaths
|
|
|
|
|
.Where(path => !File.Exists(path))
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
// Now remove them from the dictionary
|
|
|
|
|
foreach (var path in deletedImages)
|
|
|
|
|
/// <param name="imageType">Type of the image.</param>
|
|
|
|
|
/// <param name="imageIndex">Index of the image.</param>
|
|
|
|
|
/// <returns>System.String.</returns>
|
|
|
|
|
/// <exception cref="System.InvalidOperationException">
|
|
|
|
|
/// </exception>
|
|
|
|
|
/// <exception cref="System.ArgumentNullException">item</exception>
|
|
|
|
|
public string GetImagePath(ImageType imageType, int imageIndex)
|
|
|
|
|
{
|
|
|
|
|
BackdropImagePaths.Remove(path);
|
|
|
|
|
var info = GetImageInfo(imageType, imageIndex);
|
|
|
|
|
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return changed;
|
|
|
|
|
return info == null ? null : info.Path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Validates the screenshots.
|
|
|
|
|
/// Gets the image information.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private bool ValidateScreenshots()
|
|
|
|
|
/// <param name="imageType">Type of the image.</param>
|
|
|
|
|
/// <param name="imageIndex">Index of the image.</param>
|
|
|
|
|
/// <returns>ItemImageInfo.</returns>
|
|
|
|
|
public ItemImageInfo GetImageInfo(ImageType imageType, int imageIndex)
|
|
|
|
|
{
|
|
|
|
|
var changed = false;
|
|
|
|
|
|
|
|
|
|
var hasScreenshots = this as IHasScreenshots;
|
|
|
|
|
if (imageType == ImageType.Chapter)
|
|
|
|
|
{
|
|
|
|
|
var chapter = ItemRepository.GetChapter(Id, imageIndex);
|
|
|
|
|
|
|
|
|
|
if (hasScreenshots == null)
|
|
|
|
|
if (chapter == null)
|
|
|
|
|
{
|
|
|
|
|
return changed;
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Only validate paths from the same directory - need to copy to a list because we are going to potentially modify the collection below
|
|
|
|
|
var deletedImages = hasScreenshots.ScreenshotImagePaths
|
|
|
|
|
.Where(path => !File.Exists(path))
|
|
|
|
|
.ToList();
|
|
|
|
|
var path = chapter.ImagePath;
|
|
|
|
|
|
|
|
|
|
// Now remove them from the dictionary
|
|
|
|
|
foreach (var path in deletedImages)
|
|
|
|
|
if (string.IsNullOrWhiteSpace(path))
|
|
|
|
|
{
|
|
|
|
|
hasScreenshots.ScreenshotImagePaths.Remove(path);
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return changed;
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the image path.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="imageType">Type of the image.</param>
|
|
|
|
|
/// <param name="imageIndex">Index of the image.</param>
|
|
|
|
|
/// <returns>System.String.</returns>
|
|
|
|
|
/// <exception cref="System.InvalidOperationException">
|
|
|
|
|
/// </exception>
|
|
|
|
|
/// <exception cref="System.ArgumentNullException">item</exception>
|
|
|
|
|
public string GetImagePath(ImageType imageType, int imageIndex)
|
|
|
|
|
return new ItemImageInfo
|
|
|
|
|
{
|
|
|
|
|
if (imageType == ImageType.Backdrop)
|
|
|
|
|
{
|
|
|
|
|
return BackdropImagePaths.Count > imageIndex ? BackdropImagePaths[imageIndex] : null;
|
|
|
|
|
Path = path,
|
|
|
|
|
DateModified = FileSystem.GetLastWriteTimeUtc(path),
|
|
|
|
|
Type = imageType
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (imageType == ImageType.Screenshot)
|
|
|
|
|
{
|
|
|
|
|
var hasScreenshots = (IHasScreenshots)this;
|
|
|
|
|
return hasScreenshots.ScreenshotImagePaths.Count > imageIndex ? hasScreenshots.ScreenshotImagePaths[imageIndex] : null;
|
|
|
|
|
return GetImages(imageType)
|
|
|
|
|
.ElementAtOrDefault(imageIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IEnumerable<ItemImageInfo> GetImages(ImageType imageType)
|
|
|
|
|
{
|
|
|
|
|
if (imageType == ImageType.Chapter)
|
|
|
|
|
{
|
|
|
|
|
return ItemRepository.GetChapter(Id, imageIndex).ImagePath;
|
|
|
|
|
throw new ArgumentException("No image info for chapter images");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string val;
|
|
|
|
|
Images.TryGetValue(imageType, out val);
|
|
|
|
|
return val;
|
|
|
|
|
return ImageInfos.Where(i => i.Type == imageType);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the image date modified.
|
|
|
|
|
/// Adds the images.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="imagePath">The image path.</param>
|
|
|
|
|
/// <returns>DateTime.</returns>
|
|
|
|
|
/// <exception cref="System.ArgumentNullException">item</exception>
|
|
|
|
|
public DateTime GetImageDateModified(string imagePath)
|
|
|
|
|
/// <param name="imageType">Type of the image.</param>
|
|
|
|
|
/// <param name="images">The images.</param>
|
|
|
|
|
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
|
|
|
|
/// <exception cref="System.ArgumentException">Cannot call AddImages with chapter images</exception>
|
|
|
|
|
public bool AddImages(ImageType imageType, IEnumerable<FileInfo> images)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(imagePath))
|
|
|
|
|
if (imageType == ImageType.Chapter)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException("imagePath");
|
|
|
|
|
throw new ArgumentException("Cannot call AddImages with chapter images");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// See if we can avoid a file system lookup by looking for the file in ResolveArgs
|
|
|
|
|
return FileSystem.GetLastWriteTimeUtc(imagePath);
|
|
|
|
|
var existingImagePaths = GetImages(imageType)
|
|
|
|
|
.Select(i => i.Path)
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
var newImages = images
|
|
|
|
|
.Where(i => !existingImagePaths.Contains(i.FullName, StringComparer.OrdinalIgnoreCase))
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
ImageInfos.AddRange(newImages.Select(i => new ItemImageInfo
|
|
|
|
|
{
|
|
|
|
|
Path = i.FullName,
|
|
|
|
|
Type = imageType,
|
|
|
|
|
DateModified = FileSystem.GetLastWriteTimeUtc(i)
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
return newImages.Count > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -1421,25 +1338,39 @@ namespace MediaBrowser.Controller.Entities
|
|
|
|
|
return new[] { Path };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool AllowsMultipleImages(ImageType type)
|
|
|
|
|
{
|
|
|
|
|
return type == ImageType.Backdrop || type == ImageType.Screenshot || type == ImageType.Chapter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Task SwapImages(ImageType type, int index1, int index2)
|
|
|
|
|
{
|
|
|
|
|
if (type != ImageType.Screenshot && type != ImageType.Backdrop)
|
|
|
|
|
if (!AllowsMultipleImages(type))
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentException("The change index operation is only applicable to backdrops and screenshots");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var file1 = GetImagePath(type, index1);
|
|
|
|
|
var file2 = GetImagePath(type, index2);
|
|
|
|
|
var info1 = GetImageInfo(type, index1);
|
|
|
|
|
var info2 = GetImageInfo(type, index2);
|
|
|
|
|
|
|
|
|
|
FileSystem.SwapFiles(file1, file2);
|
|
|
|
|
|
|
|
|
|
// Directory watchers should repeat this, but do a quick refresh first
|
|
|
|
|
return RefreshMetadata(new MetadataRefreshOptions
|
|
|
|
|
if (info1 == null || info2 == null)
|
|
|
|
|
{
|
|
|
|
|
ForceSave = true,
|
|
|
|
|
MetadataRefreshMode = MetadataRefreshMode.None
|
|
|
|
|
// Nothing to do
|
|
|
|
|
return Task.FromResult(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var path1 = info1.Path;
|
|
|
|
|
var path2 = info2.Path;
|
|
|
|
|
|
|
|
|
|
FileSystem.SwapFiles(path1, path2);
|
|
|
|
|
|
|
|
|
|
info1.Path = path2;
|
|
|
|
|
info2.Path = path1;
|
|
|
|
|
|
|
|
|
|
info1.DateModified = FileSystem.GetLastWriteTimeUtc(info1.Path);
|
|
|
|
|
info2.DateModified = FileSystem.GetLastWriteTimeUtc(info2.Path);
|
|
|
|
|
|
|
|
|
|
}, CancellationToken.None);
|
|
|
|
|
return LibraryManager.UpdateItem(this, ItemUpdateType.ImageUpdate, CancellationToken.None);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public virtual bool IsPlayed(User user)
|
|
|
|
|