aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Controller')
-rw-r--r--MediaBrowser.Controller/Drawing/IImageProcessor.cs25
-rw-r--r--MediaBrowser.Controller/Entities/AdultVideo.cs12
-rw-r--r--MediaBrowser.Controller/Entities/AggregateFolder.cs13
-rw-r--r--MediaBrowser.Controller/Entities/Audio/Audio.cs25
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs47
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicArtist.cs150
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicGenre.cs25
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs560
-rw-r--r--MediaBrowser.Controller/Entities/Book.cs22
-rw-r--r--MediaBrowser.Controller/Entities/CollectionFolder.cs17
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs283
-rw-r--r--MediaBrowser.Controller/Entities/Game.cs21
-rw-r--r--MediaBrowser.Controller/Entities/GameGenre.cs26
-rw-r--r--MediaBrowser.Controller/Entities/GameSystem.cs12
-rw-r--r--MediaBrowser.Controller/Entities/Genre.cs25
-rw-r--r--MediaBrowser.Controller/Entities/IHasImages.cs76
-rw-r--r--MediaBrowser.Controller/Entities/IHasMetadata.cs (renamed from MediaBrowser.Controller/Providers/IHasMetadata.cs)26
-rw-r--r--MediaBrowser.Controller/Entities/IHasScreenshots.cs8
-rw-r--r--MediaBrowser.Controller/Entities/IHasSeries.cs12
-rw-r--r--MediaBrowser.Controller/Entities/IMetadataContainer.cs19
-rw-r--r--MediaBrowser.Controller/Entities/ItemImageInfo.cs14
-rw-r--r--MediaBrowser.Controller/Entities/Movies/BoxSet.cs71
-rw-r--r--MediaBrowser.Controller/Entities/Movies/Movie.cs28
-rw-r--r--MediaBrowser.Controller/Entities/MusicVideo.cs8
-rw-r--r--MediaBrowser.Controller/Entities/Person.cs35
-rw-r--r--MediaBrowser.Controller/Entities/Studio.cs25
-rw-r--r--MediaBrowser.Controller/Entities/TV/Episode.cs109
-rw-r--r--MediaBrowser.Controller/Entities/TV/Season.cs68
-rw-r--r--MediaBrowser.Controller/Entities/TV/Series.cs8
-rw-r--r--MediaBrowser.Controller/Entities/Trailer.cs17
-rw-r--r--MediaBrowser.Controller/Entities/User.cs35
-rw-r--r--MediaBrowser.Controller/Entities/UserRootFolder.cs21
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs29
-rw-r--r--MediaBrowser.Controller/Entities/Year.cs25
-rw-r--r--MediaBrowser.Controller/IO/FileData.cs9
-rw-r--r--MediaBrowser.Controller/IServerApplicationPaths.cs16
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs6
-rw-r--r--MediaBrowser.Controller/Library/ILibraryPrescanTask.cs20
-rw-r--r--MediaBrowser.Controller/Library/IMetadataSaver.cs3
-rw-r--r--MediaBrowser.Controller/Library/IPeoplePrescanTask.cs20
-rw-r--r--MediaBrowser.Controller/Library/ItemResolveArgs.cs28
-rw-r--r--MediaBrowser.Controller/Library/ItemUpdateType.cs2
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs20
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvChannel.cs25
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvProgram.cs37
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs20
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj10
-rw-r--r--MediaBrowser.Controller/MediaInfo/FFMpegManager.cs9
-rw-r--r--MediaBrowser.Controller/MediaInfo/IMediaEncoder.cs13
-rw-r--r--MediaBrowser.Controller/Providers/BaseItemXmlParser.cs66
-rw-r--r--MediaBrowser.Controller/Providers/BaseMetadataProvider.cs2
-rw-r--r--MediaBrowser.Controller/Providers/BaseProviderInfo.cs14
-rw-r--r--MediaBrowser.Controller/Providers/DirectoryService.cs79
-rw-r--r--MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs5
-rw-r--r--MediaBrowser.Controller/Providers/IHasChangeMonitor.cs6
-rw-r--r--MediaBrowser.Controller/Providers/ILocalImageProvider.cs24
-rw-r--r--MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs20
-rw-r--r--MediaBrowser.Controller/Providers/IMetadataProvider.cs1
-rw-r--r--MediaBrowser.Controller/Providers/IMetadataService.cs1
-rw-r--r--MediaBrowser.Controller/Providers/IProviderManager.cs14
-rw-r--r--MediaBrowser.Controller/Providers/IProviderRepository.cs16
-rw-r--r--MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs14
-rw-r--r--MediaBrowser.Controller/Providers/ItemLookupInfo.cs (renamed from MediaBrowser.Controller/Providers/ItemId.cs)81
-rw-r--r--MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs1
-rw-r--r--MediaBrowser.Controller/Providers/MetadataStatus.cs19
65 files changed, 1735 insertions, 763 deletions
diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs
index 2ecf3ec9a..ad5e622fc 100644
--- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs
+++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs
@@ -53,10 +53,9 @@ namespace MediaBrowser.Controller.Drawing
/// Gets the image cache tag.
/// </summary>
/// <param name="item">The item.</param>
- /// <param name="imageType">Type of the image.</param>
- /// <param name="imagePath">The image path.</param>
+ /// <param name="image">The image.</param>
/// <returns>Guid.</returns>
- Guid GetImageCacheTag(IHasImages item, ImageType imageType, string imagePath);
+ Guid GetImageCacheTag(IHasImages item, ItemImageInfo image);
/// <summary>
/// Gets the image cache tag.
@@ -87,4 +86,24 @@ namespace MediaBrowser.Controller.Drawing
/// <returns>Task{System.String}.</returns>
Task<string> GetEnhancedImage(IHasImages item, ImageType imageType, int imageIndex);
}
+
+ public static class ImageProcessorExtensions
+ {
+ public static Guid? GetImageCacheTag(this IImageProcessor processor, IHasImages item, ImageType imageType)
+ {
+ return processor.GetImageCacheTag(item, imageType, 0);
+ }
+
+ public static Guid? GetImageCacheTag(this IImageProcessor processor, IHasImages item, ImageType imageType, int imageIndex)
+ {
+ var imageInfo = item.GetImageInfo(imageType, imageIndex);
+
+ if (imageInfo == null)
+ {
+ return null;
+ }
+
+ return processor.GetImageCacheTag(item, imageInfo);
+ }
+ }
}
diff --git a/MediaBrowser.Controller/Entities/AdultVideo.cs b/MediaBrowser.Controller/Entities/AdultVideo.cs
index 475d7bc54..fc7632152 100644
--- a/MediaBrowser.Controller/Entities/AdultVideo.cs
+++ b/MediaBrowser.Controller/Entities/AdultVideo.cs
@@ -1,7 +1,8 @@
-
+using System.Collections.Generic;
+
namespace MediaBrowser.Controller.Entities
{
- public class AdultVideo : Video, IHasPreferredMetadataLanguage
+ public class AdultVideo : Video, IHasPreferredMetadataLanguage, IHasTaglines
{
/// <summary>
/// Gets or sets the preferred metadata language.
@@ -14,5 +15,12 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <value>The preferred metadata country code.</value>
public string PreferredMetadataCountryCode { get; set; }
+
+ public List<string> Taglines { get; set; }
+
+ public AdultVideo()
+ {
+ Taglines = new List<string>();
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs
index ef455846e..5cabe1cfe 100644
--- a/MediaBrowser.Controller/Entities/AggregateFolder.cs
+++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
+using MediaBrowser.Controller.Providers;
namespace MediaBrowser.Controller.Entities
{
@@ -56,12 +57,12 @@ namespace MediaBrowser.Controller.Entities
public List<string> PhysicalLocationsList { get; set; }
- protected override IEnumerable<FileSystemInfo> GetFileSystemChildren()
+ protected override IEnumerable<FileSystemInfo> GetFileSystemChildren(IDirectoryService directoryService)
{
- return CreateResolveArgs().FileSystemChildren;
+ return CreateResolveArgs(directoryService).FileSystemChildren;
}
- private ItemResolveArgs CreateResolveArgs()
+ private ItemResolveArgs CreateResolveArgs(IDirectoryService directoryService)
{
var path = ContainingFolderPath;
@@ -80,7 +81,7 @@ namespace MediaBrowser.Controller.Entities
// When resolving the root, we need it's grandchildren (children of user views)
var flattenFolderDepth = isPhysicalRoot ? 2 : 0;
- var fileSystemDictionary = FileData.GetFilteredFileSystemEntries(args.Path, FileSystem, Logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf);
+ var fileSystemDictionary = FileData.GetFilteredFileSystemEntries(directoryService, args.Path, FileSystem, Logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf);
// Need to remove subpaths that may have been resolved from shortcuts
// Example: if \\server\movies exists, then strip out \\server\movies\action
@@ -118,9 +119,9 @@ namespace MediaBrowser.Controller.Entities
/// Get the children of this folder from the actual file system
/// </summary>
/// <returns>IEnumerable{BaseItem}.</returns>
- protected override IEnumerable<BaseItem> GetNonCachedChildren()
+ protected override IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
{
- return base.GetNonCachedChildren().Concat(_virtualChildren);
+ return base.GetNonCachedChildren(directoryService).Concat(_virtualChildren);
}
/// <summary>
diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs
index 6f4a0c4d2..836874db9 100644
--- a/MediaBrowser.Controller/Entities/Audio/Audio.cs
+++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Model.Configuration;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -9,7 +10,7 @@ namespace MediaBrowser.Controller.Entities.Audio
/// <summary>
/// Class Audio
/// </summary>
- public class Audio : BaseItem, IHasMediaStreams, IHasAlbumArtist, IHasArtist, IHasMusicGenres
+ public class Audio : BaseItem, IHasMediaStreams, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo<SongInfo>, IHasSeries
{
public Audio()
{
@@ -50,6 +51,15 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
+ [IgnoreDataMember]
+ public string SeriesName
+ {
+ get
+ {
+ return Album;
+ }
+ }
+
/// <summary>
/// Gets or sets the artist.
/// </summary>
@@ -127,5 +137,16 @@ namespace MediaBrowser.Controller.Entities.Audio
{
return config.BlockUnratedMusic;
}
+
+ public SongInfo GetLookupInfo()
+ {
+ var info = GetItemLookupInfo<SongInfo>();
+
+ info.AlbumArtist = AlbumArtist;
+ info.Album = Album;
+ info.Artists = Artists;
+
+ return info;
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
index b3bf0d2b6..51c8a8727 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Model.Configuration;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
@@ -10,10 +11,10 @@ namespace MediaBrowser.Controller.Entities.Audio
/// <summary>
/// Class MusicAlbum
/// </summary>
- public class MusicAlbum : Folder, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasTags
+ public class MusicAlbum : Folder, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasTags, IHasLookupInfo<AlbumInfo>, IHasSeries
{
public List<Guid> SoundtrackIds { get; set; }
-
+
public MusicAlbum()
{
Artists = new List<string>();
@@ -21,6 +22,15 @@ namespace MediaBrowser.Controller.Entities.Audio
Tags = new List<string>();
}
+ [IgnoreDataMember]
+ public MusicArtist MusicArtist
+ {
+ get
+ {
+ return Parents.OfType<MusicArtist>().FirstOrDefault();
+ }
+ }
+
/// <summary>
/// Gets or sets the tags.
/// </summary>
@@ -40,6 +50,15 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
+ [IgnoreDataMember]
+ public string SeriesName
+ {
+ get
+ {
+ return AlbumArtist;
+ }
+ }
+
/// <summary>
/// Override this to true if class should be grouped under a container in indicies
/// The container class should be defined via IndexContainer
@@ -98,7 +117,7 @@ namespace MediaBrowser.Controller.Entities.Audio
return "MusicAlbum-MusicBrainzReleaseGroup-" + id;
}
- id = this.GetProviderId(MetadataProviders.Musicbrainz);
+ id = this.GetProviderId(MetadataProviders.MusicBrainzAlbum);
if (!string.IsNullOrEmpty(id))
{
@@ -112,6 +131,26 @@ namespace MediaBrowser.Controller.Entities.Audio
{
return config.BlockUnratedMusic;
}
+
+ public AlbumInfo GetLookupInfo()
+ {
+ var id = GetItemLookupInfo<AlbumInfo>();
+
+ id.AlbumArtist = AlbumArtist;
+
+ var artist = Parents.OfType<MusicArtist>().FirstOrDefault();
+
+ if (artist != null)
+ {
+ id.ArtistProviderIds = artist.ProviderIds;
+ }
+
+ id.SongInfos = RecursiveChildren.OfType<Audio>()
+ .Select(i => i.GetLookupInfo())
+ .ToList();
+
+ return id;
+ }
}
public class MusicAlbumDisc : Folder
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
index 9b4e3a736..2b5570a80 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
@@ -1,8 +1,11 @@
-using MediaBrowser.Model.Configuration;
+using MediaBrowser.Common.Progress;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
@@ -12,7 +15,7 @@ namespace MediaBrowser.Controller.Entities.Audio
/// <summary>
/// Class MusicArtist
/// </summary>
- public class MusicArtist : Folder, IItemByName, IHasMusicGenres, IHasDualAccess, IHasTags, IHasProductionLocations
+ public class MusicArtist : Folder, IMetadataContainer, IItemByName, IHasMusicGenres, IHasDualAccess, IHasTags, IHasProductionLocations, IHasLookupInfo<ArtistInfo>
{
[IgnoreDataMember]
public List<ItemByNameCounts> UserItemCountList { get; set; }
@@ -49,7 +52,7 @@ namespace MediaBrowser.Controller.Entities.Audio
}
private readonly Task _cachedTask = Task.FromResult(true);
- protected override Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool? recursive = null, bool forceRefreshMetadata = false)
+ protected override Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService)
{
if (IsAccessedByName)
{
@@ -57,17 +60,7 @@ namespace MediaBrowser.Controller.Entities.Audio
return _cachedTask;
}
- return base.ValidateChildrenInternal(progress, cancellationToken, recursive, forceRefreshMetadata);
- }
-
- public override string GetClientTypeName()
- {
- if (IsAccessedByName)
- {
- //return "Artist";
- }
-
- return base.GetClientTypeName();
+ return base.ValidateChildrenInternal(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService);
}
public MusicArtist()
@@ -87,13 +80,38 @@ namespace MediaBrowser.Controller.Entities.Audio
}
/// <summary>
+ /// Returns the folder containing the item.
+ /// If the item is a folder, it returns the folder itself
+ /// </summary>
+ /// <value>The containing folder path.</value>
+ public override string ContainingFolderPath
+ {
+ get
+ {
+ return Path;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance is owned item.
+ /// </summary>
+ /// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value>
+ public override bool IsOwnedItem
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ /// <summary>
/// Gets the user data key.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>System.String.</returns>
private static string GetUserDataKey(MusicArtist item)
{
- var id = item.GetProviderId(MetadataProviders.Musicbrainz);
+ var id = item.GetProviderId(MetadataProviders.MusicBrainzArtist);
if (!string.IsNullOrEmpty(id))
{
@@ -107,5 +125,107 @@ namespace MediaBrowser.Controller.Entities.Audio
{
return config.BlockUnratedMusic;
}
+
+ public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
+ {
+ var items = RecursiveChildren.ToList();
+
+ var songs = items.OfType<Audio>().ToList();
+
+ var others = items.Except(songs).ToList();
+
+ var totalItems = songs.Count + others.Count;
+ var percentages = new Dictionary<Guid, double>(totalItems);
+
+ var tasks = new List<Task>();
+
+ // Refresh songs
+ foreach (var item in songs)
+ {
+ if (tasks.Count >= 2)
+ {
+ await Task.WhenAll(tasks).ConfigureAwait(false);
+ tasks.Clear();
+ }
+
+ cancellationToken.ThrowIfCancellationRequested();
+ var innerProgress = new ActionableProgress<double>();
+
+ // Avoid implicitly captured closure
+ var currentChild = item;
+ innerProgress.RegisterAction(p =>
+ {
+ lock (percentages)
+ {
+ percentages[currentChild.Id] = p / 100;
+
+ var percent = percentages.Values.Sum();
+ percent /= totalItems;
+ percent *= 100;
+ progress.Report(percent);
+ }
+ });
+
+ tasks.Add(RefreshItem(item, refreshOptions, innerProgress, cancellationToken));
+ }
+
+ await Task.WhenAll(tasks).ConfigureAwait(false);
+ tasks.Clear();
+
+ // Refresh current item
+ await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
+
+ // Refresh all non-songs
+ foreach (var item in others)
+ {
+ if (tasks.Count > 3)
+ {
+ await Task.WhenAll(tasks).ConfigureAwait(false);
+ tasks.Clear();
+ }
+
+ cancellationToken.ThrowIfCancellationRequested();
+ var innerProgress = new ActionableProgress<double>();
+
+ // Avoid implicitly captured closure
+ var currentChild = item;
+ innerProgress.RegisterAction(p =>
+ {
+ lock (percentages)
+ {
+ percentages[currentChild.Id] = p / 100;
+
+ var percent = percentages.Values.Sum();
+ percent /= totalItems;
+ percent *= 100;
+ progress.Report(percent);
+ }
+ });
+
+ tasks.Add(RefreshItem(item, refreshOptions, innerProgress, cancellationToken));
+ }
+
+ await Task.WhenAll(tasks).ConfigureAwait(false);
+
+ progress.Report(100);
+ }
+
+ private async Task RefreshItem(BaseItem item, MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
+ {
+ await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
+
+ progress.Report(100);
+ }
+
+ public ArtistInfo GetLookupInfo()
+ {
+ var info = GetItemLookupInfo<ArtistInfo>();
+
+ info.SongInfos = RecursiveChildren.OfType<Audio>()
+ .Select(i => i.GetLookupInfo())
+ .ToList();
+
+ return info;
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
index b54e14f2d..5e1d4c3c9 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
@@ -26,5 +26,30 @@ namespace MediaBrowser.Controller.Entities.Audio
[IgnoreDataMember]
public List<ItemByNameCounts> UserItemCountList { get; set; }
+
+ /// <summary>
+ /// Returns the folder containing the item.
+ /// If the item is a folder, it returns the folder itself
+ /// </summary>
+ /// <value>The containing folder path.</value>
+ public override string ContainingFolderPath
+ {
+ get
+ {
+ return Path;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance is owned item.
+ /// </summary>
+ /// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value>
+ public override bool IsOwnedItem
+ {
+ get
+ {
+ return false;
+ }
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index de5516e29..8dcf08642 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -21,17 +21,16 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// Class BaseItem
/// </summary>
- public abstract class BaseItem : IHasProviderIds, ILibraryItem, IHasImages, IHasUserData, IHasMetadata
+ public abstract class BaseItem : IHasProviderIds, ILibraryItem, IHasImages, IHasUserData, IHasMetadata, IHasLookupInfo<ItemLookupInfo>
{
protected BaseItem()
{
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;
@@ -119,7 +124,7 @@ namespace MediaBrowser.Controller.Entities
}
[IgnoreDataMember]
- public bool IsOwnedItem
+ public virtual bool IsOwnedItem
{
get
{
@@ -152,6 +157,16 @@ namespace MediaBrowser.Controller.Entities
}
}
+ public virtual bool SupportsLocalMetadata
+ {
+ get
+ {
+ var locationType = LocationType;
+
+ return locationType == LocationType.FileSystem || locationType == LocationType.Offline;
+ }
+ }
+
/// <summary>
/// This is just a helper for convenience
/// </summary>
@@ -160,16 +175,9 @@ 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>
/// <value>The date created.</value>
@@ -236,7 +244,7 @@ namespace MediaBrowser.Controller.Entities
{
var locationType = LocationType;
- if (locationType != LocationType.Remote && locationType != LocationType.Virtual)
+ if (locationType == LocationType.Remote || locationType == LocationType.Virtual)
{
return new string[] { };
}
@@ -258,7 +266,7 @@ namespace MediaBrowser.Controller.Entities
private string _sortName;
/// <summary>
- /// Gets or sets the name of the sort.
+ /// Gets the name of the sort.
/// </summary>
/// <value>The name of the sort.</value>
[IgnoreDataMember]
@@ -350,12 +358,6 @@ namespace MediaBrowser.Controller.Entities
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>
/// <value>The official rating.</value>
@@ -447,9 +449,23 @@ namespace MediaBrowser.Controller.Entities
}
[IgnoreDataMember]
- public virtual string CustomRatingForComparison
+ public string CustomRatingForComparison
{
- get { return CustomRating; }
+ get
+ {
+ if (!string.IsNullOrEmpty(CustomRating))
+ {
+ return CustomRating;
+ }
+
+ var parent = Parent;
+ if (parent != null)
+ {
+ return parent.CustomRatingForComparison;
+ }
+
+ return null;
+ }
}
/// <summary>
@@ -458,75 +474,30 @@ namespace MediaBrowser.Controller.Entities
/// <returns>List{Video}.</returns>
private IEnumerable<Trailer> LoadLocalTrailers(List<FileSystemInfo> fileSystemChildren)
{
- return new List<Trailer>();
- //ItemResolveArgs resolveArgs;
-
- //try
- //{
- // resolveArgs = ResolveArgs;
-
- // if (!resolveArgs.IsDirectory)
- // {
- // return new List<Trailer>();
- // }
- //}
- //catch (IOException ex)
- //{
- // Logger.ErrorException("Error getting ResolveArgs for {0}", ex, Path);
- // return new List<Trailer>();
- //}
-
- //var files = new List<FileSystemInfo>();
-
- //var folder = resolveArgs.GetFileSystemEntryByName(TrailerFolderName);
-
- //// Path doesn't exist. No biggie
- //if (folder != null)
- //{
- // try
- // {
- // files.AddRange(new DirectoryInfo(folder.FullName).EnumerateFiles());
- // }
- // catch (IOException ex)
- // {
- // Logger.ErrorException("Error loading trailers for {0}", ex, Name);
- // }
- //}
-
- //// Support xbmc trailers (-trailer suffix on video file names)
- //files.AddRange(resolveArgs.FileSystemChildren.Where(i =>
- //{
- // try
- // {
- // if ((i.Attributes & FileAttributes.Directory) != FileAttributes.Directory)
- // {
- // if (System.IO.Path.GetFileNameWithoutExtension(i.Name).EndsWith(XbmcTrailerFileSuffix, StringComparison.OrdinalIgnoreCase) && !string.Equals(Path, i.FullName, StringComparison.OrdinalIgnoreCase))
- // {
- // return true;
- // }
- // }
- // }
- // catch (IOException ex)
- // {
- // Logger.ErrorException("Error accessing path {0}", ex, i.FullName);
- // }
-
- // return false;
- //}));
-
- //return LibraryManager.ResolvePaths<Trailer>(files, 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 Trailer;
-
- // if (dbItem != null)
- // {
- // video = dbItem;
- // }
-
- // return video;
-
- //}).ToList();
+ var files = fileSystemChildren.OfType<DirectoryInfo>()
+ .Where(i => string.Equals(i.Name, TrailerFolderName, StringComparison.OrdinalIgnoreCase))
+ .SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly))
+ .ToList();
+
+ // Support plex/xbmc convention
+ files.AddRange(fileSystemChildren.OfType<FileInfo>()
+ .Where(i => System.IO.Path.GetFileNameWithoutExtension(i.Name).EndsWith(XbmcTrailerFileSuffix, StringComparison.OrdinalIgnoreCase) && !string.Equals(Path, i.FullName, StringComparison.OrdinalIgnoreCase))
+ );
+
+ return LibraryManager.ResolvePaths<Trailer>(files, 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 Trailer;
+
+ if (dbItem != null)
+ {
+ video = dbItem;
+ }
+
+ return video;
+
+ // Sort them so that the list can be easily compared for changes
+ }).OrderBy(i => i.Path).ToList();
}
/// <summary>
@@ -556,7 +527,9 @@ namespace MediaBrowser.Controller.Entities
}
return audio;
- }).ToList();
+
+ // Sort them so that the list can be easily compared for changes
+ }).OrderBy(i => i.Path).ToList();
}
/// <summary>
@@ -580,7 +553,9 @@ namespace MediaBrowser.Controller.Entities
}
return item;
- }).ToList();
+
+ // Sort them so that the list can be easily compared for changes
+ }).OrderBy(i => i.Path).ToList();
}
public Task RefreshMetadata(CancellationToken cancellationToken)
@@ -598,19 +573,44 @@ namespace MediaBrowser.Controller.Entities
{
var locationType = LocationType;
+ var requiresSave = false;
+
if (IsFolder || Parent != null)
{
+ options.DirectoryService = options.DirectoryService ?? new DirectoryService(Logger);
+
var files = locationType == LocationType.FileSystem || locationType == LocationType.Offline ?
- GetFileSystemChildren().ToList() :
+ GetFileSystemChildren(options.DirectoryService).ToList() :
new List<FileSystemInfo>();
- await BeforeRefreshMetadata(options, files, cancellationToken).ConfigureAwait(false);
+ var ownedItemsChanged = await RefreshedOwnedItems(options, files, cancellationToken).ConfigureAwait(false);
+
+ if (ownedItemsChanged)
+ {
+ requiresSave = true;
+ }
}
+ var dateLastSaved = DateLastSaved;
+
await ProviderManager.RefreshMetadata(this, options, cancellationToken).ConfigureAwait(false);
+
+ // If it wasn't saved by the provider process, save now
+ if (requiresSave && dateLastSaved == DateLastSaved)
+ {
+ await UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
+ }
}
- protected virtual async Task BeforeRefreshMetadata(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
+ /// <summary>
+ /// Refreshes owned items such as trailers, theme videos, special features, etc.
+ /// Returns true or false indicating if changes were found.
+ /// </summary>
+ /// <param name="options"></param>
+ /// <param name="fileSystemChildren"></param>
+ /// <param name="cancellationToken"></param>
+ /// <returns></returns>
+ protected virtual async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
var themeSongsChanged = false;
@@ -637,18 +637,15 @@ namespace MediaBrowser.Controller.Entities
localTrailersChanged = await RefreshLocalTrailers(hasTrailers, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
}
}
-
- if (themeSongsChanged || themeVideosChanged || localTrailersChanged)
- {
- options.ForceSave = true;
- }
+
+ return themeSongsChanged || themeVideosChanged || localTrailersChanged;
}
- protected virtual IEnumerable<FileSystemInfo> GetFileSystemChildren()
+ protected virtual IEnumerable<FileSystemInfo> GetFileSystemChildren(IDirectoryService directoryService)
{
var path = ContainingFolderPath;
- return new DirectoryInfo(path).EnumerateFileSystemInfos("*", SearchOption.TopDirectoryOnly);
+ return directoryService.GetFileSystemEntries(path);
}
private async Task<bool> RefreshLocalTrailers(IHasTrailers item, MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
@@ -670,6 +667,7 @@ namespace MediaBrowser.Controller.Entities
private async Task<bool> RefreshThemeVideos(IHasThemeMedia item, MetadataRefreshOptions options, IEnumerable<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
var newThemeVideos = LoadThemeVideos(fileSystemChildren).ToList();
+
var newThemeVideoIds = newThemeVideos.Select(i => i.Id).ToList();
var themeVideosChanged = !item.ThemeVideoIds.SequenceEqual(newThemeVideoIds);
@@ -885,29 +883,6 @@ namespace MediaBrowser.Controller.Entities
}
/// <summary>
- /// Finds the particular item by searching through our parents and, if not found there, loading from repo
- /// </summary>
- /// <param name="id">The id.</param>
- /// <returns>BaseItem.</returns>
- /// <exception cref="System.ArgumentException"></exception>
- protected BaseItem FindParentItem(Guid id)
- {
- if (id == Guid.Empty)
- {
- throw new ArgumentException();
- }
-
- var parent = Parent;
- while (parent != null && !parent.IsRoot)
- {
- if (parent.Id == id) return parent;
- parent = parent.Parent;
- }
-
- return null;
- }
-
- /// <summary>
/// Gets a value indicating whether this instance is folder.
/// </summary>
/// <value><c>true</c> if this instance is folder; otherwise, <c>false</c>.</value>
@@ -1188,43 +1163,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)
- {
- throw new ArgumentException("Backdrops should be accessed using Item.Backdrops");
- }
- if (type == ImageType.Screenshot)
+ if (type == ImageType.Chapter)
{
- 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);
}
}
@@ -1234,66 +1197,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)
+ public Task DeleteImage(ImageType type, int index)
{
- if (type == ImageType.Backdrop)
- {
- 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 (info == null)
{
- if (!index.HasValue)
- {
- throw new ArgumentException("Please specify a screenshot image index to delete.");
- }
-
- var hasScreenshots = (IHasScreenshots)this;
- var file = hasScreenshots.ScreenshotImagePaths[index.Value];
-
- hasScreenshots.ScreenshotImagePaths.Remove(file);
-
- // Delete the source file
- DeleteImagePath(file);
+ // Nothing to do
+ return Task.FromResult(true);
}
- 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
+ // Remove it from the item
+ ImageInfos.Remove(info);
- }, CancellationToken.None);
- }
-
- /// <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)
@@ -1303,90 +1223,33 @@ namespace MediaBrowser.Controller.Entities
currentFile.Delete();
}
- }
-
- /// <summary>
- /// Validates that images within the item are still on the file system
- /// </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)
- .ToList();
-
- // Now remove them from the dictionary
- foreach (var key in deletedKeys)
- {
- Images.Remove(key);
- changed = true;
- }
-
- if (ValidateBackdrops())
- {
- changed = true;
- }
- if (ValidateScreenshots())
- {
- changed = true;
- }
- return changed;
+ return UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
}
- /// <summary>
- /// Validates that backdrops within the item are still on the file system
- /// </summary>
- private bool ValidateBackdrops()
+ public virtual Task UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken)
{
- 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)
- {
- BackdropImagePaths.Remove(path);
-
- changed = true;
- }
-
- return changed;
+ return LibraryManager.UpdateItem(this, ItemUpdateType.ImageUpdate, cancellationToken);
}
/// <summary>
- /// Validates the screenshots.
+ /// Validates that images within the item are still on the file system
/// </summary>
- private bool ValidateScreenshots()
+ public bool ValidateImages(IDirectoryService directoryService)
{
- var changed = false;
+ var allDirectories = ImageInfos.Select(i => System.IO.Path.GetDirectoryName(i.Path)).Distinct(StringComparer.OrdinalIgnoreCase).ToList();
+ var allFiles = allDirectories.SelectMany(directoryService.GetFiles).Select(i => i.FullName).ToList();
- var hasScreenshots = this as IHasScreenshots;
-
- if (hasScreenshots == null)
- {
- return changed;
- }
-
- // 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))
+ var deletedImages = ImageInfos
+ .Where(image => !allFiles.Contains(image.Path, StringComparer.OrdinalIgnoreCase))
.ToList();
- // Now remove them from the dictionary
- foreach (var path in deletedImages)
+ if (deletedImages.Count > 0)
{
- hasScreenshots.ScreenshotImagePaths.Remove(path);
- changed = true;
+ ImageInfos = ImageInfos.Except(deletedImages).ToList();
}
- return changed;
+ return deletedImages.Count > 0;
}
/// <summary>
@@ -1400,42 +1263,87 @@ namespace MediaBrowser.Controller.Entities
/// <exception cref="System.ArgumentNullException">item</exception>
public string GetImagePath(ImageType imageType, int imageIndex)
{
- if (imageType == ImageType.Backdrop)
- {
- return BackdropImagePaths.Count > imageIndex ? BackdropImagePaths[imageIndex] : null;
- }
+ var info = GetImageInfo(imageType, imageIndex);
+
+ return info == null ? null : info.Path;
+ }
- if (imageType == ImageType.Screenshot)
+ /// <summary>
+ /// Gets the image information.
+ /// </summary>
+ /// <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)
+ {
+ if (imageType == ImageType.Chapter)
{
- var hasScreenshots = (IHasScreenshots)this;
- return hasScreenshots.ScreenshotImagePaths.Count > imageIndex ? hasScreenshots.ScreenshotImagePaths[imageIndex] : null;
+ var chapter = ItemRepository.GetChapter(Id, imageIndex);
+
+ if (chapter == null)
+ {
+ return null;
+ }
+
+ var path = chapter.ImagePath;
+
+ if (string.IsNullOrWhiteSpace(path))
+ {
+ return null;
+ }
+
+ return new ItemImageInfo
+ {
+ Path = path,
+ DateModified = FileSystem.GetLastWriteTimeUtc(path),
+ Type = imageType
+ };
}
+ 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>
@@ -1447,25 +1355,37 @@ 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);
-
- FileSystem.SwapFiles(file1, file2);
+ var info1 = GetImageInfo(type, index1);
+ var info2 = GetImageInfo(type, index2);
- // 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);
+ }
- }, CancellationToken.None);
+ var path1 = info1.Path;
+ var path2 = info2.Path;
+
+ FileSystem.SwapFiles(path1, path2);
+
+ // Refresh these values
+ info1.DateModified = FileSystem.GetLastWriteTimeUtc(info1.Path);
+ info2.DateModified = FileSystem.GetLastWriteTimeUtc(info2.Path);
+
+ return UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
}
public virtual bool IsPlayed(User user)
@@ -1481,5 +1401,41 @@ namespace MediaBrowser.Controller.Entities
return userdata == null || !userdata.Played;
}
+
+ ItemLookupInfo IHasLookupInfo<ItemLookupInfo>.GetLookupInfo()
+ {
+ return GetItemLookupInfo<ItemLookupInfo>();
+ }
+
+ protected T GetItemLookupInfo<T>()
+ where T : ItemLookupInfo, new()
+ {
+ return new T
+ {
+ MetadataCountryCode = GetPreferredMetadataCountryCode(),
+ MetadataLanguage = GetPreferredMetadataLanguage(),
+ Name = Name,
+ ProviderIds = ProviderIds,
+ IndexNumber = IndexNumber,
+ ParentIndexNumber = ParentIndexNumber
+ };
+ }
+
+ /// <summary>
+ /// This is called before any metadata refresh and returns ItemUpdateType indictating if changes were made, and what.
+ /// </summary>
+ /// <returns>ItemUpdateType.</returns>
+ public virtual ItemUpdateType BeforeMetadataRefresh()
+ {
+ var updateType = ItemUpdateType.None;
+
+ if (string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(Path))
+ {
+ Name = System.IO.Path.GetFileNameWithoutExtension(Path);
+ updateType = updateType | ItemUpdateType.MetadataEdit;
+ }
+
+ return updateType;
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/Book.cs b/MediaBrowser.Controller/Entities/Book.cs
index 28ccf687c..0405fc484 100644
--- a/MediaBrowser.Controller/Entities/Book.cs
+++ b/MediaBrowser.Controller/Entities/Book.cs
@@ -1,9 +1,11 @@
-using MediaBrowser.Model.Configuration;
+using System.Linq;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Configuration;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Entities
{
- public class Book : BaseItem, IHasTags, IHasPreferredMetadataLanguage
+ public class Book : BaseItem, IHasTags, IHasPreferredMetadataLanguage, IHasLookupInfo<BookInfo>, IHasSeries
{
public override string MediaType
{
@@ -38,5 +40,21 @@ namespace MediaBrowser.Controller.Entities
{
return config.BlockUnratedBooks;
}
+
+ public BookInfo GetLookupInfo()
+ {
+ var info = GetItemLookupInfo<BookInfo>();
+
+ if (string.IsNullOrEmpty(SeriesName))
+ {
+ info.SeriesName = Parents.Select(i => i.Name).FirstOrDefault();
+ }
+ else
+ {
+ info.SeriesName = SeriesName;
+ }
+
+ return info;
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs
index 9c6b60969..416796b69 100644
--- a/MediaBrowser.Controller/Entities/CollectionFolder.cs
+++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
using System;
using System.Collections.Generic;
using System.IO;
@@ -60,12 +61,12 @@ namespace MediaBrowser.Controller.Entities
public List<string> PhysicalLocationsList { get; set; }
- protected override IEnumerable<FileSystemInfo> GetFileSystemChildren()
+ protected override IEnumerable<FileSystemInfo> GetFileSystemChildren(IDirectoryService directoryService)
{
- return CreateResolveArgs().FileSystemChildren;
+ return CreateResolveArgs(directoryService).FileSystemChildren;
}
- private ItemResolveArgs CreateResolveArgs()
+ private ItemResolveArgs CreateResolveArgs(IDirectoryService directoryService)
{
var path = ContainingFolderPath;
@@ -84,7 +85,7 @@ namespace MediaBrowser.Controller.Entities
// When resolving the root, we need it's grandchildren (children of user views)
var flattenFolderDepth = isPhysicalRoot ? 2 : 0;
- var fileSystemDictionary = FileData.GetFilteredFileSystemEntries(args.Path, FileSystem, Logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf);
+ var fileSystemDictionary = FileData.GetFilteredFileSystemEntries(directoryService, args.Path, FileSystem, Logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf);
// Need to remove subpaths that may have been resolved from shortcuts
// Example: if \\server\movies exists, then strip out \\server\movies\action
@@ -116,11 +117,13 @@ namespace MediaBrowser.Controller.Entities
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="recursive">if set to <c>true</c> [recursive].</param>
- /// <param name="forceRefreshMetadata">if set to <c>true</c> [force refresh metadata].</param>
+ /// <param name="refreshChildMetadata">if set to <c>true</c> [refresh child metadata].</param>
+ /// <param name="refreshOptions">The refresh options.</param>
+ /// <param name="directoryService">The directory service.</param>
/// <returns>Task.</returns>
- protected override Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool? recursive = null, bool forceRefreshMetadata = false)
+ protected override Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService)
{
- CreateResolveArgs();
+ CreateResolveArgs(directoryService);
ResetDynamicChildren();
return NullTaskResult;
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index 63a1c2bab..cb14ed099 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -1,11 +1,9 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
using MoreLinq;
using System;
@@ -301,15 +299,27 @@ namespace MediaBrowser.Controller.Entities
/// <value>The current validation cancellation token source.</value>
private CancellationTokenSource CurrentValidationCancellationTokenSource { get; set; }
+ public Task ValidateChildren(IProgress<double> progress, CancellationToken cancellationToken)
+ {
+ return ValidateChildren(progress, cancellationToken, new MetadataRefreshOptions());
+ }
+
/// <summary>
/// Validates that the children of the folder still exist
/// </summary>
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The cancellation token.</param>
+ /// <param name="metadataRefreshOptions">The metadata refresh options.</param>
/// <param name="recursive">if set to <c>true</c> [recursive].</param>
- /// <param name="forceRefreshMetadata">if set to <c>true</c> [force refresh metadata].</param>
/// <returns>Task.</returns>
- public async Task ValidateChildren(IProgress<double> progress, CancellationToken cancellationToken, bool? recursive = null, bool forceRefreshMetadata = false)
+ public Task ValidateChildren(IProgress<double> progress, CancellationToken cancellationToken, MetadataRefreshOptions metadataRefreshOptions, bool recursive = true)
+ {
+ metadataRefreshOptions.DirectoryService = metadataRefreshOptions.DirectoryService ?? new DirectoryService(Logger);
+
+ return ValidateChildrenWithCancellationSupport(progress, cancellationToken, recursive, true, metadataRefreshOptions, metadataRefreshOptions.DirectoryService);
+ }
+
+ private async Task ValidateChildrenWithCancellationSupport(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -329,7 +339,7 @@ namespace MediaBrowser.Controller.Entities
var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(innerCancellationTokenSource.Token, cancellationToken);
- await ValidateChildrenInternal(progress, linkedCancellationTokenSource.Token, recursive, forceRefreshMetadata).ConfigureAwait(false);
+ await ValidateChildrenInternal(progress, linkedCancellationTokenSource.Token, recursive, refreshChildMetadata, refreshOptions, directoryService).ConfigureAwait(false);
}
catch (OperationCanceledException ex)
{
@@ -354,21 +364,22 @@ namespace MediaBrowser.Controller.Entities
}
/// <summary>
- /// Compare our current children (presumably just read from the repo) with the current state of the file system and adjust for any changes
- /// ***Currently does not contain logic to maintain items that are unavailable in the file system***
+ /// Validates the children internal.
/// </summary>
/// <param name="progress">The progress.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="recursive">if set to <c>true</c> [recursive].</param>
- /// <param name="forceRefreshMetadata">if set to <c>true</c> [force refresh metadata].</param>
+ /// <param name="refreshChildMetadata">if set to <c>true</c> [refresh child metadata].</param>
+ /// <param name="refreshOptions">The refresh options.</param>
+ /// <param name="directoryService">The directory service.</param>
/// <returns>Task.</returns>
- protected async virtual Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool? recursive = null, bool forceRefreshMetadata = false)
+ protected async virtual Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService)
{
var locationType = LocationType;
cancellationToken.ThrowIfCancellationRequested();
- var validChildren = new List<Tuple<BaseItem, bool>>();
+ var validChildren = new List<BaseItem>();
if (locationType != LocationType.Remote && locationType != LocationType.Virtual)
{
@@ -376,7 +387,7 @@ namespace MediaBrowser.Controller.Entities
try
{
- nonCachedChildren = GetNonCachedChildren();
+ nonCachedChildren = GetNonCachedChildren(directoryService);
}
catch (IOException ex)
{
@@ -403,43 +414,30 @@ namespace MediaBrowser.Controller.Entities
if (currentChildren.TryGetValue(child.Id, out currentChild))
{
- //existing item - check if it has changed
- if (currentChild.HasChanged(child))
- {
- var currentChildLocationType = currentChild.LocationType;
- if (currentChildLocationType != LocationType.Remote &&
- currentChildLocationType != LocationType.Virtual)
- {
- currentChild.DateModified = child.DateModified;
- }
-
- currentChild.IsInMixedFolder = child.IsInMixedFolder;
- validChildren.Add(new Tuple<BaseItem, bool>(currentChild, true));
- }
- else
+ var currentChildLocationType = currentChild.LocationType;
+ if (currentChildLocationType != LocationType.Remote &&
+ currentChildLocationType != LocationType.Virtual)
{
- validChildren.Add(new Tuple<BaseItem, bool>(currentChild, false));
+ currentChild.DateModified = child.DateModified;
}
+ currentChild.IsInMixedFolder = child.IsInMixedFolder;
currentChild.IsOffline = false;
}
else
{
//brand new item - needs to be added
newItems.Add(child);
-
- validChildren.Add(new Tuple<BaseItem, bool>(child, true));
}
+
+ validChildren.Add(currentChild);
}
// If any items were added or removed....
if (newItems.Count > 0 || currentChildren.Count != validChildren.Count)
{
- var newChildren = validChildren.Select(c => c.Item1).ToList();
-
// That's all the new and changed ones - now see if there are any that are missing
- var itemsRemoved = currentChildren.Values.Except(newChildren).ToList();
-
+ var itemsRemoved = currentChildren.Values.Except(validChildren).ToList();
var actualRemovals = new List<BaseItem>();
foreach (var item in itemsRemoved)
@@ -448,14 +446,13 @@ namespace MediaBrowser.Controller.Entities
item.LocationType == LocationType.Remote)
{
// Don't remove these because there's no way to accurately validate them.
- validChildren.Add(new Tuple<BaseItem, bool>(item, false));
+ validChildren.Add(item);
}
else if (!string.IsNullOrEmpty(item.Path) && IsPathOffline(item.Path))
{
item.IsOffline = true;
-
- validChildren.Add(new Tuple<BaseItem, bool>(item, false));
+ validChildren.Add(item);
}
else
{
@@ -481,88 +478,134 @@ namespace MediaBrowser.Controller.Entities
await ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false);
}
}
- else
- {
- validChildren.AddRange(ActualChildren.Select(i => new Tuple<BaseItem, bool>(i, false)));
- }
progress.Report(10);
cancellationToken.ThrowIfCancellationRequested();
- await RefreshChildren(validChildren, progress, cancellationToken, recursive, forceRefreshMetadata).ConfigureAwait(false);
+ if (recursive)
+ {
+ await ValidateSubFolders(ActualChildren.OfType<Folder>().ToList(), directoryService, progress, cancellationToken).ConfigureAwait(false);
+ }
+
+ progress.Report(20);
+
+ if (refreshChildMetadata)
+ {
+ var container = this as IMetadataContainer;
+
+ var innerProgress = new ActionableProgress<double>();
+
+ innerProgress.RegisterAction(p => progress.Report((.80 * p) + 20));
+
+ if (container != null)
+ {
+ await container.RefreshAllMetadata(refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false);
+ }
+ else
+ {
+ await RefreshMetadataRecursive(refreshOptions, recursive, innerProgress, cancellationToken);
+ }
+ }
progress.Report(100);
}
- /// <summary>
- /// Refreshes the children.
- /// </summary>
- /// <param name="children">The children.</param>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="recursive">if set to <c>true</c> [recursive].</param>
- /// <param name="forceRefreshMetadata">if set to <c>true</c> [force refresh metadata].</param>
- /// <returns>Task.</returns>
- private async Task RefreshChildren(IList<Tuple<BaseItem, bool>> children, IProgress<double> progress, CancellationToken cancellationToken, bool? recursive, bool forceRefreshMetadata = false)
+ private async Task RefreshMetadataRecursive(MetadataRefreshOptions refreshOptions, bool recursive, IProgress<double> progress, CancellationToken cancellationToken)
{
- var list = children;
+ var children = ActualChildren.ToList();
- var percentages = new Dictionary<Guid, double>(list.Count);
+ var percentages = new Dictionary<Guid, double>(children.Count);
var tasks = new List<Task>();
- foreach (var tuple in list)
+ foreach (var child in children)
{
- if (tasks.Count > 10)
+ if (tasks.Count >= 8)
{
await Task.WhenAll(tasks).ConfigureAwait(false);
+ tasks.Clear();
}
- tasks.Add(RefreshChild(tuple, progress, percentages, list.Count, cancellationToken, recursive, forceRefreshMetadata));
- }
+ cancellationToken.ThrowIfCancellationRequested();
+ var innerProgress = new ActionableProgress<double>();
- cancellationToken.ThrowIfCancellationRequested();
+ // Avoid implicitly captured closure
+ var currentChild = child;
+ innerProgress.RegisterAction(p =>
+ {
+ lock (percentages)
+ {
+ percentages[currentChild.Id] = p / 100;
+
+ var percent = percentages.Values.Sum();
+ percent /= children.Count;
+ percent *= 100;
+ progress.Report(percent);
+ }
+ });
+
+ if (child.IsFolder)
+ {
+ await RefreshChildMetadata(child, refreshOptions, recursive, innerProgress, cancellationToken)
+ .ConfigureAwait(false);
+ }
+ else
+ {
+ tasks.Add(RefreshChildMetadata(child, refreshOptions, false, innerProgress, cancellationToken));
+ }
+ }
await Task.WhenAll(tasks).ConfigureAwait(false);
+ progress.Report(100);
}
- private async Task RefreshChild(Tuple<BaseItem, bool> currentTuple, IProgress<double> progress, Dictionary<Guid, double> percentages, int childCount, CancellationToken cancellationToken, bool? recursive, bool forceRefreshMetadata = false)
+ private async Task RefreshChildMetadata(BaseItem child, MetadataRefreshOptions refreshOptions, bool recursive, IProgress<double> progress, CancellationToken cancellationToken)
{
- cancellationToken.ThrowIfCancellationRequested();
+ var container = child as IMetadataContainer;
- var child = currentTuple.Item1;
- try
+ if (container != null)
{
- //refresh it
- await child.RefreshMetadata(new MetadataRefreshOptions
- {
- ForceSave = currentTuple.Item2,
- ReplaceAllMetadata = forceRefreshMetadata
-
- }, cancellationToken).ConfigureAwait(false);
+ await container.RefreshAllMetadata(refreshOptions, progress, cancellationToken).ConfigureAwait(false);
}
- catch (IOException ex)
+ else
{
- Logger.ErrorException("Error refreshing {0}", ex, child.Path ?? child.Name);
- }
-
- // Refresh children if a folder and the item changed or recursive is set to true
- var refreshChildren = child.IsFolder && (currentTuple.Item2 || (recursive.HasValue && recursive.Value));
+ await child.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
- if (refreshChildren)
- {
- // Don't refresh children if explicitly set to false
- if (recursive.HasValue && recursive.Value == false)
+ if (recursive)
{
- refreshChildren = false;
+ var folder = child as Folder;
+
+ if (folder != null)
+ {
+ await folder.RefreshMetadataRecursive(refreshOptions, true, progress, cancellationToken);
+ }
}
}
+ progress.Report(100);
+ }
+
+ /// <summary>
+ /// Refreshes the children.
+ /// </summary>
+ /// <param name="children">The children.</param>
+ /// <param name="directoryService">The directory service.</param>
+ /// <param name="progress">The progress.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ private async Task ValidateSubFolders(IList<Folder> children, IDirectoryService directoryService, IProgress<double> progress, CancellationToken cancellationToken)
+ {
+ var list = children;
+ var childCount = list.Count;
+
+ var percentages = new Dictionary<Guid, double>(list.Count);
- if (refreshChildren)
+ foreach (var item in list)
{
cancellationToken.ThrowIfCancellationRequested();
+ var child = item;
+
var innerProgress = new ActionableProgress<double>();
innerProgress.RegisterAction(p =>
@@ -574,23 +617,12 @@ namespace MediaBrowser.Controller.Entities
var percent = percentages.Values.Sum();
percent /= childCount;
- progress.Report((90 * percent) + 10);
+ progress.Report((10 * percent) + 10);
}
});
- await ((Folder)child).ValidateChildren(innerProgress, cancellationToken, recursive, forceRefreshMetadata).ConfigureAwait(false);
- }
- else
- {
- lock (percentages)
- {
- percentages[child.Id] = 1;
-
- var percent = percentages.Values.Sum();
- percent /= childCount;
-
- progress.Report((90 * percent) + 10);
- }
+ await child.ValidateChildrenWithCancellationSupport(innerProgress, cancellationToken, true, false, null, directoryService)
+ .ConfigureAwait(false);
}
}
@@ -647,9 +679,9 @@ namespace MediaBrowser.Controller.Entities
/// Get the children of this folder from the actual file system
/// </summary>
/// <returns>IEnumerable{BaseItem}.</returns>
- protected virtual IEnumerable<BaseItem> GetNonCachedChildren()
+ protected virtual IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
{
- return LibraryManager.ResolvePaths<BaseItem>(GetFileSystemChildren(), this);
+ return LibraryManager.ResolvePaths<BaseItem>(GetFileSystemChildren(directoryService), this);
}
/// <summary>
@@ -895,17 +927,21 @@ namespace MediaBrowser.Controller.Entities
return item;
}
- protected override Task BeforeRefreshMetadata(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
+ protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
+ var changesFound = false;
+
if (SupportsShortcutChildren && LocationType == LocationType.FileSystem)
{
if (RefreshLinkedChildren(fileSystemChildren))
{
- options.ForceSave = true;
+ changesFound = true;
}
}
- return base.BeforeRefreshMetadata(options, fileSystemChildren, cancellationToken);
+ var baseHasChanges = await base.RefreshedOwnedItems(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
+
+ return baseHasChanges || changesFound;
}
/// <summary>
@@ -967,11 +1003,11 @@ namespace MediaBrowser.Controller.Entities
/// <returns>Task.</returns>
public override async Task ChangedExternally()
{
- await base.ChangedExternally().ConfigureAwait(false);
-
var progress = new Progress<double>();
await ValidateChildren(progress, CancellationToken.None).ConfigureAwait(false);
+
+ await base.ChangedExternally().ConfigureAwait(false);
}
/// <summary>
@@ -1016,50 +1052,17 @@ namespace MediaBrowser.Controller.Entities
throw new ArgumentNullException();
}
- try
- {
- var locationType = LocationType;
-
- if (locationType == LocationType.Remote && string.Equals(Path, path, StringComparison.OrdinalIgnoreCase))
- {
- return this;
- }
-
- if (locationType != LocationType.Virtual && PhysicalLocations.Contains(path, StringComparer.OrdinalIgnoreCase))
- {
- return this;
- }
- }
- catch (IOException ex)
+ if (string.Equals(Path, path, StringComparison.OrdinalIgnoreCase))
{
- Logger.ErrorException("Error getting ResolveArgs for {0}", ex, Path);
+ return this;
}
- return RecursiveChildren.Where(i => i.LocationType != LocationType.Virtual).FirstOrDefault(i =>
+ if (PhysicalLocations.Contains(path, StringComparer.OrdinalIgnoreCase))
{
- try
- {
- if (string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
-
- if (i.LocationType != LocationType.Remote)
- {
- if (i.PhysicalLocations.Contains(path, StringComparer.OrdinalIgnoreCase))
- {
- return true;
- }
- }
+ return this;
+ }
- return false;
- }
- catch (IOException ex)
- {
- Logger.ErrorException("Error getting ResolveArgs for {0}", ex, Path);
- return false;
- }
- });
+ return RecursiveChildren.FirstOrDefault(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) || i.PhysicalLocations.Contains(path, StringComparer.OrdinalIgnoreCase));
}
public override bool IsPlayed(User user)
diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs
index 1b5176362..cc9d9a1a4 100644
--- a/MediaBrowser.Controller/Entities/Game.cs
+++ b/MediaBrowser.Controller/Entities/Game.cs
@@ -1,11 +1,12 @@
-using MediaBrowser.Model.Configuration;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Entities
{
- public class Game : BaseItem, IHasSoundtracks, IHasTrailers, IHasThemeMedia, IHasTags, IHasScreenshots, IHasPreferredMetadataLanguage
+ public class Game : BaseItem, IHasSoundtracks, IHasTrailers, IHasThemeMedia, IHasTags, IHasScreenshots, IHasPreferredMetadataLanguage, IHasLookupInfo<GameInfo>
{
public List<Guid> SoundtrackIds { get; set; }
@@ -29,18 +30,11 @@ namespace MediaBrowser.Controller.Entities
ThemeSongIds = new List<Guid>();
ThemeVideoIds = new List<Guid>();
Tags = new List<string>();
- ScreenshotImagePaths = new List<string>();
}
public List<Guid> LocalTrailerIds { get; set; }
/// <summary>
- /// Gets or sets the screenshot image paths.
- /// </summary>
- /// <value>The screenshot image paths.</value>
- public List<string> ScreenshotImagePaths { get; set; }
-
- /// <summary>
/// Gets or sets the tags.
/// </summary>
/// <value>The tags.</value>
@@ -115,5 +109,14 @@ namespace MediaBrowser.Controller.Entities
{
return config.BlockUnratedGames;
}
+
+ public GameInfo GetLookupInfo()
+ {
+ var id = GetItemLookupInfo<GameInfo>();
+
+ id.GameSystem = GameSystem;
+
+ return id;
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/GameGenre.cs b/MediaBrowser.Controller/Entities/GameGenre.cs
index ffe62ba03..3a3c575cd 100644
--- a/MediaBrowser.Controller/Entities/GameGenre.cs
+++ b/MediaBrowser.Controller/Entities/GameGenre.cs
@@ -1,5 +1,4 @@
using MediaBrowser.Model.Dto;
-using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
@@ -23,5 +22,30 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember]
public List<ItemByNameCounts> UserItemCountList { get; set; }
+
+ /// <summary>
+ /// Returns the folder containing the item.
+ /// If the item is a folder, it returns the folder itself
+ /// </summary>
+ /// <value>The containing folder path.</value>
+ public override string ContainingFolderPath
+ {
+ get
+ {
+ return Path;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance is owned item.
+ /// </summary>
+ /// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value>
+ public override bool IsOwnedItem
+ {
+ get
+ {
+ return false;
+ }
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/GameSystem.cs b/MediaBrowser.Controller/Entities/GameSystem.cs
index 69cb5e974..f2fec4397 100644
--- a/MediaBrowser.Controller/Entities/GameSystem.cs
+++ b/MediaBrowser.Controller/Entities/GameSystem.cs
@@ -1,4 +1,5 @@
using System.Runtime.Serialization;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using System;
@@ -7,7 +8,7 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// Class GameSystem
/// </summary>
- public class GameSystem : Folder
+ public class GameSystem : Folder, IHasLookupInfo<GameSystemInfo>
{
/// <summary>
/// Return the id that should be used to key display prefs for this item.
@@ -47,5 +48,14 @@ namespace MediaBrowser.Controller.Entities
// Don't block. Determine by game
return false;
}
+
+ public GameSystemInfo GetLookupInfo()
+ {
+ var id = GetItemLookupInfo<GameSystemInfo>();
+
+ id.Path = Path;
+
+ return id;
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs
index 53bc64194..c15ca0aa2 100644
--- a/MediaBrowser.Controller/Entities/Genre.cs
+++ b/MediaBrowser.Controller/Entities/Genre.cs
@@ -25,5 +25,30 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember]
public List<ItemByNameCounts> UserItemCountList { get; set; }
+
+ /// <summary>
+ /// Returns the folder containing the item.
+ /// If the item is a folder, it returns the folder itself
+ /// </summary>
+ /// <value>The containing folder path.</value>
+ public override string ContainingFolderPath
+ {
+ get
+ {
+ return Path;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance is owned item.
+ /// </summary>
+ /// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value>
+ public override bool IsOwnedItem
+ {
+ get
+ {
+ return false;
+ }
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/IHasImages.cs b/MediaBrowser.Controller/Entities/IHasImages.cs
index dd6194bc7..d53eba11a 100644
--- a/MediaBrowser.Controller/Entities/IHasImages.cs
+++ b/MediaBrowser.Controller/Entities/IHasImages.cs
@@ -1,6 +1,8 @@
-using MediaBrowser.Model.Entities;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
+using System.IO;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Entities
@@ -32,6 +34,13 @@ namespace MediaBrowser.Controller.Entities
LocationType LocationType { get; }
/// <summary>
+ /// Gets the images.
+ /// </summary>
+ /// <param name="imageType">Type of the image.</param>
+ /// <returns>IEnumerable{ItemImageInfo}.</returns>
+ IEnumerable<ItemImageInfo> GetImages(ImageType imageType);
+
+ /// <summary>
/// Gets the image path.
/// </summary>
/// <param name="imageType">Type of the image.</param>
@@ -40,19 +49,20 @@ namespace MediaBrowser.Controller.Entities
string GetImagePath(ImageType imageType, int imageIndex);
/// <summary>
- /// Gets the image date modified.
+ /// Gets the image information.
/// </summary>
- /// <param name="imagePath">The image path.</param>
- /// <returns>DateTime.</returns>
- DateTime GetImageDateModified(string imagePath);
+ /// <param name="imageType">Type of the image.</param>
+ /// <param name="imageIndex">Index of the image.</param>
+ /// <returns>ItemImageInfo.</returns>
+ ItemImageInfo GetImageInfo(ImageType imageType, int imageIndex);
/// <summary>
/// Sets the image.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="index">The index.</param>
- /// <param name="path">The path.</param>
- void SetImagePath(ImageType type, int index, string path);
+ /// <param name="file">The file.</param>
+ void SetImagePath(ImageType type, int index, FileInfo file);
/// <summary>
/// Determines whether the specified type has image.
@@ -63,6 +73,13 @@ namespace MediaBrowser.Controller.Entities
bool HasImage(ImageType type, int imageIndex);
/// <summary>
+ /// Allowses the multiple images.
+ /// </summary>
+ /// <param name="type">The type.</param>
+ /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
+ bool AllowsMultipleImages(ImageType type);
+
+ /// <summary>
/// Swaps the images.
/// </summary>
/// <param name="type">The type.</param>
@@ -92,13 +109,7 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// Validates the images and returns true or false indicating if any were removed.
/// </summary>
- bool ValidateImages();
-
- /// <summary>
- /// Gets or sets the backdrop image paths.
- /// </summary>
- /// <value>The backdrop image paths.</value>
- List<string> BackdropImagePaths { get; set; }
+ bool ValidateImages(IDirectoryService directoryService);
/// <summary>
/// Gets a value indicating whether this instance is owned item.
@@ -111,6 +122,26 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <value>The containing folder path.</value>
string ContainingFolderPath { get; }
+
+ /// <summary>
+ /// Adds the images.
+ /// </summary>
+ /// <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>
+ bool AddImages(ImageType imageType, IEnumerable<FileInfo> images);
+
+ /// <summary>
+ /// Determines whether [is save local metadata enabled].
+ /// </summary>
+ /// <returns><c>true</c> if [is save local metadata enabled]; otherwise, <c>false</c>.</returns>
+ bool IsSaveLocalMetadataEnabled();
+
+ /// <summary>
+ /// Gets a value indicating whether [supports local metadata].
+ /// </summary>
+ /// <value><c>true</c> if [supports local metadata]; otherwise, <c>false</c>.</value>
+ bool SupportsLocalMetadata { get; }
}
public static class HasImagesExtensions
@@ -136,10 +167,21 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <param name="item">The item.</param>
/// <param name="imageType">Type of the image.</param>
- /// <param name="path">The path.</param>
- public static void SetImagePath(this IHasImages item, ImageType imageType, string path)
+ /// <param name="file">The file.</param>
+ public static void SetImagePath(this IHasImages item, ImageType imageType, FileInfo file)
+ {
+ item.SetImagePath(imageType, 0, file);
+ }
+
+ /// <summary>
+ /// Sets the image path.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <param name="imageType">Type of the image.</param>
+ /// <param name="file">The file.</param>
+ public static void SetImagePath(this IHasImages item, ImageType imageType, string file)
{
- item.SetImagePath(imageType, 0, path);
+ item.SetImagePath(imageType, new FileInfo(file));
}
}
}
diff --git a/MediaBrowser.Controller/Providers/IHasMetadata.cs b/MediaBrowser.Controller/Entities/IHasMetadata.cs
index 19ab00e68..0285b6749 100644
--- a/MediaBrowser.Controller/Providers/IHasMetadata.cs
+++ b/MediaBrowser.Controller/Entities/IHasMetadata.cs
@@ -1,9 +1,11 @@
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
-namespace MediaBrowser.Controller.Providers
+namespace MediaBrowser.Controller.Entities
{
/// <summary>
/// Interface IHasMetadata
@@ -35,15 +37,23 @@ namespace MediaBrowser.Controller.Providers
DateTime DateLastSaved { get; set; }
/// <summary>
- /// Determines whether [is save local metadata enabled].
- /// </summary>
- /// <returns><c>true</c> if [is save local metadata enabled]; otherwise, <c>false</c>.</returns>
- bool IsSaveLocalMetadataEnabled();
-
- /// <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>
bool IsInMixedFolder { get; }
+
+ /// <summary>
+ /// Updates to repository.
+ /// </summary>
+ /// <param name="updateReason">The update reason.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// This is called before any metadata refresh and returns ItemUpdateType indictating if changes were made, and what.
+ /// </summary>
+ /// <returns>ItemUpdateType.</returns>
+ ItemUpdateType BeforeMetadataRefresh();
}
}
diff --git a/MediaBrowser.Controller/Entities/IHasScreenshots.cs b/MediaBrowser.Controller/Entities/IHasScreenshots.cs
index 341d6403f..2fd402bc2 100644
--- a/MediaBrowser.Controller/Entities/IHasScreenshots.cs
+++ b/MediaBrowser.Controller/Entities/IHasScreenshots.cs
@@ -1,5 +1,4 @@
-using System.Collections.Generic;
-
+
namespace MediaBrowser.Controller.Entities
{
/// <summary>
@@ -7,10 +6,5 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public interface IHasScreenshots
{
- /// <summary>
- /// Gets or sets the screenshot image paths.
- /// </summary>
- /// <value>The screenshot image paths.</value>
- List<string> ScreenshotImagePaths { get; set; }
}
}
diff --git a/MediaBrowser.Controller/Entities/IHasSeries.cs b/MediaBrowser.Controller/Entities/IHasSeries.cs
new file mode 100644
index 000000000..64c33a376
--- /dev/null
+++ b/MediaBrowser.Controller/Entities/IHasSeries.cs
@@ -0,0 +1,12 @@
+
+namespace MediaBrowser.Controller.Entities
+{
+ public interface IHasSeries
+ {
+ /// <summary>
+ /// Gets the name of the series.
+ /// </summary>
+ /// <value>The name of the series.</value>
+ string SeriesName { get; }
+ }
+}
diff --git a/MediaBrowser.Controller/Entities/IMetadataContainer.cs b/MediaBrowser.Controller/Entities/IMetadataContainer.cs
new file mode 100644
index 000000000..33aa08425
--- /dev/null
+++ b/MediaBrowser.Controller/Entities/IMetadataContainer.cs
@@ -0,0 +1,19 @@
+using MediaBrowser.Controller.Providers;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Entities
+{
+ public interface IMetadataContainer
+ {
+ /// <summary>
+ /// Refreshes all metadata.
+ /// </summary>
+ /// <param name="refreshOptions">The refresh options.</param>
+ /// <param name="progress">The progress.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken);
+ }
+}
diff --git a/MediaBrowser.Controller/Entities/ItemImageInfo.cs b/MediaBrowser.Controller/Entities/ItemImageInfo.cs
new file mode 100644
index 000000000..80aec6482
--- /dev/null
+++ b/MediaBrowser.Controller/Entities/ItemImageInfo.cs
@@ -0,0 +1,14 @@
+using MediaBrowser.Model.Entities;
+using System;
+
+namespace MediaBrowser.Controller.Entities
+{
+ public class ItemImageInfo
+ {
+ public string Path { get; set; }
+
+ public ImageType Type { get; set; }
+
+ public DateTime DateModified { get; set; }
+ }
+}
diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
index 19d0d6682..a00bda772 100644
--- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
+++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
@@ -1,15 +1,20 @@
-using MediaBrowser.Model.Configuration;
+using MediaBrowser.Common.Progress;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using System;
using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
namespace MediaBrowser.Controller.Entities.Movies
{
/// <summary>
/// Class BoxSet
/// </summary>
- public class BoxSet : Folder, IHasTrailers, IHasTags, IHasKeywords, IHasPreferredMetadataLanguage, IHasDisplayOrder
+ public class BoxSet : Folder, IHasTrailers, IHasTags, IHasKeywords, IHasPreferredMetadataLanguage, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IMetadataContainer
{
public BoxSet()
{
@@ -74,5 +79,67 @@ namespace MediaBrowser.Controller.Entities.Movies
// Default sorting
return LibraryManager.Sort(children, user, new[] { ItemSortBy.ProductionYear, ItemSortBy.PremiereDate, ItemSortBy.SortName }, SortOrder.Ascending);
}
+
+ public BoxSetInfo GetLookupInfo()
+ {
+ return GetItemLookupInfo<BoxSetInfo>();
+ }
+
+ public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
+ {
+ // Refresh bottom up, children first, then the boxset
+ // By then hopefully the movies within will have Tmdb collection values
+ var items = RecursiveChildren.ToList();
+
+ var totalItems = items.Count;
+ var percentages = new Dictionary<Guid, double>(totalItems);
+
+ var tasks = new List<Task>();
+
+ // Refresh songs
+ foreach (var item in items)
+ {
+ if (tasks.Count >= 2)
+ {
+ await Task.WhenAll(tasks).ConfigureAwait(false);
+ tasks.Clear();
+ }
+
+ cancellationToken.ThrowIfCancellationRequested();
+ var innerProgress = new ActionableProgress<double>();
+
+ // Avoid implicitly captured closure
+ var currentChild = item;
+ innerProgress.RegisterAction(p =>
+ {
+ lock (percentages)
+ {
+ percentages[currentChild.Id] = p / 100;
+
+ var percent = percentages.Values.Sum();
+ percent /= totalItems;
+ percent *= 100;
+ progress.Report(percent);
+ }
+ });
+
+ tasks.Add(RefreshItem(item, refreshOptions, innerProgress, cancellationToken));
+ }
+
+ await Task.WhenAll(tasks).ConfigureAwait(false);
+ tasks.Clear();
+
+ // Refresh current item
+ await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
+
+ progress.Report(100);
+ }
+
+ private async Task RefreshItem(BaseItem item, MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
+ {
+ await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
+
+ progress.Report(100);
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs
index 41a1969d6..8eba21df0 100644
--- a/MediaBrowser.Controller/Entities/Movies/Movie.cs
+++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs
@@ -13,7 +13,7 @@ namespace MediaBrowser.Controller.Entities.Movies
/// <summary>
/// Class Movie
/// </summary>
- public class Movie : Video, IHasCriticRating, IHasSoundtracks, IHasBudget, IHasKeywords, IHasTrailers, IHasThemeMedia, IHasTaglines, IHasTags, IHasPreferredMetadataLanguage, IHasAwards, IHasMetascore
+ public class Movie : Video, IHasCriticRating, IHasSoundtracks, IHasBudget, IHasKeywords, IHasTrailers, IHasThemeMedia, IHasTaglines, IHasPreferredMetadataLanguage, IHasAwards, IHasMetascore, IHasLookupInfo<MovieInfo>
{
public List<Guid> SpecialFeatureIds { get; set; }
@@ -39,7 +39,6 @@ namespace MediaBrowser.Controller.Entities.Movies
ThemeSongIds = new List<Guid>();
ThemeVideoIds = new List<Guid>();
Taglines = new List<string>();
- Tags = new List<string>();
Keywords = new List<string>();
}
@@ -53,12 +52,6 @@ namespace MediaBrowser.Controller.Entities.Movies
public List<MediaUrl> RemoteTrailers { get; set; }
/// <summary>
- /// Gets or sets the tags.
- /// </summary>
- /// <value>The tags.</value>
- public List<string> Tags { get; set; }
-
- /// <summary>
/// Gets or sets the taglines.
/// </summary>
/// <value>The taglines.</value>
@@ -103,9 +96,9 @@ namespace MediaBrowser.Controller.Entities.Movies
return this.GetProviderId(MetadataProviders.Tmdb) ?? this.GetProviderId(MetadataProviders.Imdb) ?? base.GetUserDataKey();
}
- protected override async Task BeforeRefreshMetadata(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
+ protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
- await base.BeforeRefreshMetadata(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
+ var hasChanges = await base.RefreshedOwnedItems(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
// Must have a parent to have special features
// In other words, it must be part of the Parent/Child tree
@@ -115,12 +108,14 @@ namespace MediaBrowser.Controller.Entities.Movies
if (specialFeaturesChanged)
{
- options.ForceSave = true;
+ hasChanges = true;
}
}
+
+ return hasChanges;
}
- private async Task<bool> RefreshSpecialFeatures(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
+ private async Task<bool> RefreshSpecialFeatures(MetadataRefreshOptions options, IEnumerable<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
var newItems = LoadSpecialFeatures(fileSystemChildren).ToList();
var newItemIds = newItems.Select(i => i.Id).ToList();
@@ -157,12 +152,19 @@ namespace MediaBrowser.Controller.Entities.Movies
}
return video;
- });
+
+ // Sort them so that the list can be easily compared for changes
+ }).OrderBy(i => i.Path).ToList();
}
protected override bool GetBlockUnratedValue(UserConfiguration config)
{
return config.BlockUnratedMovies;
}
+
+ public MovieInfo GetLookupInfo()
+ {
+ return GetItemLookupInfo<MovieInfo>();
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/MusicVideo.cs b/MediaBrowser.Controller/Entities/MusicVideo.cs
index d9eff8fbe..56cd71d49 100644
--- a/MediaBrowser.Controller/Entities/MusicVideo.cs
+++ b/MediaBrowser.Controller/Entities/MusicVideo.cs
@@ -1,11 +1,12 @@
using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using System;
namespace MediaBrowser.Controller.Entities
{
- public class MusicVideo : Video, IHasArtist, IHasMusicGenres, IHasBudget
+ public class MusicVideo : Video, IHasArtist, IHasMusicGenres, IHasBudget, IHasLookupInfo<MusicVideoInfo>
{
/// <summary>
/// Gets or sets the artist.
@@ -54,5 +55,10 @@ namespace MediaBrowser.Controller.Entities
{
return config.BlockUnratedMusic;
}
+
+ public MusicVideoInfo GetLookupInfo()
+ {
+ return GetItemLookupInfo<MusicVideoInfo>();
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs
index 832586ab9..c1dc81136 100644
--- a/MediaBrowser.Controller/Entities/Person.cs
+++ b/MediaBrowser.Controller/Entities/Person.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Model.Dto;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Dto;
using System.Collections.Generic;
using System.Runtime.Serialization;
@@ -7,7 +8,7 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// This is the full Person object that can be retrieved with all of it's data.
/// </summary>
- public class Person : BaseItem, IItemByName
+ public class Person : BaseItem, IItemByName, IHasLookupInfo<PersonLookupInfo>
{
public Person()
{
@@ -31,6 +32,36 @@ namespace MediaBrowser.Controller.Entities
{
return "Person-" + Name;
}
+
+ public PersonLookupInfo GetLookupInfo()
+ {
+ return GetItemLookupInfo<PersonLookupInfo>();
+ }
+
+ /// <summary>
+ /// Returns the folder containing the item.
+ /// If the item is a folder, it returns the folder itself
+ /// </summary>
+ /// <value>The containing folder path.</value>
+ public override string ContainingFolderPath
+ {
+ get
+ {
+ return Path;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance is owned item.
+ /// </summary>
+ /// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value>
+ public override bool IsOwnedItem
+ {
+ get
+ {
+ return false;
+ }
+ }
}
/// <summary>
diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs
index 7bc17549f..5c3946f9b 100644
--- a/MediaBrowser.Controller/Entities/Studio.cs
+++ b/MediaBrowser.Controller/Entities/Studio.cs
@@ -26,5 +26,30 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember]
public List<ItemByNameCounts> UserItemCountList { get; set; }
+
+ /// <summary>
+ /// Returns the folder containing the item.
+ /// If the item is a folder, it returns the folder itself
+ /// </summary>
+ /// <value>The containing folder path.</value>
+ public override string ContainingFolderPath
+ {
+ get
+ {
+ return Path;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance is owned item.
+ /// </summary>
+ /// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value>
+ public override bool IsOwnedItem
+ {
+ get
+ {
+ return false;
+ }
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs
index 73726a4e2..daff3dd6c 100644
--- a/MediaBrowser.Controller/Entities/TV/Episode.cs
+++ b/MediaBrowser.Controller/Entities/TV/Episode.cs
@@ -1,4 +1,7 @@
-using MediaBrowser.Model.Configuration;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -9,7 +12,7 @@ namespace MediaBrowser.Controller.Entities.TV
/// <summary>
/// Class Episode
/// </summary>
- public class Episode : Video
+ public class Episode : Video, IHasLookupInfo<EpisodeInfo>, IHasSeries
{
/// <summary>
/// Gets the season in which it aired.
@@ -41,7 +44,7 @@ namespace MediaBrowser.Controller.Entities.TV
/// </summary>
/// <value>The index number.</value>
public int? IndexNumberEnd { get; set; }
-
+
/// <summary>
/// We want to group into series not show individually in an index
/// </summary>
@@ -98,9 +101,11 @@ namespace MediaBrowser.Controller.Entities.TV
/// <returns>System.String.</returns>
public override string GetUserDataKey()
{
- if (Series != null && ParentIndexNumber.HasValue && IndexNumber.HasValue)
+ var series = Series;
+
+ if (series != null && ParentIndexNumber.HasValue && IndexNumber.HasValue)
{
- return Series.GetUserDataKey() + ParentIndexNumber.Value.ToString("000") + IndexNumber.Value.ToString("000");
+ return series.GetUserDataKey() + ParentIndexNumber.Value.ToString("000") + IndexNumber.Value.ToString("000");
}
return base.GetUserDataKey();
@@ -112,16 +117,11 @@ namespace MediaBrowser.Controller.Entities.TV
[IgnoreDataMember]
public override string OfficialRatingForComparison
{
- get { return Series != null ? Series.OfficialRatingForComparison : base.OfficialRatingForComparison; }
- }
-
- /// <summary>
- /// Our rating comes from our series
- /// </summary>
- [IgnoreDataMember]
- public override string CustomRatingForComparison
- {
- get { return Series != null ? Series.CustomRatingForComparison : base.CustomRatingForComparison; }
+ get
+ {
+ var series = Series;
+ return series != null ? series.OfficialRatingForComparison : base.OfficialRatingForComparison;
+ }
}
/// <summary>
@@ -140,6 +140,16 @@ namespace MediaBrowser.Controller.Entities.TV
get { return FindParent<Season>(); }
}
+ [IgnoreDataMember]
+ public string SeriesName
+ {
+ get
+ {
+ var series = Series;
+ return series == null ? null : series.Name;
+ }
+ }
+
/// <summary>
/// Creates the name of the sort.
/// </summary>
@@ -175,7 +185,7 @@ namespace MediaBrowser.Controller.Entities.TV
{
get
{
- return LocationType == Model.Entities.LocationType.Virtual && PremiereDate.HasValue && PremiereDate.Value < DateTime.UtcNow;
+ return LocationType == LocationType.Virtual && PremiereDate.HasValue && PremiereDate.Value < DateTime.UtcNow;
}
}
@@ -188,7 +198,7 @@ namespace MediaBrowser.Controller.Entities.TV
[IgnoreDataMember]
public bool IsVirtualUnaired
{
- get { return LocationType == Model.Entities.LocationType.Virtual && IsUnaired; }
+ get { return LocationType == LocationType.Virtual && IsUnaired; }
}
[IgnoreDataMember]
@@ -236,5 +246,70 @@ namespace MediaBrowser.Controller.Entities.TV
{
return config.BlockUnratedSeries;
}
+
+ public EpisodeInfo GetLookupInfo()
+ {
+ var id = GetItemLookupInfo<EpisodeInfo>();
+
+ var series = Series;
+
+ if (series != null)
+ {
+ id.SeriesProviderIds = series.ProviderIds;
+ }
+
+ id.IndexNumberEnd = IndexNumberEnd;
+
+ return id;
+ }
+
+ public override ItemUpdateType BeforeMetadataRefresh()
+ {
+ var updateType = base.BeforeMetadataRefresh();
+
+ var locationType = LocationType;
+ if (locationType == LocationType.FileSystem || locationType == LocationType.Offline)
+ {
+ if (!IndexNumber.HasValue && !string.IsNullOrEmpty(Path))
+ {
+ IndexNumber = IndexNumber ?? TVUtils.GetEpisodeNumberFromFile(Path, Parent is Season);
+
+ // If a change was made record it
+ if (IndexNumber.HasValue)
+ {
+ updateType = updateType | ItemUpdateType.MetadataImport;
+ }
+ }
+
+ if (!IndexNumberEnd.HasValue && !string.IsNullOrEmpty(Path))
+ {
+ IndexNumberEnd = IndexNumberEnd ?? TVUtils.GetEndingEpisodeNumberFromFile(Path);
+
+ // If a change was made record it
+ if (IndexNumberEnd.HasValue)
+ {
+ updateType = updateType | ItemUpdateType.MetadataImport;
+ }
+ }
+ }
+
+ if (!ParentIndexNumber.HasValue)
+ {
+ var season = Season;
+
+ if (season != null)
+ {
+ ParentIndexNumber = season.IndexNumber;
+ }
+
+ // If a change was made record it
+ if (ParentIndexNumber.HasValue)
+ {
+ updateType = updateType | ItemUpdateType.MetadataImport;
+ }
+ }
+
+ return updateType;
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs
index 744416560..830ccb8a2 100644
--- a/MediaBrowser.Controller/Entities/TV/Season.cs
+++ b/MediaBrowser.Controller/Entities/TV/Season.cs
@@ -1,11 +1,10 @@
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Localization;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
-using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
using System.Runtime.Serialization;
@@ -14,7 +13,7 @@ namespace MediaBrowser.Controller.Entities.TV
/// <summary>
/// Class Season
/// </summary>
- public class Season : Folder
+ public class Season : Folder, IHasSeries, IHasLookupInfo<SeasonInfo>
{
/// <summary>
@@ -119,16 +118,11 @@ namespace MediaBrowser.Controller.Entities.TV
[IgnoreDataMember]
public override string OfficialRatingForComparison
{
- get { return Series != null ? Series.OfficialRatingForComparison : base.OfficialRatingForComparison; }
- }
-
- /// <summary>
- /// Our rating comes from our series
- /// </summary>
- [IgnoreDataMember]
- public override string CustomRatingForComparison
- {
- get { return Series != null ? Series.CustomRatingForComparison : base.CustomRatingForComparison; }
+ get
+ {
+ var series = Series;
+ return series != null ? series.OfficialRatingForComparison : base.OfficialRatingForComparison;
+ }
}
/// <summary>
@@ -223,7 +217,7 @@ namespace MediaBrowser.Controller.Entities.TV
{
episodes = episodes.Where(i => !i.IsVirtualUnaired);
}
-
+
return LibraryManager
.Sort(episodes, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending)
.Cast<Episode>();
@@ -239,5 +233,51 @@ namespace MediaBrowser.Controller.Entities.TV
// Don't block. Let either the entire series rating or episode rating determine it
return false;
}
+
+ [IgnoreDataMember]
+ public string SeriesName
+ {
+ get
+ {
+ var series = Series;
+ return series == null ? null : series.Name;
+ }
+ }
+
+ /// <summary>
+ /// Gets the lookup information.
+ /// </summary>
+ /// <returns>SeasonInfo.</returns>
+ public SeasonInfo GetLookupInfo()
+ {
+ return GetItemLookupInfo<SeasonInfo>();
+ }
+
+ /// <summary>
+ /// This is called before any metadata refresh and returns ItemUpdateType indictating if changes were made, and what.
+ /// </summary>
+ /// <returns>ItemUpdateType.</returns>
+ public override ItemUpdateType BeforeMetadataRefresh()
+ {
+ var updateType = base.BeforeMetadataRefresh();
+
+ var locationType = LocationType;
+
+ if (locationType == LocationType.FileSystem || locationType == LocationType.Offline)
+ {
+ if (!IndexNumber.HasValue && !string.IsNullOrEmpty(Path))
+ {
+ IndexNumber = IndexNumber ?? TVUtils.GetSeasonNumberFromPath(Path);
+
+ // If a change was made record it
+ if (IndexNumber.HasValue)
+ {
+ updateType = updateType | ItemUpdateType.MetadataImport;
+ }
+ }
+ }
+
+ return updateType;
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
index efb3c393b..0e07654d6 100644
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ b/MediaBrowser.Controller/Entities/TV/Series.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Localization;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
@@ -14,7 +15,7 @@ namespace MediaBrowser.Controller.Entities.TV
/// <summary>
/// Class Series
/// </summary>
- public class Series : Folder, IHasSoundtracks, IHasTrailers, IHasTags, IHasPreferredMetadataLanguage, IHasDisplayOrder
+ public class Series : Folder, IHasSoundtracks, IHasTrailers, IHasTags, IHasPreferredMetadataLanguage, IHasDisplayOrder, IHasLookupInfo<SeriesInfo>
{
public List<Guid> SpecialFeatureIds { get; set; }
public List<Guid> SoundtrackIds { get; set; }
@@ -222,5 +223,10 @@ namespace MediaBrowser.Controller.Entities.TV
}
public string PreferredMetadataLanguage { get; set; }
+
+ public SeriesInfo GetLookupInfo()
+ {
+ return GetItemLookupInfo<SeriesInfo>();
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs
index d6d193442..d655c275d 100644
--- a/MediaBrowser.Controller/Entities/Trailer.cs
+++ b/MediaBrowser.Controller/Entities/Trailer.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Model.Configuration;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
@@ -9,7 +10,7 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// Class Trailer
/// </summary>
- public class Trailer : Video, IHasCriticRating, IHasSoundtracks, IHasBudget, IHasTrailers, IHasKeywords, IHasTaglines, IHasTags, IHasPreferredMetadataLanguage, IHasMetascore
+ public class Trailer : Video, IHasCriticRating, IHasSoundtracks, IHasBudget, IHasTrailers, IHasKeywords, IHasTaglines, IHasPreferredMetadataLanguage, IHasMetascore, IHasLookupInfo<TrailerInfo>
{
public List<Guid> SoundtrackIds { get; set; }
@@ -27,7 +28,6 @@ namespace MediaBrowser.Controller.Entities
Taglines = new List<string>();
SoundtrackIds = new List<Guid>();
LocalTrailerIds = new List<Guid>();
- Tags = new List<string>();
Keywords = new List<string>();
}
@@ -40,12 +40,6 @@ namespace MediaBrowser.Controller.Entities
public List<string> Keywords { get; set; }
/// <summary>
- /// Gets or sets the tags.
- /// </summary>
- /// <value>The tags.</value>
- public List<string> Tags { get; set; }
-
- /// <summary>
/// Gets or sets the taglines.
/// </summary>
/// <value>The taglines.</value>
@@ -105,5 +99,10 @@ namespace MediaBrowser.Controller.Entities
{
return config.BlockUnratedTrailers;
}
+
+ public TrailerInfo GetLookupInfo()
+ {
+ return GetItemLookupInfo<TrailerInfo>();
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs
index 5feb000af..66ef8c7dc 100644
--- a/MediaBrowser.Controller/Entities/User.cs
+++ b/MediaBrowser.Controller/Entities/User.cs
@@ -73,6 +73,31 @@ namespace MediaBrowser.Controller.Entities
}
/// <summary>
+ /// Returns the folder containing the item.
+ /// If the item is a folder, it returns the folder itself
+ /// </summary>
+ /// <value>The containing folder path.</value>
+ public override string ContainingFolderPath
+ {
+ get
+ {
+ return Path;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance is owned item.
+ /// </summary>
+ /// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value>
+ public override bool IsOwnedItem
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ /// <summary>
/// The _root folder
/// </summary>
private UserRootFolder _rootFolder;
@@ -215,12 +240,18 @@ namespace MediaBrowser.Controller.Entities
return RefreshMetadata(new MetadataRefreshOptions
{
- ForceSave = true,
- ReplaceAllMetadata = true
+ ReplaceAllMetadata = true,
+ ImageRefreshMode = ImageRefreshMode.FullRefresh,
+ MetadataRefreshMode = MetadataRefreshMode.FullRefresh
}, CancellationToken.None);
}
+ public override Task UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken)
+ {
+ return UserManager.UpdateUser(this);
+ }
+
/// <summary>
/// Gets the path to the user's configuration directory
/// </summary>
diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs
index 8fe5f43f1..dc3d4c384 100644
--- a/MediaBrowser.Controller/Entities/UserRootFolder.cs
+++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs
@@ -1,4 +1,6 @@
-using System.Collections.Generic;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
+using System.Collections.Generic;
using System.Linq;
namespace MediaBrowser.Controller.Entities
@@ -13,9 +15,22 @@ namespace MediaBrowser.Controller.Entities
/// Get the children of this folder from the actual file system
/// </summary>
/// <returns>IEnumerable{BaseItem}.</returns>
- protected override IEnumerable<BaseItem> GetNonCachedChildren()
+ protected override IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
{
- return base.GetNonCachedChildren().Concat(LibraryManager.RootFolder.VirtualChildren);
+ return base.GetNonCachedChildren(directoryService).Concat(LibraryManager.RootFolder.VirtualChildren);
+ }
+
+ public override ItemUpdateType BeforeMetadataRefresh()
+ {
+ var updateType = base.BeforeMetadataRefresh();
+
+ if (string.Equals("default", Name, System.StringComparison.OrdinalIgnoreCase))
+ {
+ Name = "Default Media Library";
+ updateType = updateType | ItemUpdateType.MetadataEdit;
+ }
+
+ return updateType;
}
}
}
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index de78068b3..e778b38bd 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -16,7 +16,7 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// Class Video
/// </summary>
- public class Video : BaseItem, IHasMediaStreams, IHasAspectRatio
+ public class Video : BaseItem, IHasMediaStreams, IHasAspectRatio, IHasTags
{
public bool IsMultiPart { get; set; }
@@ -26,15 +26,29 @@ namespace MediaBrowser.Controller.Entities
{
PlayableStreamFileNames = new List<string>();
AdditionalPartIds = new List<Guid>();
+ Tags = new List<string>();
+ SubtitleFiles = new List<string>();
}
/// <summary>
+ /// Gets or sets the subtitle paths.
+ /// </summary>
+ /// <value>The subtitle paths.</value>
+ public List<string> SubtitleFiles { get; set; }
+
+ /// <summary>
/// Gets or sets a value indicating whether this instance has subtitles.
/// </summary>
/// <value><c>true</c> if this instance has subtitles; otherwise, <c>false</c>.</value>
public bool HasSubtitles { 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>
/// <value>The video bit rate.</value>
@@ -149,9 +163,9 @@ namespace MediaBrowser.Controller.Entities
}
}
- protected override async Task BeforeRefreshMetadata(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
+ protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
- await base.BeforeRefreshMetadata(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
+ var hasChanges = await base.RefreshedOwnedItems(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
// Must have a parent to have additional parts
// In other words, it must be part of the Parent/Child tree
@@ -162,9 +176,11 @@ namespace MediaBrowser.Controller.Entities
if (additionalPartsChanged)
{
- options.ForceSave = true;
+ hasChanges = true;
}
}
+
+ return hasChanges;
}
/// <summary>
@@ -174,7 +190,7 @@ namespace MediaBrowser.Controller.Entities
/// <param name="fileSystemChildren">The file system children.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{System.Boolean}.</returns>
- private async Task<bool> RefreshAdditionalParts(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
+ private async Task<bool> RefreshAdditionalParts(MetadataRefreshOptions options, IEnumerable<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
var newItems = LoadAdditionalParts(fileSystemChildren).ToList();
@@ -238,7 +254,8 @@ namespace MediaBrowser.Controller.Entities
return video;
- }).ToList();
+ // Sort them so that the list can be easily compared for changes
+ }).OrderBy(i => i.Path).ToList();
}
public override IEnumerable<string> GetDeletePaths()
diff --git a/MediaBrowser.Controller/Entities/Year.cs b/MediaBrowser.Controller/Entities/Year.cs
index cd50a1c60..c6ca028ae 100644
--- a/MediaBrowser.Controller/Entities/Year.cs
+++ b/MediaBrowser.Controller/Entities/Year.cs
@@ -26,5 +26,30 @@ namespace MediaBrowser.Controller.Entities
{
return "Year-" + Name;
}
+
+ /// <summary>
+ /// Returns the folder containing the item.
+ /// If the item is a folder, it returns the folder itself
+ /// </summary>
+ /// <value>The containing folder path.</value>
+ public override string ContainingFolderPath
+ {
+ get
+ {
+ return Path;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance is owned item.
+ /// </summary>
+ /// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value>
+ public override bool IsOwnedItem
+ {
+ get
+ {
+ return false;
+ }
+ }
}
}
diff --git a/MediaBrowser.Controller/IO/FileData.cs b/MediaBrowser.Controller/IO/FileData.cs
index 270afd89a..4ee8a810b 100644
--- a/MediaBrowser.Controller/IO/FileData.cs
+++ b/MediaBrowser.Controller/IO/FileData.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
@@ -15,16 +16,16 @@ namespace MediaBrowser.Controller.IO
/// <summary>
/// Gets the filtered file system entries.
/// </summary>
+ /// <param name="directoryService">The directory service.</param>
/// <param name="path">The path.</param>
/// <param name="fileSystem">The file system.</param>
/// <param name="logger">The logger.</param>
/// <param name="args">The args.</param>
- /// <param name="searchPattern">The search pattern.</param>
/// <param name="flattenFolderDepth">The flatten folder depth.</param>
/// <param name="resolveShortcuts">if set to <c>true</c> [resolve shortcuts].</param>
/// <returns>Dictionary{System.StringFileSystemInfo}.</returns>
/// <exception cref="System.ArgumentNullException">path</exception>
- public static Dictionary<string, FileSystemInfo> GetFilteredFileSystemEntries(string path, IFileSystem fileSystem, ILogger logger, ItemResolveArgs args, string searchPattern = "*", int flattenFolderDepth = 0, bool resolveShortcuts = true)
+ public static Dictionary<string, FileSystemInfo> GetFilteredFileSystemEntries(IDirectoryService directoryService, string path, IFileSystem fileSystem, ILogger logger, ItemResolveArgs args, int flattenFolderDepth = 0, bool resolveShortcuts = true)
{
if (string.IsNullOrEmpty(path))
{
@@ -35,7 +36,7 @@ namespace MediaBrowser.Controller.IO
throw new ArgumentNullException("args");
}
- var entries = new DirectoryInfo(path).EnumerateFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly);
+ var entries = directoryService.GetFileSystemEntries(path);
if (!resolveShortcuts && flattenFolderDepth == 0)
{
@@ -79,7 +80,7 @@ namespace MediaBrowser.Controller.IO
}
else if (flattenFolderDepth > 0 && isDirectory)
{
- foreach (var child in GetFilteredFileSystemEntries(fullName, fileSystem, logger, args, flattenFolderDepth: flattenFolderDepth - 1, resolveShortcuts: resolveShortcuts))
+ foreach (var child in GetFilteredFileSystemEntries(directoryService, fullName, fileSystem, logger, args, flattenFolderDepth: flattenFolderDepth - 1, resolveShortcuts: resolveShortcuts))
{
dict[child.Key] = child.Value;
}
diff --git a/MediaBrowser.Controller/IServerApplicationPaths.cs b/MediaBrowser.Controller/IServerApplicationPaths.cs
index e163ac31d..05f681597 100644
--- a/MediaBrowser.Controller/IServerApplicationPaths.cs
+++ b/MediaBrowser.Controller/IServerApplicationPaths.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Common.Configuration;
+using System;
+using MediaBrowser.Common.Configuration;
namespace MediaBrowser.Controller
{
@@ -101,9 +102,16 @@ namespace MediaBrowser.Controller
string TranscodingTempPath { get; }
/// <summary>
- /// Gets the downloaded images data path.
+ /// Gets the internal metadata path.
/// </summary>
- /// <value>The downloaded images data path.</value>
- string DownloadedImagesDataPath { get; }
+ /// <value>The internal metadata path.</value>
+ string InternalMetadataPath { get; }
+
+ /// <summary>
+ /// Gets the internal metadata path.
+ /// </summary>
+ /// <param name="id">The identifier.</param>
+ /// <returns>System.String.</returns>
+ string GetInternalMetadataPath(Guid id);
}
} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index d8ba019db..7c803e651 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -162,17 +162,13 @@ namespace MediaBrowser.Controller.Library
/// <param name="resolvers">The resolvers.</param>
/// <param name="introProviders">The intro providers.</param>
/// <param name="itemComparers">The item comparers.</param>
- /// <param name="prescanTasks">The prescan tasks.</param>
/// <param name="postscanTasks">The postscan tasks.</param>
- /// <param name="peoplePrescanTasks">The people prescan tasks.</param>
void AddParts(IEnumerable<IResolverIgnoreRule> rules,
IEnumerable<IVirtualFolderCreator> pluginFolders,
IEnumerable<IItemResolver> resolvers,
IEnumerable<IIntroProvider> introProviders,
IEnumerable<IBaseItemComparer> itemComparers,
- IEnumerable<ILibraryPrescanTask> prescanTasks,
- IEnumerable<ILibraryPostScanTask> postscanTasks,
- IEnumerable<IPeoplePrescanTask> peoplePrescanTasks);
+ IEnumerable<ILibraryPostScanTask> postscanTasks);
/// <summary>
/// Sorts the specified items.
diff --git a/MediaBrowser.Controller/Library/ILibraryPrescanTask.cs b/MediaBrowser.Controller/Library/ILibraryPrescanTask.cs
deleted file mode 100644
index 6a48ba777..000000000
--- a/MediaBrowser.Controller/Library/ILibraryPrescanTask.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Library
-{
- /// <summary>
- /// An interface for tasks that run prior to the media library scan
- /// </summary>
- public interface ILibraryPrescanTask
- {
- /// <summary>
- /// Runs the specified progress.
- /// </summary>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task Run(IProgress<double> progress, CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.Controller/Library/IMetadataSaver.cs b/MediaBrowser.Controller/Library/IMetadataSaver.cs
index 75d318057..cfee9d206 100644
--- a/MediaBrowser.Controller/Library/IMetadataSaver.cs
+++ b/MediaBrowser.Controller/Library/IMetadataSaver.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Providers;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
using System.Threading;
namespace MediaBrowser.Controller.Library
diff --git a/MediaBrowser.Controller/Library/IPeoplePrescanTask.cs b/MediaBrowser.Controller/Library/IPeoplePrescanTask.cs
deleted file mode 100644
index 04d179bae..000000000
--- a/MediaBrowser.Controller/Library/IPeoplePrescanTask.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Controller.Library
-{
- /// <summary>
- /// Interface IPeoplePrescanTask
- /// </summary>
- public interface IPeoplePrescanTask
- {
- /// <summary>
- /// Runs the specified progress.
- /// </summary>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task Run(IProgress<double> progress, CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs
index 5d6d850f0..d84e7aa8c 100644
--- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs
+++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs
@@ -97,18 +97,6 @@ namespace MediaBrowser.Controller.Library
}
/// <summary>
- /// Gets a value indicating whether this instance is system file.
- /// </summary>
- /// <value><c>true</c> if this instance is system file; otherwise, <c>false</c>.</value>
- public bool IsSystemFile
- {
- get
- {
- return (FileInfo.Attributes & FileAttributes.System) == FileAttributes.System;
- }
- }
-
- /// <summary>
/// Gets a value indicating whether this instance is vf.
/// </summary>
/// <value><c>true</c> if this instance is vf; otherwise, <c>false</c>.</value>
@@ -238,22 +226,6 @@ namespace MediaBrowser.Controller.Library
}
/// <summary>
- /// Gets the meta file by path.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>FileSystemInfo.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- public FileSystemInfo GetMetaFileByPath(string path)
- {
- if (string.IsNullOrEmpty(path))
- {
- throw new ArgumentNullException();
- }
-
- return GetFileSystemEntryByPath(path);
- }
-
- /// <summary>
/// Gets the name of the meta file by.
/// </summary>
/// <param name="name">The name.</param>
diff --git a/MediaBrowser.Controller/Library/ItemUpdateType.cs b/MediaBrowser.Controller/Library/ItemUpdateType.cs
index 31a00d7b4..cf6263356 100644
--- a/MediaBrowser.Controller/Library/ItemUpdateType.cs
+++ b/MediaBrowser.Controller/Library/ItemUpdateType.cs
@@ -5,7 +5,7 @@ namespace MediaBrowser.Controller.Library
[Flags]
public enum ItemUpdateType
{
- Unspecified = 1,
+ None = 1,
MetadataImport = 2,
ImageUpdate = 4,
MetadataDownload = 8,
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
index 93de9d5c3..a4a85ec04 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
@@ -25,6 +25,18 @@ namespace MediaBrowser.Controller.LiveTv
public string ServiceName { get; set; }
+ /// <summary>
+ /// Gets a value indicating whether this instance is owned item.
+ /// </summary>
+ /// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value>
+ public override bool IsOwnedItem
+ {
+ get
+ {
+ return false;
+ }
+ }
+
public override string MediaType
{
get
@@ -55,5 +67,13 @@ namespace MediaBrowser.Controller.LiveTv
{
return false;
}
+
+ public override bool SupportsLocalMetadata
+ {
+ get
+ {
+ return false;
+ }
+ }
}
}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
index f37e94714..583b90fd4 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
@@ -26,6 +26,31 @@ namespace MediaBrowser.Controller.LiveTv
public List<ItemByNameCounts> UserItemCountList { get; set; }
/// <summary>
+ /// Returns the folder containing the item.
+ /// If the item is a folder, it returns the folder itself
+ /// </summary>
+ /// <value>The containing folder path.</value>
+ public override string ContainingFolderPath
+ {
+ get
+ {
+ return Path;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance is owned item.
+ /// </summary>
+ /// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value>
+ public override bool IsOwnedItem
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ /// <summary>
/// Gets or sets the number.
/// </summary>
/// <value>The number.</value>
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
index 6a00607e4..e7a501479 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
@@ -1,6 +1,9 @@
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Model.LiveTv;
using System;
+using System.Threading;
+using System.Threading.Tasks;
namespace MediaBrowser.Controller.LiveTv
{
@@ -127,6 +130,31 @@ namespace MediaBrowser.Controller.LiveTv
/// <value><c>true</c> if this instance is premiere; otherwise, <c>false</c>.</value>
public bool IsPremiere { get; set; }
+ /// <summary>
+ /// Returns the folder containing the item.
+ /// If the item is a folder, it returns the folder itself
+ /// </summary>
+ /// <value>The containing folder path.</value>
+ public override string ContainingFolderPath
+ {
+ get
+ {
+ return Path;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance is owned item.
+ /// </summary>
+ /// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value>
+ public override bool IsOwnedItem
+ {
+ get
+ {
+ return false;
+ }
+ }
+
public override string MediaType
{
get
@@ -159,5 +187,14 @@ namespace MediaBrowser.Controller.LiveTv
{
return "Program";
}
+
+ public override Task UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken)
+ {
+ DateLastSaved = DateTime.UtcNow;
+
+ // Avoid library manager and keep out of in-memory cache
+ // Not great that this class has to know about that, but we'll improve that later.
+ return ItemRepository.SaveItem(this, cancellationToken);
+ }
}
}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
index bc4ed5493..6e3644b4a 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
@@ -46,6 +46,18 @@ namespace MediaBrowser.Controller.LiveTv
}
}
+ /// <summary>
+ /// Gets a value indicating whether this instance is owned item.
+ /// </summary>
+ /// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value>
+ public override bool IsOwnedItem
+ {
+ get
+ {
+ return false;
+ }
+ }
+
public override string GetClientTypeName()
{
return "Recording";
@@ -55,5 +67,13 @@ namespace MediaBrowser.Controller.LiveTv
{
return false;
}
+
+ public override bool SupportsLocalMetadata
+ {
+ get
+ {
+ return false;
+ }
+ }
}
}
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index 8a11cc9a0..18ac01c8b 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -94,6 +94,7 @@
<Compile Include="Entities\IHasPreferredMetadataLanguage.cs" />
<Compile Include="Entities\IHasProductionLocations.cs" />
<Compile Include="Entities\IHasScreenshots.cs" />
+ <Compile Include="Entities\IHasSeries.cs" />
<Compile Include="Entities\IHasSoundtracks.cs" />
<Compile Include="Entities\IHasTaglines.cs" />
<Compile Include="Entities\IHasTags.cs" />
@@ -103,14 +104,14 @@
<Compile Include="Entities\IItemByName.cs" />
<Compile Include="Entities\ILibraryItem.cs" />
<Compile Include="Entities\ImageSourceInfo.cs" />
+ <Compile Include="Entities\IMetadataContainer.cs" />
+ <Compile Include="Entities\ItemImageInfo.cs" />
<Compile Include="Entities\LinkedChild.cs" />
<Compile Include="Entities\MusicVideo.cs" />
<Compile Include="Entities\IHasAwards.cs" />
<Compile Include="FileOrganization\IFileOrganizationService.cs" />
<Compile Include="Library\ILibraryPostScanTask.cs" />
- <Compile Include="Library\ILibraryPrescanTask.cs" />
<Compile Include="Library\IMetadataSaver.cs" />
- <Compile Include="Library\IPeoplePrescanTask.cs" />
<Compile Include="Library\ItemUpdateType.cs" />
<Compile Include="Library\IUserDataManager.cs" />
<Compile Include="Library\UserDataSaveEventArgs.cs" />
@@ -143,9 +144,10 @@
<Compile Include="Notifications\NotificationUpdateEventArgs.cs" />
<Compile Include="Persistence\IFileOrganizationRepository.cs" />
<Compile Include="Persistence\MediaStreamQuery.cs" />
+ <Compile Include="Providers\DirectoryService.cs" />
<Compile Include="Providers\ICustomMetadataProvider.cs" />
<Compile Include="Providers\IHasChangeMonitor.cs" />
- <Compile Include="Providers\IHasMetadata.cs" />
+ <Compile Include="Entities\IHasMetadata.cs" />
<Compile Include="Providers\IImageProvider.cs" />
<Compile Include="Providers\ILocalMetadataProvider.cs" />
<Compile Include="Providers\IProviderRepository.cs" />
@@ -154,7 +156,7 @@
<Compile Include="Providers\IMetadataProvider.cs" />
<Compile Include="Providers\IMetadataService.cs" />
<Compile Include="Providers\IRemoteMetadataProvider.cs" />
- <Compile Include="Providers\ItemId.cs" />
+ <Compile Include="Providers\ItemLookupInfo.cs" />
<Compile Include="Providers\MetadataRefreshOptions.cs" />
<Compile Include="Providers\NameParser.cs" />
<Compile Include="Providers\MetadataStatus.cs" />
diff --git a/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs b/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs
index 746157bb3..e3604eb0e 100644
--- a/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs
+++ b/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs
@@ -177,7 +177,14 @@ namespace MediaBrowser.Controller.MediaInfo
Directory.CreateDirectory(parentPath);
- await _encoder.ExtractImage(inputPath, type, false, video.Video3DFormat, time, path, cancellationToken).ConfigureAwait(false);
+ using (var stream = await _encoder.ExtractImage(inputPath, type, false, video.Video3DFormat, time, cancellationToken).ConfigureAwait(false))
+ {
+ using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true))
+ {
+ await stream.CopyToAsync(fileStream).ConfigureAwait(false);
+ }
+ }
+
chapter.ImagePath = path;
changesMade = true;
}
diff --git a/MediaBrowser.Controller/MediaInfo/IMediaEncoder.cs b/MediaBrowser.Controller/MediaInfo/IMediaEncoder.cs
index e77cd14d1..d8cad48b3 100644
--- a/MediaBrowser.Controller/MediaInfo/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaInfo/IMediaEncoder.cs
@@ -31,19 +31,6 @@ namespace MediaBrowser.Controller.MediaInfo
/// <param name="isAudio">if set to <c>true</c> [is audio].</param>
/// <param name="threedFormat">The threed format.</param>
/// <param name="offset">The offset.</param>
- /// <param name="outputPath">The output path.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task ExtractImage(string[] inputFiles, InputType type, bool isAudio, Video3DFormat? threedFormat, TimeSpan? offset, string outputPath, CancellationToken cancellationToken);
-
- /// <summary>
- /// Extracts the image.
- /// </summary>
- /// <param name="inputFiles">The input files.</param>
- /// <param name="type">The type.</param>
- /// <param name="isAudio">if set to <c>true</c> [is audio].</param>
- /// <param name="threedFormat">The threed format.</param>
- /// <param name="offset">The offset.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{Stream}.</returns>
Task<Stream> ExtractImage(string[] inputFiles, InputType type, bool isAudio, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken);
diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs
index 5d3d9dbca..59b7cd8db 100644
--- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs
+++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs
@@ -247,7 +247,10 @@ namespace MediaBrowser.Controller.Providers
{
var val = reader.ReadElementContentAsString();
-
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ item.ForcedSortName = val;
+ }
break;
}
@@ -479,7 +482,7 @@ namespace MediaBrowser.Controller.Providers
case "Director":
{
- foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonType.Director }))
+ foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new Entities.PersonInfo { Name = v.Trim(), Type = PersonType.Director }))
{
if (string.IsNullOrWhiteSpace(p.Name))
{
@@ -491,7 +494,7 @@ namespace MediaBrowser.Controller.Providers
}
case "Writer":
{
- foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonType.Writer }))
+ foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new Entities.PersonInfo { Name = v.Trim(), Type = PersonType.Writer }))
{
if (string.IsNullOrWhiteSpace(p.Name))
{
@@ -516,7 +519,7 @@ namespace MediaBrowser.Controller.Providers
else
{
// Old-style piped string
- foreach (var p in SplitNames(actors).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonType.Actor }))
+ foreach (var p in SplitNames(actors).Select(v => new Entities.PersonInfo { Name = v.Trim(), Type = PersonType.Actor }))
{
if (string.IsNullOrWhiteSpace(p.Name))
{
@@ -530,7 +533,7 @@ namespace MediaBrowser.Controller.Providers
case "GuestStars":
{
- foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonType.GuestStar }))
+ foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new Entities.PersonInfo { Name = v.Trim(), Type = PersonType.GuestStar }))
{
if (string.IsNullOrWhiteSpace(p.Name))
{
@@ -680,12 +683,30 @@ namespace MediaBrowser.Controller.Providers
}
break;
}
- case "MusicbrainzId":
+ case "MusicBrainzAlbumId":
{
var mbz = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(mbz))
{
- item.SetProviderId(MetadataProviders.Musicbrainz, mbz);
+ item.SetProviderId(MetadataProviders.MusicBrainzAlbum, mbz);
+ }
+ break;
+ }
+ case "MusicBrainzAlbumArtistId":
+ {
+ var mbz = reader.ReadElementContentAsString();
+ if (!string.IsNullOrWhiteSpace(mbz))
+ {
+ item.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, mbz);
+ }
+ break;
+ }
+ case "MusicBrainzArtistId":
+ {
+ var mbz = reader.ReadElementContentAsString();
+ if (!string.IsNullOrWhiteSpace(mbz))
+ {
+ item.SetProviderId(MetadataProviders.MusicBrainzArtist, mbz);
}
break;
}
@@ -698,6 +719,33 @@ namespace MediaBrowser.Controller.Providers
}
break;
}
+ case "TvRageId":
+ {
+ var id = reader.ReadElementContentAsString();
+ if (!string.IsNullOrWhiteSpace(id))
+ {
+ item.SetProviderId(MetadataProviders.TvRage, id);
+ }
+ break;
+ }
+ case "AudioDbArtistId":
+ {
+ var id = reader.ReadElementContentAsString();
+ if (!string.IsNullOrWhiteSpace(id))
+ {
+ item.SetProviderId(MetadataProviders.AudioDbArtist, id);
+ }
+ break;
+ }
+ case "AudioDbAlbumId":
+ {
+ var id = reader.ReadElementContentAsString();
+ if (!string.IsNullOrWhiteSpace(id))
+ {
+ item.SetProviderId(MetadataProviders.AudioDbAlbum, id);
+ }
+ break;
+ }
case "RottenTomatoesId":
var rtId = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(rtId))
@@ -1195,7 +1243,7 @@ namespace MediaBrowser.Controller.Providers
/// </summary>
/// <param name="reader">The reader.</param>
/// <returns>IEnumerable{PersonInfo}.</returns>
- private IEnumerable<PersonInfo> GetPersonsFromXmlNode(XmlReader reader)
+ private IEnumerable<Entities.PersonInfo> GetPersonsFromXmlNode(XmlReader reader)
{
var name = string.Empty;
var type = "Actor"; // If type is not specified assume actor
@@ -1257,7 +1305,7 @@ namespace MediaBrowser.Controller.Providers
}
}
- var personInfo = new PersonInfo
+ var personInfo = new Entities.PersonInfo
{
Name = name.Trim(),
Role = role,
diff --git a/MediaBrowser.Controller/Providers/BaseMetadataProvider.cs b/MediaBrowser.Controller/Providers/BaseMetadataProvider.cs
index 6818fa52b..f8580244a 100644
--- a/MediaBrowser.Controller/Providers/BaseMetadataProvider.cs
+++ b/MediaBrowser.Controller/Providers/BaseMetadataProvider.cs
@@ -40,8 +40,6 @@ namespace MediaBrowser.Controller.Providers
protected static readonly Task<bool> FalseTaskResult = Task.FromResult(false);
- protected static readonly SemaphoreSlim XmlParsingResourcePool = new SemaphoreSlim(4, 4);
-
/// <summary>
/// Supportses the specified item.
/// </summary>
diff --git a/MediaBrowser.Controller/Providers/BaseProviderInfo.cs b/MediaBrowser.Controller/Providers/BaseProviderInfo.cs
index 829dd34c8..3a33924f0 100644
--- a/MediaBrowser.Controller/Providers/BaseProviderInfo.cs
+++ b/MediaBrowser.Controller/Providers/BaseProviderInfo.cs
@@ -38,14 +38,14 @@ namespace MediaBrowser.Controller.Providers
/// <summary>
/// The success
/// </summary>
- Success,
- /// <summary>
- /// The failure
- /// </summary>
- Failure,
+ Success = 0,
/// <summary>
/// The completed with errors
/// </summary>
- CompletedWithErrors
- }
+ CompletedWithErrors = 1,
+ /// <summary>
+ /// The failure
+ /// </summary>
+ Failure = 2
+ }
}
diff --git a/MediaBrowser.Controller/Providers/DirectoryService.cs b/MediaBrowser.Controller/Providers/DirectoryService.cs
new file mode 100644
index 000000000..e17ae76ff
--- /dev/null
+++ b/MediaBrowser.Controller/Providers/DirectoryService.cs
@@ -0,0 +1,79 @@
+using MediaBrowser.Model.Logging;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace MediaBrowser.Controller.Providers
+{
+ public interface IDirectoryService
+ {
+ List<FileSystemInfo> GetFileSystemEntries(string path);
+ IEnumerable<FileInfo> GetFiles(string path);
+ IEnumerable<DirectoryInfo> GetDirectories(string path);
+ FileInfo GetFile(string path);
+ DirectoryInfo GetDirectory(string path);
+ }
+
+ public class DirectoryService : IDirectoryService
+ {
+ private readonly ILogger _logger;
+
+ private readonly Dictionary<string, List<FileSystemInfo>> _cache = new Dictionary<string, List<FileSystemInfo>>(StringComparer.OrdinalIgnoreCase);
+
+ public DirectoryService(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ public List<FileSystemInfo> GetFileSystemEntries(string path)
+ {
+ List<FileSystemInfo> entries;
+
+ if (!_cache.TryGetValue(path, out entries))
+ {
+ //_logger.Debug("Getting files for " + path);
+
+ try
+ {
+ entries = new DirectoryInfo(path).EnumerateFileSystemInfos("*", SearchOption.TopDirectoryOnly).ToList();
+ }
+ catch (DirectoryNotFoundException)
+ {
+ entries = new List<FileSystemInfo>();
+ }
+
+ _cache.Add(path, entries);
+ }
+
+ return entries;
+ }
+
+ public IEnumerable<FileInfo> GetFiles(string path)
+ {
+ return GetFileSystemEntries(path).OfType<FileInfo>();
+ }
+
+ public IEnumerable<DirectoryInfo> GetDirectories(string path)
+ {
+ return GetFileSystemEntries(path).OfType<DirectoryInfo>();
+ }
+
+ public FileInfo GetFile(string path)
+ {
+ var directory = Path.GetDirectoryName(path);
+ var filename = Path.GetFileName(path);
+
+ return GetFiles(directory).FirstOrDefault(i => string.Equals(i.Name, filename, StringComparison.OrdinalIgnoreCase));
+ }
+
+
+ public DirectoryInfo GetDirectory(string path)
+ {
+ var directory = Path.GetDirectoryName(path);
+ var name = Path.GetFileName(path);
+
+ return GetDirectories(directory).FirstOrDefault(i => string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase));
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs b/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs
index de75c62e9..c98810cbc 100644
--- a/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs
+++ b/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
using System.Threading;
using System.Threading.Tasks;
@@ -11,6 +12,6 @@ namespace MediaBrowser.Controller.Providers
public interface ICustomMetadataProvider<TItemType> : IMetadataProvider<TItemType>, ICustomMetadataProvider
where TItemType : IHasMetadata
{
- Task<ItemUpdateType> FetchAsync(TItemType item, CancellationToken cancellationToken);
+ Task<ItemUpdateType> FetchAsync(TItemType item, IDirectoryService directoryService, CancellationToken cancellationToken);
}
}
diff --git a/MediaBrowser.Controller/Providers/IHasChangeMonitor.cs b/MediaBrowser.Controller/Providers/IHasChangeMonitor.cs
index 41313da3d..aa0b0e3c9 100644
--- a/MediaBrowser.Controller/Providers/IHasChangeMonitor.cs
+++ b/MediaBrowser.Controller/Providers/IHasChangeMonitor.cs
@@ -1,4 +1,5 @@
-using System;
+using MediaBrowser.Controller.Entities;
+using System;
namespace MediaBrowser.Controller.Providers
{
@@ -8,8 +9,9 @@ namespace MediaBrowser.Controller.Providers
/// Determines whether the specified item has changed.
/// </summary>
/// <param name="item">The item.</param>
+ /// <param name="directoryService">The directory service.</param>
/// <param name="date">The date.</param>
/// <returns><c>true</c> if the specified item has changed; otherwise, <c>false</c>.</returns>
- bool HasChanged(IHasMetadata item, DateTime date);
+ bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date);
}
}
diff --git a/MediaBrowser.Controller/Providers/ILocalImageProvider.cs b/MediaBrowser.Controller/Providers/ILocalImageProvider.cs
index ed7cdc8b2..ec24e1d60 100644
--- a/MediaBrowser.Controller/Providers/ILocalImageProvider.cs
+++ b/MediaBrowser.Controller/Providers/ILocalImageProvider.cs
@@ -1,6 +1,7 @@
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Entities;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
@@ -15,14 +16,14 @@ namespace MediaBrowser.Controller.Providers
{
}
- public interface IImageFileProvider : ILocalImageProvider
+ public interface ILocalImageFileProvider : ILocalImageProvider
{
- List<LocalImageInfo> GetImages(IHasImages item);
+ List<LocalImageInfo> GetImages(IHasImages item, IDirectoryService directoryService);
}
public class LocalImageInfo
{
- public string Path { get; set; }
+ public FileInfo FileInfo { get; set; }
public ImageType Type { get; set; }
}
@@ -60,7 +61,22 @@ namespace MediaBrowser.Controller.Providers
public void SetFormatFromMimeType(string mimeType)
{
-
+ if (mimeType.EndsWith("gif", StringComparison.OrdinalIgnoreCase))
+ {
+ Format = ImageFormat.Gif;
+ }
+ else if (mimeType.EndsWith("bmp", StringComparison.OrdinalIgnoreCase))
+ {
+ Format = ImageFormat.Bmp;
+ }
+ else if (mimeType.EndsWith("png", StringComparison.OrdinalIgnoreCase))
+ {
+ Format = ImageFormat.Png;
+ }
+ else
+ {
+ Format = ImageFormat.Jpg;
+ }
}
}
}
diff --git a/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs b/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs
index 91ef22b2c..a7c1e6e1b 100644
--- a/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs
+++ b/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs
@@ -1,4 +1,6 @@
-using System.Threading;
+using MediaBrowser.Controller.Entities;
+using System.Collections.Generic;
+using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Providers
@@ -16,7 +18,7 @@ namespace MediaBrowser.Controller.Providers
/// <param name="info">The information.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{MetadataResult{`0}}.</returns>
- Task<MetadataResult<TItemType>> GetMetadata(ItemInfo info, CancellationToken cancellationToken);
+ Task<LocalMetadataResult<TItemType>> GetMetadata(ItemInfo info, CancellationToken cancellationToken);
}
public class ItemInfo
@@ -25,4 +27,18 @@ namespace MediaBrowser.Controller.Providers
public bool IsInMixedFolder { get; set; }
}
+
+ public class LocalMetadataResult<T>
+ where T : IHasMetadata
+ {
+ public bool HasMetadata { get; set; }
+ public T Item { get; set; }
+
+ public List<LocalImageInfo> Images { get; set; }
+
+ public LocalMetadataResult()
+ {
+ Images = new List<LocalImageInfo>();
+ }
+ }
}
diff --git a/MediaBrowser.Controller/Providers/IMetadataProvider.cs b/MediaBrowser.Controller/Providers/IMetadataProvider.cs
index 910c88e53..70bc06059 100644
--- a/MediaBrowser.Controller/Providers/IMetadataProvider.cs
+++ b/MediaBrowser.Controller/Providers/IMetadataProvider.cs
@@ -1,4 +1,5 @@
using System;
+using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Controller.Providers
{
diff --git a/MediaBrowser.Controller/Providers/IMetadataService.cs b/MediaBrowser.Controller/Providers/IMetadataService.cs
index c4314c15f..786a7147c 100644
--- a/MediaBrowser.Controller/Providers/IMetadataService.cs
+++ b/MediaBrowser.Controller/Providers/IMetadataService.cs
@@ -1,5 +1,6 @@
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Controller.Providers
{
diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs
index 369b58256..691a5add6 100644
--- a/MediaBrowser.Controller/Providers/IProviderManager.cs
+++ b/MediaBrowser.Controller/Providers/IProviderManager.cs
@@ -62,18 +62,17 @@ namespace MediaBrowser.Controller.Providers
/// Gets the available remote images.
/// </summary>
/// <param name="item">The item.</param>
+ /// <param name="query">The query.</param>
/// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="providerName">Name of the provider.</param>
- /// <param name="type">The type.</param>
/// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns>
- Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(IHasImages item, CancellationToken cancellationToken, string providerName = null, ImageType? type = null);
+ Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(IHasImages item, RemoteImageQuery query, CancellationToken cancellationToken);
/// <summary>
/// Gets the image providers.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>IEnumerable{ImageProviderInfo}.</returns>
- IEnumerable<ImageProviderInfo> GetImageProviderInfo(IHasImages item);
+ IEnumerable<ImageProviderInfo> GetRemoteImageProviderInfo(IHasImages item);
/// <summary>
/// Gets all metadata plugins.
@@ -88,5 +87,12 @@ namespace MediaBrowser.Controller.Providers
/// <param name="updateType">Type of the update.</param>
/// <returns>Task.</returns>
Task SaveMetadata(IHasMetadata item, ItemUpdateType updateType);
+
+ /// <summary>
+ /// Gets the metadata options.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <returns>MetadataOptions.</returns>
+ MetadataOptions GetMetadataOptions(IHasImages item);
}
} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Providers/IProviderRepository.cs b/MediaBrowser.Controller/Providers/IProviderRepository.cs
index 1c0ad2cd7..3cd2c3f31 100644
--- a/MediaBrowser.Controller/Providers/IProviderRepository.cs
+++ b/MediaBrowser.Controller/Providers/IProviderRepository.cs
@@ -9,22 +9,6 @@ namespace MediaBrowser.Controller.Providers
public interface IProviderRepository : IRepository
{
/// <summary>
- /// Gets the provider history.
- /// </summary>
- /// <param name="itemId">The item identifier.</param>
- /// <returns>IEnumerable{BaseProviderInfo}.</returns>
- IEnumerable<BaseProviderInfo> GetProviderHistory(Guid itemId);
-
- /// <summary>
- /// Saves the provider history.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="history">The history.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task SaveProviderHistory(Guid id, IEnumerable<BaseProviderInfo> history, CancellationToken cancellationToken);
-
- /// <summary>
/// Gets the metadata status.
/// </summary>
/// <param name="itemId">The item identifier.</param>
diff --git a/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs b/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs
index 6007a5af6..cbbd62557 100644
--- a/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs
+++ b/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs
@@ -1,5 +1,6 @@
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Controller.Providers
{
@@ -7,15 +8,10 @@ namespace MediaBrowser.Controller.Providers
{
}
- public interface IRemoteMetadataProvider<TItemType> : IMetadataProvider<TItemType>, IRemoteMetadataProvider
- where TItemType : IHasMetadata
+ public interface IRemoteMetadataProvider<TItemType, in TLookupInfoType> : IMetadataProvider<TItemType>, IRemoteMetadataProvider
+ where TItemType : IHasMetadata, IHasLookupInfo<TLookupInfoType>
+ where TLookupInfoType : ItemLookupInfo, new()
{
- /// <summary>
- /// Gets the metadata.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{MetadataResult{`0}}.</returns>
- Task<MetadataResult<TItemType>> GetMetadata(ItemId id, CancellationToken cancellationToken);
+ Task<MetadataResult<TItemType>> GetMetadata(TLookupInfoType info, CancellationToken cancellationToken);
}
}
diff --git a/MediaBrowser.Controller/Providers/ItemId.cs b/MediaBrowser.Controller/Providers/ItemLookupInfo.cs
index 9be6b783c..b43654005 100644
--- a/MediaBrowser.Controller/Providers/ItemId.cs
+++ b/MediaBrowser.Controller/Providers/ItemLookupInfo.cs
@@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace MediaBrowser.Controller.Providers
{
- public class ItemId : IHasProviderIds
+ public class ItemLookupInfo : IHasProviderIds
{
/// <summary>
/// Gets or sets the name.
@@ -34,13 +34,29 @@ namespace MediaBrowser.Controller.Providers
public int? IndexNumber { get; set; }
public int? ParentIndexNumber { get; set; }
- public ItemId()
+ public ItemLookupInfo()
{
ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
}
- public class AlbumId : ItemId
+ public interface IHasLookupInfo<out TLookupInfoType>
+ where TLookupInfoType : ItemLookupInfo, new()
+ {
+ TLookupInfoType GetLookupInfo();
+ }
+
+ public class ArtistInfo : ItemLookupInfo
+ {
+ public List<SongInfo> SongInfos { get; set; }
+
+ public ArtistInfo()
+ {
+ SongInfos = new List<SongInfo>();
+ }
+ }
+
+ public class AlbumInfo : ItemLookupInfo
{
/// <summary>
/// Gets or sets the album artist.
@@ -53,14 +69,16 @@ namespace MediaBrowser.Controller.Providers
/// </summary>
/// <value>The artist provider ids.</value>
public Dictionary<string, string> ArtistProviderIds { get; set; }
+ public List<SongInfo> SongInfos { get; set; }
- public AlbumId()
+ public AlbumInfo()
{
ArtistProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ SongInfos = new List<SongInfo>();
}
}
- public class GameId : ItemId
+ public class GameInfo : ItemLookupInfo
{
/// <summary>
/// Gets or sets the game system.
@@ -69,7 +87,7 @@ namespace MediaBrowser.Controller.Providers
public string GameSystem { get; set; }
}
- public class GameSystemId : ItemId
+ public class GameSystemInfo : ItemLookupInfo
{
/// <summary>
/// Gets or sets the path.
@@ -78,15 +96,62 @@ namespace MediaBrowser.Controller.Providers
public string Path { get; set; }
}
- public class EpisodeId : ItemId
+ public class EpisodeInfo : ItemLookupInfo
{
public Dictionary<string, string> SeriesProviderIds { get; set; }
public int? IndexNumberEnd { get; set; }
- public EpisodeId()
+ public EpisodeInfo()
{
SeriesProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
}
+
+ public class SongInfo : ItemLookupInfo
+ {
+ public string AlbumArtist { get; set; }
+ public string Album { get; set; }
+ public List<string> Artists { get; set; }
+ }
+
+ public class SeriesInfo : ItemLookupInfo
+ {
+
+ }
+
+ public class PersonLookupInfo : ItemLookupInfo
+ {
+
+ }
+
+ public class MovieInfo : ItemLookupInfo
+ {
+
+ }
+
+ public class BoxSetInfo : ItemLookupInfo
+ {
+
+ }
+
+ public class MusicVideoInfo : ItemLookupInfo
+ {
+
+ }
+
+ public class TrailerInfo : ItemLookupInfo
+ {
+
+ }
+
+ public class BookInfo : ItemLookupInfo
+ {
+ public string SeriesName { get; set; }
+ }
+
+ public class SeasonInfo : ItemLookupInfo
+ {
+
+ }
}
diff --git a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
index 83f1a12d9..780aa6a56 100644
--- a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
+++ b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
@@ -21,6 +21,7 @@ namespace MediaBrowser.Controller.Providers
public class ImageRefreshOptions
{
public ImageRefreshMode ImageRefreshMode { get; set; }
+ public IDirectoryService DirectoryService { get; set; }
public ImageRefreshOptions()
{
diff --git a/MediaBrowser.Controller/Providers/MetadataStatus.cs b/MediaBrowser.Controller/Providers/MetadataStatus.cs
index 834d8ec35..1e84e5880 100644
--- a/MediaBrowser.Controller/Providers/MetadataStatus.cs
+++ b/MediaBrowser.Controller/Providers/MetadataStatus.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using MediaBrowser.Common.Extensions;
namespace MediaBrowser.Controller.Providers
{
@@ -14,6 +13,24 @@ namespace MediaBrowser.Controller.Providers
public Guid ItemId { get; set; }
/// <summary>
+ /// Gets or sets the name of the item.
+ /// </summary>
+ /// <value>The name of the item.</value>
+ public string ItemName { get; set; }
+
+ /// <summary>
+ /// Gets or sets the type of the item.
+ /// </summary>
+ /// <value>The type of the item.</value>
+ public string ItemType { get; set; }
+
+ /// <summary>
+ /// Gets or sets the name of the series.
+ /// </summary>
+ /// <value>The name of the series.</value>
+ public string SeriesName { get; set; }
+
+ /// <summary>
/// Gets or sets the date last metadata refresh.
/// </summary>
/// <value>The date last metadata refresh.</value>