aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller/Entities
diff options
context:
space:
mode:
authorLuke Pulverenti <luke.pulverenti@gmail.com>2016-05-15 13:14:02 -0400
committerLuke Pulverenti <luke.pulverenti@gmail.com>2016-05-15 13:14:02 -0400
commit1e299549a8d459ffa6931617bb3d281f696e6062 (patch)
tree78b0738ba42347521227b28ef5c4fe5bb36aade4 /MediaBrowser.Controller/Entities
parent780d5b914cc22bdf88477761e60c5de64b20504d (diff)
parent3dadeeae7b12be43ace187dede3d451e867b6c24 (diff)
Merge branch 'beta'
Diffstat (limited to 'MediaBrowser.Controller/Entities')
-rw-r--r--MediaBrowser.Controller/Entities/Audio/Audio.cs40
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs41
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicArtist.cs69
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicGenre.cs19
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs93
-rw-r--r--MediaBrowser.Controller/Entities/CollectionFolder.cs27
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs296
-rw-r--r--MediaBrowser.Controller/Entities/Game.cs7
-rw-r--r--MediaBrowser.Controller/Entities/GameGenre.cs19
-rw-r--r--MediaBrowser.Controller/Entities/GameSystem.cs13
-rw-r--r--MediaBrowser.Controller/Entities/Genre.cs19
-rw-r--r--MediaBrowser.Controller/Entities/IHasMetadata.cs2
-rw-r--r--MediaBrowser.Controller/Entities/IHasUserData.cs11
-rw-r--r--MediaBrowser.Controller/Entities/IItemByName.cs2
-rw-r--r--MediaBrowser.Controller/Entities/InternalItemsQuery.cs15
-rw-r--r--MediaBrowser.Controller/Entities/Movies/BoxSet.cs3
-rw-r--r--MediaBrowser.Controller/Entities/Movies/Movie.cs30
-rw-r--r--MediaBrowser.Controller/Entities/MusicVideo.cs15
-rw-r--r--MediaBrowser.Controller/Entities/Person.cs18
-rw-r--r--MediaBrowser.Controller/Entities/PhotoAlbum.cs9
-rw-r--r--MediaBrowser.Controller/Entities/Studio.cs18
-rw-r--r--MediaBrowser.Controller/Entities/TV/Episode.cs55
-rw-r--r--MediaBrowser.Controller/Entities/TV/Season.cs99
-rw-r--r--MediaBrowser.Controller/Entities/TV/Series.cs163
-rw-r--r--MediaBrowser.Controller/Entities/Trailer.cs22
-rw-r--r--MediaBrowser.Controller/Entities/UserItemData.cs4
-rw-r--r--MediaBrowser.Controller/Entities/UserRootFolder.cs5
-rw-r--r--MediaBrowser.Controller/Entities/UserViewBuilder.cs12
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs266
-rw-r--r--MediaBrowser.Controller/Entities/Year.cs27
30 files changed, 848 insertions, 571 deletions
diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs
index 929308ba0..c34a884ff 100644
--- a/MediaBrowser.Controller/Entities/Audio/Audio.cs
+++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs
@@ -26,7 +26,7 @@ namespace MediaBrowser.Controller.Entities.Audio
IArchivable
{
public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
-
+
public long? Size { get; set; }
public string Container { get; set; }
public int? TotalBitrate { get; set; }
@@ -40,12 +40,6 @@ namespace MediaBrowser.Controller.Entities.Audio
public List<string> AlbumArtists { get; set; }
- /// <summary>
- /// Gets or sets the album.
- /// </summary>
- /// <value>The album.</value>
- public string Album { get; set; }
-
[IgnoreDataMember]
public bool IsThemeMedia
{
@@ -150,12 +144,10 @@ namespace MediaBrowser.Controller.Entities.Audio
+ (IndexNumber != null ? IndexNumber.Value.ToString("0000 - ") : "") + Name;
}
- /// <summary>
- /// Gets the user data key.
- /// </summary>
- /// <returns>System.String.</returns>
- protected override string CreateUserDataKey()
+ public override List<string> GetUserDataKeys()
{
+ var list = base.GetUserDataKeys();
+
if (ConfigurationManager.Configuration.EnableStandaloneMusicKeys)
{
var songKey = IndexNumber.HasValue ? IndexNumber.Value.ToString("0000") : string.Empty;
@@ -165,7 +157,7 @@ namespace MediaBrowser.Controller.Entities.Audio
{
songKey = ParentIndexNumber.Value.ToString("0000") + "-" + songKey;
}
- songKey+= Name;
+ songKey += Name;
if (!string.IsNullOrWhiteSpace(Album))
{
@@ -178,25 +170,25 @@ namespace MediaBrowser.Controller.Entities.Audio
songKey = albumArtist + "-" + songKey;
}
- return songKey;
+ list.Insert(0, songKey);
}
-
- var parent = AlbumEntity;
-
- if (parent != null)
+ else
{
- var parentKey = parent.GetUserDataKey();
+ var parent = AlbumEntity;
- if (IndexNumber.HasValue)
+ if (parent != null && IndexNumber.HasValue)
{
- var songKey = (ParentIndexNumber != null ? ParentIndexNumber.Value.ToString("0000 - ") : "")
- + IndexNumber.Value.ToString("0000 - ");
+ list.InsertRange(0, parent.GetUserDataKeys().Select(i =>
+ {
+ var songKey = (ParentIndexNumber != null ? ParentIndexNumber.Value.ToString("0000 - ") : "")
+ + IndexNumber.Value.ToString("0000 - ");
- return parentKey + songKey;
+ return i + songKey;
+ }));
}
}
- return base.CreateUserDataKey();
+ return list;
}
public override UnratedItem GetBlockUnratedType()
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
index e6178c183..615276e83 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
@@ -49,6 +49,15 @@ namespace MediaBrowser.Controller.Entities.Audio
}
[IgnoreDataMember]
+ public override bool SupportsCumulativeRunTimeTicks
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ [IgnoreDataMember]
public List<string> AllArtists
{
get
@@ -96,36 +105,34 @@ namespace MediaBrowser.Controller.Entities.Audio
public List<string> Artists { get; set; }
- /// <summary>
- /// Gets the user data key.
- /// </summary>
- /// <returns>System.String.</returns>
- protected override string CreateUserDataKey()
+ public override List<string> GetUserDataKeys()
{
- var id = this.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
+ var list = base.GetUserDataKeys();
- if (!string.IsNullOrWhiteSpace(id))
+ if (ConfigurationManager.Configuration.EnableStandaloneMusicKeys)
{
- return "MusicAlbum-MusicBrainzReleaseGroup-" + id;
+ var albumArtist = AlbumArtist;
+ if (!string.IsNullOrWhiteSpace(albumArtist))
+ {
+ list.Insert(0, albumArtist + "-" + Name);
+ }
}
- id = this.GetProviderId(MetadataProviders.MusicBrainzAlbum);
+ var id = this.GetProviderId(MetadataProviders.MusicBrainzAlbum);
if (!string.IsNullOrWhiteSpace(id))
{
- return "MusicAlbum-Musicbrainz-" + id;
+ list.Insert(0, "MusicAlbum-Musicbrainz-" + id);
}
- if (ConfigurationManager.Configuration.EnableStandaloneMusicKeys)
+ id = this.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
+
+ if (!string.IsNullOrWhiteSpace(id))
{
- var albumArtist = AlbumArtist;
- if (!string.IsNullOrWhiteSpace(albumArtist))
- {
- return albumArtist + "-" + Name;
- }
+ list.Insert(0, "MusicAlbum-MusicBrainzReleaseGroup-" + id);
}
- return base.CreateUserDataKey();
+ return list;
}
protected override bool GetBlockUnratedValue(UserPolicy config)
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
index 02bcceada..fb8a24061 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
@@ -17,7 +17,12 @@ namespace MediaBrowser.Controller.Entities.Audio
/// </summary>
public class MusicArtist : Folder, IMetadataContainer, IItemByName, IHasMusicGenres, IHasDualAccess, IHasProductionLocations, IHasLookupInfo<ArtistInfo>
{
- public bool IsAccessedByName { get; set; }
+ [IgnoreDataMember]
+ public bool IsAccessedByName
+ {
+ get { return ParentId == Guid.Empty; }
+ }
+
public List<string> ProductionLocations { get; set; }
[IgnoreDataMember]
@@ -30,6 +35,15 @@ namespace MediaBrowser.Controller.Entities.Audio
}
[IgnoreDataMember]
+ public override bool SupportsCumulativeRunTimeTicks
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ [IgnoreDataMember]
public override bool SupportsAddingToPlaylist
{
get { return true; }
@@ -40,6 +54,40 @@ namespace MediaBrowser.Controller.Entities.Audio
return !IsAccessedByName;
}
+ public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query)
+ {
+ var itemByNameFilter = GetItemFilter();
+
+ if (query.User != null)
+ {
+ return query.User.RootFolder
+ .GetRecursiveChildren(query.User, i =>
+ {
+ if (query.IsFolder.HasValue)
+ {
+ if (query.IsFolder.Value != i.IsFolder)
+ {
+ return false;
+ }
+ }
+ return itemByNameFilter(i);
+ });
+ }
+
+ return LibraryManager.RootFolder
+ .GetRecursiveChildren(i =>
+ {
+ if (query.IsFolder.HasValue)
+ {
+ if (query.IsFolder.Value != i.IsFolder)
+ {
+ return false;
+ }
+ }
+ return itemByNameFilter(i);
+ });
+ }
+
protected override IEnumerable<BaseItem> ActualChildren
{
get
@@ -80,13 +128,12 @@ namespace MediaBrowser.Controller.Entities.Audio
ProductionLocations = new List<string>();
}
- /// <summary>
- /// Gets the user data key.
- /// </summary>
- /// <returns>System.String.</returns>
- protected override string CreateUserDataKey()
+ public override List<string> GetUserDataKeys()
{
- return GetUserDataKey(this);
+ var list = base.GetUserDataKeys();
+
+ list.InsertRange(0, GetUserDataKeys(this));
+ return list;
}
/// <summary>
@@ -121,16 +168,18 @@ namespace MediaBrowser.Controller.Entities.Audio
/// </summary>
/// <param name="item">The item.</param>
/// <returns>System.String.</returns>
- private static string GetUserDataKey(MusicArtist item)
+ private static List<string> GetUserDataKeys(MusicArtist item)
{
+ var list = new List<string>();
var id = item.GetProviderId(MetadataProviders.MusicBrainzArtist);
if (!string.IsNullOrEmpty(id))
{
- return "Artist-Musicbrainz-" + id;
+ list.Add("Artist-Musicbrainz-" + id);
}
- return "Artist-" + item.Name;
+ list.Add("Artist-" + item.Name);
+ return list;
}
protected override bool GetBlockUnratedValue(UserPolicy config)
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
index 45304d47e..77cf0cc49 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
@@ -10,13 +10,12 @@ namespace MediaBrowser.Controller.Entities.Audio
/// </summary>
public class MusicGenre : BaseItem, IItemByName
{
- /// <summary>
- /// Gets the user data key.
- /// </summary>
- /// <returns>System.String.</returns>
- protected override string CreateUserDataKey()
+ public override List<string> GetUserDataKeys()
{
- return "MusicGenre-" + Name;
+ var list = base.GetUserDataKeys();
+
+ list.Insert(0, "MusicGenre-" + Name);
+ return list;
}
[IgnoreDataMember]
@@ -80,5 +79,13 @@ namespace MediaBrowser.Controller.Entities.Audio
return false;
}
}
+
+ public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query)
+ {
+ query.Genres = new[] { Name };
+ query.IncludeItemTypes = new[] { typeof(MusicVideo).Name, typeof(Audio).Name, typeof(MusicAlbum).Name, typeof(MusicArtist).Name };
+
+ return LibraryManager.GetItemList(query);
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 475e2ace8..61060766f 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -44,6 +44,9 @@ namespace MediaBrowser.Controller.Entities
ImageInfos = new List<ItemImageInfo>();
}
+ public static readonly char[] SlugReplaceChars = { '?', '/', '&' };
+ public static char SlugChar = '-';
+
/// <summary>
/// The supported image extensions
/// </summary>
@@ -67,6 +70,12 @@ namespace MediaBrowser.Controller.Entities
public List<ItemImageInfo> ImageInfos { get; set; }
/// <summary>
+ /// Gets or sets the album.
+ /// </summary>
+ /// <value>The album.</value>
+ public string Album { get; set; }
+
+ /// <summary>
/// Gets or sets the channel identifier.
/// </summary>
/// <value>The channel identifier.</value>
@@ -125,6 +134,29 @@ namespace MediaBrowser.Controller.Entities
}
}
+ [IgnoreDataMember]
+ public string SlugName
+ {
+ get
+ {
+ var name = Name;
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ return string.Empty;
+ }
+
+ return SlugReplaceChars.Aggregate(name, (current, c) => current.Replace(c, SlugChar));
+ }
+ }
+
+ [IgnoreDataMember]
+ public bool IsUnaired
+ {
+ get { return PremiereDate.HasValue && PremiereDate.Value.ToLocalTime().Date >= DateTime.Now.Date; }
+ }
+
+ public string OriginalTitle { get; set; }
+
/// <summary>
/// Gets or sets the id.
/// </summary>
@@ -726,12 +758,14 @@ namespace MediaBrowser.Controller.Entities
/// Gets or sets the critic rating.
/// </summary>
/// <value>The critic rating.</value>
+ [IgnoreDataMember]
public float? CriticRating { get; set; }
/// <summary>
/// Gets or sets the critic rating summary.
/// </summary>
/// <value>The critic rating summary.</value>
+ [IgnoreDataMember]
public string CriticRatingSummary { get; set; }
/// <summary>
@@ -1147,33 +1181,31 @@ namespace MediaBrowser.Controller.Entities
get { return null; }
}
- private string _userDataKey;
- /// <summary>
- /// Gets the user data key.
- /// </summary>
- /// <returns>System.String.</returns>
- public string GetUserDataKey()
+ [IgnoreDataMember]
+ public virtual string PresentationUniqueKey
{
- if (string.IsNullOrWhiteSpace(_userDataKey))
- {
- var key = CreateUserDataKey();
- _userDataKey = key;
- return key;
- }
+ get { return Id.ToString("N"); }
+ }
- return _userDataKey;
+ public virtual bool RequiresRefresh()
+ {
+ return false;
}
- protected virtual string CreateUserDataKey()
+ public virtual List<string> GetUserDataKeys()
{
+ var list = new List<string>();
+
if (SourceType == SourceType.Channel)
{
if (!string.IsNullOrWhiteSpace(ExternalId))
{
- return ExternalId;
+ list.Add(ExternalId);
}
}
- return Id.ToString();
+
+ list.Add(Id.ToString());
+ return list;
}
internal virtual bool IsValidFromResolver(BaseItem newItem)
@@ -1186,7 +1218,6 @@ namespace MediaBrowser.Controller.Entities
public void AfterMetadataRefresh()
{
_sortName = null;
- _userDataKey = null;
}
/// <summary>
@@ -1540,11 +1571,11 @@ namespace MediaBrowser.Controller.Entities
{
if (!string.IsNullOrEmpty(info.Path))
{
- var itemByPath = LibraryManager.FindByPath(info.Path);
+ var itemByPath = LibraryManager.FindByPath(info.Path, null);
if (itemByPath == null)
{
- Logger.Warn("Unable to find linked item at path {0}", info.Path);
+ //Logger.Warn("Unable to find linked item at path {0}", info.Path);
}
return itemByPath;
@@ -1553,6 +1584,15 @@ namespace MediaBrowser.Controller.Entities
return null;
}
+ [IgnoreDataMember]
+ public virtual bool EnableRememberingTrackSelections
+ {
+ get
+ {
+ return true;
+ }
+ }
+
/// <summary>
/// Adds a studio to the item
/// </summary>
@@ -1606,9 +1646,7 @@ namespace MediaBrowser.Controller.Entities
throw new ArgumentNullException();
}
- var key = GetUserDataKey();
-
- var data = UserDataManager.GetUserData(user.Id, key);
+ var data = UserDataManager.GetUserData(user, this);
if (datePlayed.HasValue)
{
@@ -1643,9 +1681,7 @@ namespace MediaBrowser.Controller.Entities
throw new ArgumentNullException();
}
- var key = GetUserDataKey();
-
- var data = UserDataManager.GetUserData(user.Id, key);
+ var data = UserDataManager.GetUserData(user, this);
//I think it is okay to do this here.
// if this is only called when a user is manually forcing something to un-played
@@ -1976,14 +2012,14 @@ namespace MediaBrowser.Controller.Entities
public virtual bool IsPlayed(User user)
{
- var userdata = UserDataManager.GetUserData(user.Id, GetUserDataKey());
+ var userdata = UserDataManager.GetUserData(user, this);
return userdata != null && userdata.Played;
}
public bool IsFavoriteOrLiked(User user)
{
- var userdata = UserDataManager.GetUserData(user.Id, GetUserDataKey());
+ var userdata = UserDataManager.GetUserData(user, this);
return userdata != null && (userdata.IsFavorite || (userdata.Likes ?? false));
}
@@ -1995,7 +2031,7 @@ namespace MediaBrowser.Controller.Entities
throw new ArgumentNullException("user");
}
- var userdata = UserDataManager.GetUserData(user.Id, GetUserDataKey());
+ var userdata = UserDataManager.GetUserData(user, this);
return userdata == null || !userdata.Played;
}
@@ -2026,7 +2062,6 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public virtual bool BeforeMetadataRefresh()
{
- _userDataKey = null;
_sortName = null;
var hasChanges = false;
diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs
index 429700327..5e0cf6e88 100644
--- a/MediaBrowser.Controller/Entities/CollectionFolder.cs
+++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs
@@ -8,6 +8,7 @@ using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using CommonIO;
+using MoreLinq;
namespace MediaBrowser.Controller.Entities
{
@@ -97,7 +98,6 @@ namespace MediaBrowser.Controller.Entities
}
}
-
return base.IsValidFromResolver(newItem);
}
@@ -200,9 +200,30 @@ namespace MediaBrowser.Controller.Entities
public IEnumerable<Folder> GetPhysicalParents()
{
- return LibraryManager.RootFolder.Children
+ var rootChildren = LibraryManager.RootFolder.Children
.OfType<Folder>()
- .Where(i => i.Path != null && PhysicalLocations.Contains(i.Path, StringComparer.OrdinalIgnoreCase));
+ .ToList();
+
+ return PhysicalLocations.Where(i => !string.Equals(i, Path, StringComparison.OrdinalIgnoreCase)).SelectMany(i => GetPhysicalParents(i, rootChildren)).DistinctBy(i => i.Id);
+ }
+
+ private IEnumerable<Folder> GetPhysicalParents(string path, List<Folder> rootChildren)
+ {
+ var result = rootChildren
+ .Where(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase))
+ .ToList();
+
+ if (result.Count == 0)
+ {
+ var folder = LibraryManager.FindByPath(path, true) as Folder;
+
+ if (folder != null)
+ {
+ result.Add(folder);
+ }
+ }
+
+ return result;
}
[IgnoreDataMember]
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index f4cdc8fa1..77e362419 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -28,6 +28,9 @@ namespace MediaBrowser.Controller.Entities
public List<Guid> ThemeSongIds { get; set; }
public List<Guid> ThemeVideoIds { get; set; }
+ [IgnoreDataMember]
+ public DateTime? DateLastMediaAdded { get; set; }
+
public Folder()
{
LinkedChildren = new List<LinkedChild>();
@@ -56,6 +59,36 @@ namespace MediaBrowser.Controller.Entities
}
[IgnoreDataMember]
+ public virtual bool SupportsCumulativeRunTimeTicks
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ [IgnoreDataMember]
+ public virtual bool SupportsDateLastMediaAdded
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public override bool RequiresRefresh()
+ {
+ var baseResult = base.RequiresRefresh();
+
+ if (SupportsCumulativeRunTimeTicks && !RunTimeTicks.HasValue)
+ {
+ baseResult = true;
+ }
+
+ return baseResult;
+ }
+
+ [IgnoreDataMember]
public override string FileNameWithoutExtension
{
get
@@ -199,8 +232,8 @@ namespace MediaBrowser.Controller.Entities
/// <returns>Dictionary{System.StringFunc{UserIEnumerable{BaseItem}}}.</returns>
protected virtual IEnumerable<string> GetIndexByOptions()
{
- return new List<string> {
- {"None"},
+ return new List<string> {
+ {"None"},
{"Performer"},
{"Genre"},
{"Director"},
@@ -707,8 +740,8 @@ namespace MediaBrowser.Controller.Entities
{
return ItemRepository.GetItemIdsList(new InternalItemsQuery
{
- ParentId = Id
-
+ ParentId = Id,
+ GroupByPresentationUniqueKey = false
});
}
@@ -751,39 +784,44 @@ namespace MediaBrowser.Controller.Entities
return true;
}
}
+
+ var supportsUserDataQueries = ConfigurationManager.Configuration.SchemaVersion >= 76;
if (query.SortBy != null && query.SortBy.Length > 0)
{
- if (query.SortBy.Contains(ItemSortBy.DatePlayed, StringComparer.OrdinalIgnoreCase))
- {
- Logger.Debug("Query requires post-filtering due to ItemSortBy.DatePlayed");
- return true;
- }
- if (query.SortBy.Contains(ItemSortBy.IsFavoriteOrLiked, StringComparer.OrdinalIgnoreCase))
- {
- Logger.Debug("Query requires post-filtering due to ItemSortBy.IsFavoriteOrLiked");
- return true;
- }
- if (query.SortBy.Contains(ItemSortBy.IsPlayed, StringComparer.OrdinalIgnoreCase))
+ if (!supportsUserDataQueries)
{
- Logger.Debug("Query requires post-filtering due to ItemSortBy.IsPlayed");
- return true;
- }
- if (query.SortBy.Contains(ItemSortBy.IsUnplayed, StringComparer.OrdinalIgnoreCase))
- {
- Logger.Debug("Query requires post-filtering due to ItemSortBy.IsUnplayed");
- return true;
+ if (query.SortBy.Contains(ItemSortBy.DatePlayed, StringComparer.OrdinalIgnoreCase))
+ {
+ Logger.Debug("Query requires post-filtering due to ItemSortBy.IsFavoriteOrLiked");
+ return true;
+ }
+ if (query.SortBy.Contains(ItemSortBy.PlayCount, StringComparer.OrdinalIgnoreCase))
+ {
+ Logger.Debug("Query requires post-filtering due to ItemSortBy.PlayCount");
+ return true;
+ }
+ if (query.SortBy.Contains(ItemSortBy.IsFavoriteOrLiked, StringComparer.OrdinalIgnoreCase))
+ {
+ Logger.Debug("Query requires post-filtering due to ItemSortBy.IsFavoriteOrLiked");
+ return true;
+ }
+ if (query.SortBy.Contains(ItemSortBy.IsPlayed, StringComparer.OrdinalIgnoreCase))
+ {
+ Logger.Debug("Query requires post-filtering due to ItemSortBy.IsPlayed");
+ return true;
+ }
+ if (query.SortBy.Contains(ItemSortBy.IsUnplayed, StringComparer.OrdinalIgnoreCase))
+ {
+ Logger.Debug("Query requires post-filtering due to ItemSortBy.IsUnplayed");
+ return true;
+ }
}
if (query.SortBy.Contains(ItemSortBy.AiredEpisodeOrder, StringComparer.OrdinalIgnoreCase))
{
Logger.Debug("Query requires post-filtering due to ItemSortBy.AiredEpisodeOrder");
return true;
}
- if (query.SortBy.Contains(ItemSortBy.Album, StringComparer.OrdinalIgnoreCase))
- {
- Logger.Debug("Query requires post-filtering due to ItemSortBy.Album");
- return true;
- }
if (query.SortBy.Contains(ItemSortBy.AlbumArtist, StringComparer.OrdinalIgnoreCase))
{
Logger.Debug("Query requires post-filtering due to ItemSortBy.AlbumArtist");
@@ -799,11 +837,6 @@ namespace MediaBrowser.Controller.Entities
Logger.Debug("Query requires post-filtering due to ItemSortBy.Budget");
return true;
}
- if (query.SortBy.Contains(ItemSortBy.DateLastContentAdded, StringComparer.OrdinalIgnoreCase))
- {
- Logger.Debug("Query requires post-filtering due to ItemSortBy.DateLastContentAdded");
- return true;
- }
if (query.SortBy.Contains(ItemSortBy.GameSystem, StringComparer.OrdinalIgnoreCase))
{
Logger.Debug("Query requires post-filtering due to ItemSortBy.GameSystem");
@@ -819,11 +852,6 @@ namespace MediaBrowser.Controller.Entities
Logger.Debug("Query requires post-filtering due to ItemSortBy.OfficialRating");
return true;
}
- if (query.SortBy.Contains(ItemSortBy.PlayCount, StringComparer.OrdinalIgnoreCase))
- {
- Logger.Debug("Query requires post-filtering due to ItemSortBy.PlayCount");
- return true;
- }
if (query.SortBy.Contains(ItemSortBy.Players, StringComparer.OrdinalIgnoreCase))
{
Logger.Debug("Query requires post-filtering due to ItemSortBy.Players");
@@ -839,11 +867,6 @@ namespace MediaBrowser.Controller.Entities
Logger.Debug("Query requires post-filtering due to ItemSortBy.SeriesSortName");
return true;
}
- if (query.SortBy.Contains(ItemSortBy.StartDate, StringComparer.OrdinalIgnoreCase))
- {
- Logger.Debug("Query requires post-filtering due to ItemSortBy.StartDate");
- return true;
- }
if (query.SortBy.Contains(ItemSortBy.Studio, StringComparer.OrdinalIgnoreCase))
{
Logger.Debug("Query requires post-filtering due to ItemSortBy.Studio");
@@ -868,34 +891,37 @@ namespace MediaBrowser.Controller.Entities
return true;
}
- if (query.IsLiked.HasValue)
+ if (!supportsUserDataQueries)
{
- Logger.Debug("Query requires post-filtering due to IsLiked");
- return true;
- }
+ if (query.IsLiked.HasValue)
+ {
+ Logger.Debug("Query requires post-filtering due to IsLiked");
+ return true;
+ }
- if (query.IsFavoriteOrLiked.HasValue)
- {
- Logger.Debug("Query requires post-filtering due to IsFavoriteOrLiked");
- return true;
- }
+ if (query.IsFavoriteOrLiked.HasValue)
+ {
+ Logger.Debug("Query requires post-filtering due to IsFavoriteOrLiked");
+ return true;
+ }
- if (query.IsFavorite.HasValue)
- {
- Logger.Debug("Query requires post-filtering due to IsFavorite");
- return true;
- }
+ if (query.IsFavorite.HasValue)
+ {
+ Logger.Debug("Query requires post-filtering due to IsFavorite");
+ return true;
+ }
- if (query.IsResumable.HasValue)
- {
- Logger.Debug("Query requires post-filtering due to IsResumable");
- return true;
- }
+ if (query.IsResumable.HasValue)
+ {
+ Logger.Debug("Query requires post-filtering due to IsResumable");
+ return true;
+ }
- if (query.IsPlayed.HasValue)
- {
- Logger.Debug("Query requires post-filtering due to IsPlayed");
- return true;
+ if (query.IsPlayed.HasValue)
+ {
+ Logger.Debug("Query requires post-filtering due to IsPlayed");
+ return true;
+ }
}
if (query.IsInBoxSet.HasValue)
@@ -1059,30 +1085,6 @@ namespace MediaBrowser.Controller.Entities
return true;
}
- if (!string.IsNullOrWhiteSpace(query.NameContains))
- {
- Logger.Debug("Query requires post-filtering due to NameContains");
- return true;
- }
-
- if (!string.IsNullOrWhiteSpace(query.NameLessThan))
- {
- Logger.Debug("Query requires post-filtering due to NameLessThan");
- return true;
- }
-
- if (!string.IsNullOrWhiteSpace(query.NameStartsWith))
- {
- Logger.Debug("Query requires post-filtering due to NameStartsWith");
- return true;
- }
-
- if (!string.IsNullOrWhiteSpace(query.NameStartsWithOrGreater))
- {
- Logger.Debug("Query requires post-filtering due to NameStartsWithOrGreater");
- return true;
- }
-
if (query.AirDays.Length > 0)
{
Logger.Debug("Query requires post-filtering due to AirDays");
@@ -1107,12 +1109,6 @@ namespace MediaBrowser.Controller.Entities
return true;
}
- if (query.AlbumNames.Length > 0)
- {
- Logger.Debug("Query requires post-filtering due to AlbumNames");
- return true;
- }
-
if (query.ArtistNames.Length > 0)
{
Logger.Debug("Query requires post-filtering due to ArtistNames");
@@ -1297,33 +1293,42 @@ namespace MediaBrowser.Controller.Entities
public IList<BaseItem> GetRecursiveChildren(Func<BaseItem, bool> filter)
{
- var list = new List<BaseItem>();
+ var result = new Dictionary<Guid, BaseItem>();
- AddChildrenToList(list, true, filter);
+ AddChildrenToList(result, true, true, filter);
- return list;
+ return result.Values.ToList();
}
/// <summary>
/// Adds the children to list.
/// </summary>
- /// <param name="list">The list.</param>
- /// <param name="recursive">if set to <c>true</c> [recursive].</param>
- /// <param name="filter">The filter.</param>
- private void AddChildrenToList(List<BaseItem> list, bool recursive, Func<BaseItem, bool> filter)
+ private void AddChildrenToList(Dictionary<Guid,BaseItem> result, bool includeLinkedChildren, bool recursive, Func<BaseItem, bool> filter)
{
foreach (var child in Children)
{
if (filter == null || filter(child))
{
- list.Add(child);
+ result[child.Id] = child;
}
if (recursive && child.IsFolder)
{
var folder = (Folder)child;
- folder.AddChildrenToList(list, true, filter);
+ // We can only support includeLinkedChildren for the first folder, or we might end up stuck in a loop of linked items
+ folder.AddChildrenToList(result, false, true, filter);
+ }
+ }
+
+ if (includeLinkedChildren)
+ {
+ foreach (var child in GetLinkedChildren())
+ {
+ if (filter == null || filter(child))
+ {
+ result[child.Id] = child;
+ }
}
}
}
@@ -1567,38 +1572,17 @@ namespace MediaBrowser.Controller.Entities
await Task.WhenAll(tasks).ConfigureAwait(false);
}
- /// <summary>
- /// Finds an item by path, recursively
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>BaseItem.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- public BaseItem FindByPath(string path)
+ public override bool IsPlayed(User user)
{
- if (string.IsNullOrEmpty(path))
+ var itemsResult = GetItems(new InternalItemsQuery(user)
{
- throw new ArgumentNullException();
- }
-
- if (string.Equals(Path, path, StringComparison.OrdinalIgnoreCase))
- {
- return this;
- }
-
- if (PhysicalLocations.Contains(path, StringComparer.OrdinalIgnoreCase))
- {
- return this;
- }
+ Recursive = true,
+ IsFolder = false,
+ ExcludeLocationTypes = new[] { LocationType.Virtual }
- return GetRecursiveChildren(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) ||
- (!i.IsFolder && !i.IsInMixedFolder && string.Equals(i.ContainingFolderPath, path, StringComparison.OrdinalIgnoreCase)) ||
- i.PhysicalLocations.Contains(path, StringComparer.OrdinalIgnoreCase))
- .FirstOrDefault();
- }
+ }).Result;
- public override bool IsPlayed(User user)
- {
- return GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual)
+ return itemsResult.Items
.All(i => i.IsPlayed(user));
}
@@ -1607,26 +1591,50 @@ namespace MediaBrowser.Controller.Entities
return !IsPlayed(user);
}
+ [IgnoreDataMember]
+ public virtual bool SupportsUserDataFromChildren
+ {
+ get
+ {
+ // These are just far too slow.
+ if (this is ICollectionFolder)
+ {
+ return false;
+ }
+ if (this is UserView)
+ {
+ return false;
+ }
+ if (this is UserRootFolder)
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+
public override void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, User user)
{
+ if (!SupportsUserDataFromChildren)
+ {
+ return;
+ }
+
var recursiveItemCount = 0;
var unplayed = 0;
double totalPercentPlayed = 0;
- IEnumerable<BaseItem> children;
- var folder = this;
+ var itemsResult = GetItems(new InternalItemsQuery(user)
+ {
+ Recursive = true,
+ IsFolder = false,
+ ExcludeLocationTypes = new[] { LocationType.Virtual }
- var season = folder as Season;
+ }).Result;
- if (season != null)
- {
- children = season.GetEpisodes(user).Where(i => i.LocationType != LocationType.Virtual);
- }
- else
- {
- children = folder.GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual);
- }
+ var children = itemsResult.Items;
// Loop through each recursive child
foreach (var child in children)
@@ -1635,7 +1643,7 @@ namespace MediaBrowser.Controller.Entities
var isUnplayed = true;
- var itemUserData = UserDataManager.GetUserData(user.Id, child.GetUserDataKey());
+ var itemUserData = UserDataManager.GetUserData(user, child);
// Incrememt totalPercentPlayed
if (itemUserData != null)
diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs
index e597b2a15..9ed240046 100644
--- a/MediaBrowser.Controller/Entities/Game.cs
+++ b/MediaBrowser.Controller/Entities/Game.cs
@@ -76,15 +76,16 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public List<string> MultiPartGameFiles { get; set; }
- protected override string CreateUserDataKey()
+ public override List<string> GetUserDataKeys()
{
+ var list = base.GetUserDataKeys();
var id = this.GetProviderId(MetadataProviders.Gamesdb);
if (!string.IsNullOrEmpty(id))
{
- return "Game-Gamesdb-" + id;
+ list.Insert(0, "Game-Gamesdb-" + id);
}
- return base.CreateUserDataKey();
+ return list;
}
public override IEnumerable<string> GetDeletePaths()
diff --git a/MediaBrowser.Controller/Entities/GameGenre.cs b/MediaBrowser.Controller/Entities/GameGenre.cs
index d2b6b4856..7c1e88cb1 100644
--- a/MediaBrowser.Controller/Entities/GameGenre.cs
+++ b/MediaBrowser.Controller/Entities/GameGenre.cs
@@ -7,13 +7,12 @@ namespace MediaBrowser.Controller.Entities
{
public class GameGenre : BaseItem, IItemByName
{
- /// <summary>
- /// Gets the user data key.
- /// </summary>
- /// <returns>System.String.</returns>
- protected override string CreateUserDataKey()
+ public override List<string> GetUserDataKeys()
{
- return "GameGenre-" + Name;
+ var list = base.GetUserDataKeys();
+
+ list.Insert(0, "GameGenre-" + Name);
+ return list;
}
/// <summary>
@@ -63,6 +62,14 @@ namespace MediaBrowser.Controller.Entities
return i => i is Game && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase);
}
+ public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query)
+ {
+ query.Genres = new[] { Name };
+ query.IncludeItemTypes = new[] { typeof(Game).Name };
+
+ return LibraryManager.GetItemList(query);
+ }
+
[IgnoreDataMember]
public override bool SupportsPeople
{
diff --git a/MediaBrowser.Controller/Entities/GameSystem.cs b/MediaBrowser.Controller/Entities/GameSystem.cs
index bc35c4738..1c09ee507 100644
--- a/MediaBrowser.Controller/Entities/GameSystem.cs
+++ b/MediaBrowser.Controller/Entities/GameSystem.cs
@@ -2,6 +2,7 @@
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using System;
+using System.Collections.Generic;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities
@@ -31,17 +32,15 @@ namespace MediaBrowser.Controller.Entities
/// <value>The game system.</value>
public string GameSystemName { get; set; }
- /// <summary>
- /// Gets the user data key.
- /// </summary>
- /// <returns>System.String.</returns>
- protected override string CreateUserDataKey()
+ public override List<string> GetUserDataKeys()
{
+ var list = base.GetUserDataKeys();
+
if (!string.IsNullOrEmpty(GameSystemName))
{
- return "GameSystem-" + GameSystemName;
+ list.Insert(0, "GameSystem-" + GameSystemName);
}
- return base.CreateUserDataKey();
+ return list;
}
protected override bool GetBlockUnratedValue(UserPolicy config)
diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs
index 233e1e0fd..c87d4daaf 100644
--- a/MediaBrowser.Controller/Entities/Genre.cs
+++ b/MediaBrowser.Controller/Entities/Genre.cs
@@ -11,13 +11,12 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public class Genre : BaseItem, IItemByName
{
- /// <summary>
- /// Gets the user data key.
- /// </summary>
- /// <returns>System.String.</returns>
- protected override string CreateUserDataKey()
+ public override List<string> GetUserDataKeys()
{
- return "Genre-" + Name;
+ var list = base.GetUserDataKeys();
+
+ list.Insert(0, "Genre-" + Name);
+ return list;
}
/// <summary>
@@ -67,6 +66,14 @@ namespace MediaBrowser.Controller.Entities
return i => !(i is Game) && !(i is IHasMusicGenres) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase);
}
+ public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query)
+ {
+ query.Genres = new[] { Name };
+ query.ExcludeItemTypes = new[] { typeof(Game).Name, typeof(MusicVideo).Name, typeof(Audio.Audio).Name, typeof(MusicAlbum).Name, typeof(MusicArtist).Name };
+
+ return LibraryManager.GetItemList(query);
+ }
+
[IgnoreDataMember]
public override bool SupportsPeople
{
diff --git a/MediaBrowser.Controller/Entities/IHasMetadata.cs b/MediaBrowser.Controller/Entities/IHasMetadata.cs
index 1f680b35f..c7940c8a9 100644
--- a/MediaBrowser.Controller/Entities/IHasMetadata.cs
+++ b/MediaBrowser.Controller/Entities/IHasMetadata.cs
@@ -49,5 +49,7 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <value><c>true</c> if [supports people]; otherwise, <c>false</c>.</value>
bool SupportsPeople { get; }
+
+ bool RequiresRefresh();
}
}
diff --git a/MediaBrowser.Controller/Entities/IHasUserData.cs b/MediaBrowser.Controller/Entities/IHasUserData.cs
index 34a820853..244b319bd 100644
--- a/MediaBrowser.Controller/Entities/IHasUserData.cs
+++ b/MediaBrowser.Controller/Entities/IHasUserData.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Model.Dto;
+using System.Collections.Generic;
+using MediaBrowser.Model.Dto;
namespace MediaBrowser.Controller.Entities
{
@@ -7,11 +8,7 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public interface IHasUserData : IHasId
{
- /// <summary>
- /// Gets the user data key.
- /// </summary>
- /// <returns>System.String.</returns>
- string GetUserDataKey();
+ List<string> GetUserDataKeys();
/// <summary>
/// Fills the user data dto values.
@@ -20,5 +17,7 @@ namespace MediaBrowser.Controller.Entities
/// <param name="userData">The user data.</param>
/// <param name="user">The user.</param>
void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, User user);
+
+ bool EnableRememberingTrackSelections { get; }
}
}
diff --git a/MediaBrowser.Controller/Entities/IItemByName.cs b/MediaBrowser.Controller/Entities/IItemByName.cs
index e6667290c..7747e738c 100644
--- a/MediaBrowser.Controller/Entities/IItemByName.cs
+++ b/MediaBrowser.Controller/Entities/IItemByName.cs
@@ -20,6 +20,8 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <returns>Func&lt;BaseItem, System.Boolean&gt;.</returns>
Func<BaseItem, bool> GetItemFilter();
+
+ IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query);
}
public interface IHasDualAccess : IItemByName
diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
index f3b4f4053..5236b0a27 100644
--- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
+++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
@@ -46,8 +46,11 @@ namespace MediaBrowser.Controller.Entities
public string NameLessThan { get; set; }
public string NameContains { get; set; }
+ public string PresentationUniqueKey { get; set; }
public string Path { get; set; }
-
+ public string Name { get; set; }
+ public string SlugName { get; set; }
+
public string Person { get; set; }
public string[] PersonIds { get; set; }
public string[] ItemIds { get; set; }
@@ -106,6 +109,7 @@ namespace MediaBrowser.Controller.Entities
internal List<Guid> ItemIdsFromPersonFilters { get; set; }
public int? ParentIndexNumber { get; set; }
+ public int? IndexNumber { get; set; }
public int? MinParentalRating { get; set; }
public int? MaxParentalRating { get; set; }
@@ -130,9 +134,16 @@ namespace MediaBrowser.Controller.Entities
public string[] AlbumNames { get; set; }
public string[] ArtistNames { get; set; }
-
+ public string AncestorWithPresentationUniqueKey { get; set; }
+
+ public bool GroupByPresentationUniqueKey { get; set; }
+ public bool EnableTotalRecordCount { get; set; }
+
public InternalItemsQuery()
{
+ GroupByPresentationUniqueKey = true;
+ EnableTotalRecordCount = true;
+
AlbumNames = new string[] { };
ArtistNames = new string[] { };
diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
index cd3e07ea3..09a9d97bc 100644
--- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
+++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
@@ -8,6 +8,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
+using MediaBrowser.Controller.Entities.Audio;
namespace MediaBrowser.Controller.Entities.Movies
{
@@ -118,7 +119,7 @@ namespace MediaBrowser.Controller.Entities.Movies
// Gather all possible ratings
var ratings = GetRecursiveChildren()
.Concat(GetLinkedChildren())
- .Where(i => i is Movie || i is Series)
+ .Where(i => i is Movie || i is Series || i is MusicAlbum || i is Game)
.Select(i => i.OfficialRating)
.Where(i => !string.IsNullOrEmpty(i))
.Distinct(StringComparer.OrdinalIgnoreCase)
diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs
index 4d2ca9ffe..5882b5f4d 100644
--- a/MediaBrowser.Controller/Entities/Movies/Movie.cs
+++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs
@@ -18,8 +18,6 @@ namespace MediaBrowser.Controller.Entities.Movies
{
public List<Guid> SpecialFeatureIds { get; set; }
- public string OriginalTitle { get; set; }
-
public List<Guid> ThemeSongIds { get; set; }
public List<Guid> ThemeVideoIds { get; set; }
public List<string> ProductionLocations { get; set; }
@@ -77,34 +75,6 @@ namespace MediaBrowser.Controller.Entities.Movies
get { return TmdbCollectionName; }
set { TmdbCollectionName = value; }
}
-
- /// <summary>
- /// Gets the user data key.
- /// </summary>
- /// <returns>System.String.</returns>
- protected override string CreateUserDataKey()
- {
- var key = GetMovieUserDataKey(this);
-
- if (string.IsNullOrWhiteSpace(key))
- {
- key = base.CreateUserDataKey();
- }
-
- return key;
- }
-
- public static string GetMovieUserDataKey(BaseItem movie)
- {
- var key = movie.GetProviderId(MetadataProviders.Tmdb);
-
- if (string.IsNullOrWhiteSpace(key))
- {
- key = movie.GetProviderId(MetadataProviders.Imdb);
- }
-
- return key;
- }
protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
{
diff --git a/MediaBrowser.Controller/Entities/MusicVideo.cs b/MediaBrowser.Controller/Entities/MusicVideo.cs
index b52f16a46..7119828e2 100644
--- a/MediaBrowser.Controller/Entities/MusicVideo.cs
+++ b/MediaBrowser.Controller/Entities/MusicVideo.cs
@@ -10,12 +10,6 @@ namespace MediaBrowser.Controller.Entities
public class MusicVideo : Video, IHasArtist, IHasMusicGenres, IHasProductionLocations, IHasBudget, IHasLookupInfo<MusicVideoInfo>
{
/// <summary>
- /// Gets or sets the album.
- /// </summary>
- /// <value>The album.</value>
- public string Album { get; set; }
-
- /// <summary>
/// Gets or sets the budget.
/// </summary>
/// <value>The budget.</value>
@@ -44,15 +38,6 @@ namespace MediaBrowser.Controller.Entities
}
}
- /// <summary>
- /// Gets the user data key.
- /// </summary>
- /// <returns>System.String.</returns>
- protected override string CreateUserDataKey()
- {
- return this.GetProviderId(MetadataProviders.Tmdb) ?? this.GetProviderId(MetadataProviders.Imdb) ?? base.CreateUserDataKey();
- }
-
public override UnratedItem GetBlockUnratedType()
{
return UnratedItem.Music;
diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs
index 560ea6e05..2b099e3d5 100644
--- a/MediaBrowser.Controller/Entities/Person.cs
+++ b/MediaBrowser.Controller/Entities/Person.cs
@@ -18,13 +18,12 @@ namespace MediaBrowser.Controller.Entities
/// <value>The place of birth.</value>
public string PlaceOfBirth { get; set; }
- /// <summary>
- /// Gets the user data key.
- /// </summary>
- /// <returns>System.String.</returns>
- protected override string CreateUserDataKey()
+ public override List<string> GetUserDataKeys()
{
- return "Person-" + Name;
+ var list = base.GetUserDataKeys();
+
+ list.Insert(0, "Person-" + Name);
+ return list;
}
public PersonLookupInfo GetLookupInfo()
@@ -32,6 +31,13 @@ namespace MediaBrowser.Controller.Entities
return GetItemLookupInfo<PersonLookupInfo>();
}
+ public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query)
+ {
+ query.Person = Name;
+
+ return LibraryManager.GetItemList(query);
+ }
+
/// <summary>
/// Returns the folder containing the item.
/// If the item is a folder, it returns the folder itself
diff --git a/MediaBrowser.Controller/Entities/PhotoAlbum.cs b/MediaBrowser.Controller/Entities/PhotoAlbum.cs
index c8ab67a69..b0ddcfb8c 100644
--- a/MediaBrowser.Controller/Entities/PhotoAlbum.cs
+++ b/MediaBrowser.Controller/Entities/PhotoAlbum.cs
@@ -8,15 +8,6 @@ namespace MediaBrowser.Controller.Entities
public class PhotoAlbum : Folder
{
[IgnoreDataMember]
- public override bool SupportsLocalMetadata
- {
- get
- {
- return false;
- }
- }
-
- [IgnoreDataMember]
public override bool AlwaysScanInternalMetadataPath
{
get
diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs
index a55527f37..48ca7bbcc 100644
--- a/MediaBrowser.Controller/Entities/Studio.cs
+++ b/MediaBrowser.Controller/Entities/Studio.cs
@@ -10,13 +10,12 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public class Studio : BaseItem, IItemByName, IHasTags
{
- /// <summary>
- /// Gets the user data key.
- /// </summary>
- /// <returns>System.String.</returns>
- protected override string CreateUserDataKey()
+ public override List<string> GetUserDataKeys()
{
- return "Studio-" + Name;
+ var list = base.GetUserDataKeys();
+
+ list.Insert(0, "Studio-" + Name);
+ return list;
}
/// <summary>
@@ -66,6 +65,13 @@ namespace MediaBrowser.Controller.Entities
return i => i.Studios.Contains(Name, StringComparer.OrdinalIgnoreCase);
}
+ public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query)
+ {
+ query.Studios = new[] { Name };
+
+ return LibraryManager.GetItemList(query);
+ }
+
[IgnoreDataMember]
public override bool SupportsPeople
{
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs
index e1a91086b..a4b0b3082 100644
--- a/MediaBrowser.Controller/Entities/TV/Episode.cs
+++ b/MediaBrowser.Controller/Entities/TV/Episode.cs
@@ -58,60 +58,48 @@ namespace MediaBrowser.Controller.Entities.TV
{
get
{
- return AirsAfterSeasonNumber ?? AirsBeforeSeasonNumber ?? PhysicalSeasonNumber;
+ return AirsAfterSeasonNumber ?? AirsBeforeSeasonNumber ?? ParentIndexNumber;
}
}
[IgnoreDataMember]
- public int? PhysicalSeasonNumber
+ public override Folder LatestItemsIndexContainer
{
get
{
- var value = ParentIndexNumber;
-
- if (value.HasValue)
- {
- return value;
- }
-
- var season = Season;
-
- return season != null ? season.IndexNumber : null;
+ return Series;
}
}
[IgnoreDataMember]
- public override Folder LatestItemsIndexContainer
+ public override Guid? DisplayParentId
{
get
{
- return Series;
+ return SeasonId;
}
}
[IgnoreDataMember]
- public override Guid? DisplayParentId
+ protected override bool EnableDefaultVideoUserDataKeys
{
get
{
- return SeasonId;
+ return false;
}
}
- /// <summary>
- /// Gets the user data key.
- /// </summary>
- /// <returns>System.String.</returns>
- protected override string CreateUserDataKey()
+ public override List<string> GetUserDataKeys()
{
- var series = Series;
+ var list = base.GetUserDataKeys();
+ var series = Series;
if (series != null && ParentIndexNumber.HasValue && IndexNumber.HasValue)
{
- return series.GetUserDataKey() + ParentIndexNumber.Value.ToString("000") + IndexNumber.Value.ToString("000");
+ list.InsertRange(0, series.GetUserDataKeys().Select(i => i + ParentIndexNumber.Value.ToString("000") + IndexNumber.Value.ToString("000")));
}
- return base.CreateUserDataKey();
+ return list;
}
/// <summary>
@@ -223,12 +211,6 @@ namespace MediaBrowser.Controller.Entities.TV
}
[IgnoreDataMember]
- public bool IsUnaired
- {
- get { return PremiereDate.HasValue && PremiereDate.Value.ToLocalTime().Date >= DateTime.Now.Date; }
- }
-
- [IgnoreDataMember]
public bool IsVirtualUnaired
{
get { return LocationType == LocationType.Virtual && IsUnaired; }
@@ -310,6 +292,19 @@ namespace MediaBrowser.Controller.Entities.TV
Logger.ErrorException("Error in FillMissingEpisodeNumbersFromPath. Episode: {0}", ex, Path ?? Name ?? Id.ToString());
}
+ if (!ParentIndexNumber.HasValue)
+ {
+ var season = Season;
+ if (season != null)
+ {
+ if (season.ParentIndexNumber.HasValue)
+ {
+ ParentIndexNumber = season.ParentIndexNumber;
+ hasChanges = true;
+ }
+ }
+ }
+
return hasChanges;
}
}
diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs
index acd02e8ab..ab125eecb 100644
--- a/MediaBrowser.Controller/Entities/TV/Season.cs
+++ b/MediaBrowser.Controller/Entities/TV/Season.cs
@@ -33,6 +33,15 @@ namespace MediaBrowser.Controller.Entities.TV
}
[IgnoreDataMember]
+ public override bool SupportsDateLastMediaAdded
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ [IgnoreDataMember]
public override Guid? DisplayParentId
{
get
@@ -45,27 +54,25 @@ namespace MediaBrowser.Controller.Entities.TV
// Genre, Rating and Stuido will all be the same
protected override IEnumerable<string> GetIndexByOptions()
{
- return new List<string> {
- {"None"},
+ return new List<string> {
+ {"None"},
{"Performer"},
{"Director"},
{"Year"},
};
}
- /// <summary>
- /// Gets the user data key.
- /// </summary>
- /// <returns>System.String.</returns>
- protected override string CreateUserDataKey()
+ public override List<string> GetUserDataKeys()
{
- if (Series != null)
+ var list = base.GetUserDataKeys();
+
+ var series = Series;
+ if (series != null)
{
- var seasonNo = IndexNumber ?? 0;
- return Series.GetUserDataKey() + seasonNo.ToString("000");
+ list.InsertRange(0, series.GetUserDataKeys().Select(i => i + (IndexNumber ?? 0).ToString("000")));
}
- return base.CreateUserDataKey();
+ return list;
}
/// <summary>
@@ -94,6 +101,24 @@ namespace MediaBrowser.Controller.Entities.TV
}
}
+ [IgnoreDataMember]
+ public override string PresentationUniqueKey
+ {
+ get
+ {
+ if (IndexNumber.HasValue)
+ {
+ var series = Series;
+ if (series != null)
+ {
+ return series.PresentationUniqueKey + "-" + (IndexNumber ?? 0).ToString("000");
+ }
+ }
+
+ return base.PresentationUniqueKey;
+ }
+ }
+
/// <summary>
/// Creates the name of the sort.
/// </summary>
@@ -103,17 +128,23 @@ namespace MediaBrowser.Controller.Entities.TV
return IndexNumber != null ? IndexNumber.Value.ToString("0000") : Name;
}
- [IgnoreDataMember]
- public bool IsMissingSeason
+ public override bool RequiresRefresh()
{
- get { return LocationType == LocationType.Virtual && GetEpisodes().All(i => i.IsMissingEpisode); }
+ var result = base.RequiresRefresh();
+
+ if (!result)
+ {
+ if (!IsMissingSeason.HasValue)
+ {
+ return true;
+ }
+ }
+
+ return result;
}
[IgnoreDataMember]
- public bool IsUnaired
- {
- get { return GetEpisodes().All(i => i.IsUnaired); }
- }
+ public bool? IsMissingSeason { get; set; }
[IgnoreDataMember]
public bool IsVirtualUnaired
@@ -124,7 +155,7 @@ namespace MediaBrowser.Controller.Entities.TV
[IgnoreDataMember]
public bool IsMissingOrVirtualUnaired
{
- get { return LocationType == LocationType.Virtual && GetEpisodes().All(i => i.IsVirtualUnaired || i.IsMissingEpisode); }
+ get { return (IsMissingSeason ?? false) || (LocationType == LocationType.Virtual && IsUnaired); }
}
[IgnoreDataMember]
@@ -135,22 +166,16 @@ namespace MediaBrowser.Controller.Entities.TV
protected override Task<QueryResult<BaseItem>> GetItemsInternal(InternalItemsQuery query)
{
+ if (query.User == null)
+ {
+ return base.GetItemsInternal(query);
+ }
+
var user = query.User;
Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager);
- IEnumerable<BaseItem> items;
-
- if (query.User == null)
- {
- items = query.Recursive
- ? GetRecursiveChildren(filter)
- : Children.Where(filter);
- }
- else
- {
- items = GetEpisodes(query.User).Where(filter);
- }
+ var items = GetEpisodes(user).Where(filter);
var result = PostFilterAndSort(items, query);
@@ -171,16 +196,16 @@ namespace MediaBrowser.Controller.Entities.TV
public IEnumerable<Episode> GetEpisodes(User user, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes)
{
- var episodes = GetRecursiveChildren(user)
- .OfType<Episode>();
-
var series = Series;
if (IndexNumber.HasValue && series != null)
{
- return series.GetEpisodes(user, IndexNumber.Value, includeMissingEpisodes, includeVirtualUnairedEpisodes, episodes);
+ return series.GetEpisodes(user, IndexNumber.Value, includeMissingEpisodes, includeVirtualUnairedEpisodes);
}
+ var episodes = GetRecursiveChildren(user)
+ .OfType<Episode>();
+
if (series != null && series.ContainsEpisodesWithoutSeasonFolders)
{
var seasonNumber = IndexNumber;
@@ -199,7 +224,7 @@ namespace MediaBrowser.Controller.Entities.TV
episodes = list.DistinctBy(i => i.Id);
}
-
+
if (!includeMissingEpisodes)
{
episodes = episodes.Where(i => !i.IsMissingEpisode);
@@ -214,7 +239,7 @@ namespace MediaBrowser.Controller.Entities.TV
.Cast<Episode>();
}
- private IEnumerable<Episode> GetEpisodes()
+ public IEnumerable<Episode> GetEpisodes()
{
var episodes = GetRecursiveChildren().OfType<Episode>();
var series = Series;
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
index 9eeb89a1f..82ab99980 100644
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ b/MediaBrowser.Controller/Entities/TV/Series.cs
@@ -9,6 +9,7 @@ using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
+using MoreLinq;
namespace MediaBrowser.Controller.Entities.TV
{
@@ -19,8 +20,6 @@ namespace MediaBrowser.Controller.Entities.TV
{
public List<Guid> SpecialFeatureIds { get; set; }
- public string OriginalTitle { get; set; }
-
public int? AnimeSeriesIndex { get; set; }
public Series()
@@ -49,6 +48,15 @@ namespace MediaBrowser.Controller.Entities.TV
}
}
+ [IgnoreDataMember]
+ public override bool SupportsDateLastMediaAdded
+ {
+ get
+ {
+ return true;
+ }
+ }
+
public bool DisplaySpecialsWithSeasons { get; set; }
public List<Guid> LocalTrailerIds { get; set; }
@@ -93,25 +101,40 @@ namespace MediaBrowser.Controller.Entities.TV
}
}
+ [IgnoreDataMember]
+ public override string PresentationUniqueKey
+ {
+ get
+ {
+ if (EnablePooling())
+ {
+ return GetUserDataKeys().First();
+ }
+ return base.PresentationUniqueKey;
+ }
+ }
+
/// <summary>
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
- protected override string CreateUserDataKey()
+ public override List<string> GetUserDataKeys()
{
- var key = this.GetProviderId(MetadataProviders.Tvdb);
+ var list = base.GetUserDataKeys();
- if (string.IsNullOrWhiteSpace(key))
+ var key = this.GetProviderId(MetadataProviders.Imdb);
+ if (!string.IsNullOrWhiteSpace(key))
{
- key = this.GetProviderId(MetadataProviders.Imdb);
+ list.Insert(0, key);
}
- if (string.IsNullOrWhiteSpace(key))
+ key = this.GetProviderId(MetadataProviders.Tvdb);
+ if (!string.IsNullOrWhiteSpace(key))
{
- key = base.CreateUserDataKey();
+ list.Insert(0, key);
}
- return key;
+ return list;
}
/// <summary>
@@ -128,8 +151,8 @@ namespace MediaBrowser.Controller.Entities.TV
// Studio, Genre and Rating will all be the same so makes no sense to index by these
protected override IEnumerable<string> GetIndexByOptions()
{
- return new List<string> {
- {"None"},
+ return new List<string> {
+ {"None"},
{"Performer"},
{"Director"},
{"Year"},
@@ -174,7 +197,7 @@ namespace MediaBrowser.Controller.Entities.TV
else
{
items = query.Recursive
- ? GetRecursiveChildren(user, filter)
+ ? GetSeasons(user).Cast<BaseItem>().Concat(GetEpisodes(user)).Where(filter)
: GetSeasons(user).Where(filter);
}
@@ -185,8 +208,35 @@ namespace MediaBrowser.Controller.Entities.TV
public IEnumerable<Season> GetSeasons(User user, bool includeMissingSeasons, bool includeVirtualUnaired)
{
- var seasons = base.GetChildren(user, true)
- .OfType<Season>();
+ IEnumerable<Season> seasons;
+
+ if (EnablePooling())
+ {
+ var seriesIds = LibraryManager.GetItemIds(new InternalItemsQuery(user)
+ {
+ PresentationUniqueKey = PresentationUniqueKey,
+ IncludeItemTypes = new[] { typeof(Series).Name }
+ });
+
+ if (seriesIds.Count > 1)
+ {
+ seasons = LibraryManager.GetItemList(new InternalItemsQuery(user)
+ {
+ AncestorIds = seriesIds.Select(i => i.ToString("N")).ToArray(),
+ IncludeItemTypes = new[] { typeof(Season).Name },
+ SortBy = new[] { ItemSortBy.SortName }
+
+ }).Cast<Season>();
+ }
+ else
+ {
+ seasons = LibraryManager.Sort(base.GetChildren(user, true), user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).OfType<Season>();
+ }
+ }
+ else
+ {
+ seasons = LibraryManager.Sort(base.GetChildren(user, true), user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).OfType<Season>();
+ }
if (!includeMissingSeasons && !includeVirtualUnaired)
{
@@ -196,7 +246,7 @@ namespace MediaBrowser.Controller.Entities.TV
{
if (!includeMissingSeasons)
{
- seasons = seasons.Where(i => !i.IsMissingSeason);
+ seasons = seasons.Where(i => !(i.IsMissingSeason ?? false));
}
if (!includeVirtualUnaired)
{
@@ -204,9 +254,7 @@ namespace MediaBrowser.Controller.Entities.TV
}
}
- return LibraryManager
- .Sort(seasons, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending)
- .Cast<Season>();
+ return seasons;
}
public IEnumerable<Episode> GetEpisodes(User user)
@@ -226,17 +274,8 @@ namespace MediaBrowser.Controller.Entities.TV
// Specials could appear twice based on above - once in season 0, once in the aired season
// This depends on settings for that series
// When this happens, remove the duplicate from season 0
- var returnList = new List<Episode>();
-
- foreach (var episode in allEpisodes)
- {
- if (!returnList.Contains(episode))
- {
- returnList.Insert(0, episode);
- }
- }
- return returnList;
+ return allEpisodes.DistinctBy(i => i.Id).Reverse();
}
public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
@@ -257,7 +296,7 @@ namespace MediaBrowser.Controller.Entities.TV
// Refresh current item
await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
- // Refresh TV
+ // Refresh seasons
foreach (var item in seasons)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -270,12 +309,30 @@ namespace MediaBrowser.Controller.Entities.TV
progress.Report(percent * 100);
}
- // Refresh all non-songs
+ // Refresh episodes and other children
foreach (var item in otherItems)
{
cancellationToken.ThrowIfCancellationRequested();
- await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
+ var skipItem = false;
+
+ var episode = item as Episode;
+
+ if (episode != null
+ && refreshOptions.MetadataRefreshMode != MetadataRefreshMode.FullRefresh
+ && !refreshOptions.ReplaceAllMetadata
+ && episode.IsMissingEpisode
+ && episode.LocationType == Model.Entities.LocationType.Virtual
+ && episode.PremiereDate.HasValue
+ && (DateTime.UtcNow - episode.PremiereDate.Value).TotalDays > 30)
+ {
+ skipItem = true;
+ }
+
+ if (!skipItem)
+ {
+ await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
+ }
numComplete++;
double percent = numComplete;
@@ -295,20 +352,46 @@ namespace MediaBrowser.Controller.Entities.TV
return GetEpisodes(user, seasonNumber, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes);
}
- public IEnumerable<Episode> GetEpisodes(User user, int seasonNumber, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes)
+ private bool EnablePooling()
{
- return GetEpisodes(user, seasonNumber, includeMissingEpisodes, includeVirtualUnairedEpisodes,
- new List<Episode>());
+ return false;
}
- internal IEnumerable<Episode> GetEpisodes(User user, int seasonNumber, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes, IEnumerable<Episode> additionalEpisodes)
+ public IEnumerable<Episode> GetEpisodes(User user, int seasonNumber, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes)
{
- var episodes = GetRecursiveChildren(user, i => i is Episode)
- .Cast<Episode>();
+ IEnumerable<Episode> episodes;
- episodes = FilterEpisodesBySeason(episodes, seasonNumber, DisplaySpecialsWithSeasons);
+ if (EnablePooling())
+ {
+ var seriesIds = LibraryManager.GetItemIds(new InternalItemsQuery(user)
+ {
+ PresentationUniqueKey = PresentationUniqueKey,
+ IncludeItemTypes = new[] { typeof(Series).Name }
+ });
- episodes = episodes.Concat(additionalEpisodes).Distinct();
+ if (seriesIds.Count > 1)
+ {
+ episodes = LibraryManager.GetItemList(new InternalItemsQuery(user)
+ {
+ AncestorIds = seriesIds.Select(i => i.ToString("N")).ToArray(),
+ IncludeItemTypes = new[] { typeof(Episode).Name },
+ SortBy = new[] { ItemSortBy.SortName }
+
+ }).Cast<Episode>();
+ }
+ else
+ {
+ episodes = GetRecursiveChildren(user, i => i is Episode)
+ .Cast<Episode>();
+ }
+ }
+ else
+ {
+ episodes = GetRecursiveChildren(user, i => i is Episode)
+ .Cast<Episode>();
+ }
+
+ episodes = FilterEpisodesBySeason(episodes, seasonNumber, DisplaySpecialsWithSeasons);
if (!includeMissingEpisodes)
{
@@ -336,7 +419,7 @@ namespace MediaBrowser.Controller.Entities.TV
{
if (!includeSpecials || seasonNumber < 1)
{
- return episodes.Where(i => (i.PhysicalSeasonNumber ?? -1) == seasonNumber);
+ return episodes.Where(i => (i.ParentIndexNumber ?? -1) == seasonNumber);
}
return episodes.Where(i =>
diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs
index c3e24090c..3be2fc624 100644
--- a/MediaBrowser.Controller/Entities/Trailer.cs
+++ b/MediaBrowser.Controller/Entities/Trailer.cs
@@ -11,7 +11,7 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// Class Trailer
/// </summary>
- public class Trailer : Video, IHasCriticRating, IHasProductionLocations, IHasBudget, IHasKeywords, IHasTaglines, IHasMetascore, IHasLookupInfo<TrailerInfo>
+ public class Trailer : Video, IHasCriticRating, IHasProductionLocations, IHasBudget, IHasKeywords, IHasTaglines, IHasMetascore, IHasOriginalTitle, IHasLookupInfo<TrailerInfo>
{
public List<string> ProductionLocations { get; set; }
@@ -56,26 +56,6 @@ namespace MediaBrowser.Controller.Entities
/// <value>The revenue.</value>
public double? Revenue { get; set; }
- protected override string CreateUserDataKey()
- {
- var key = Movie.GetMovieUserDataKey(this);
-
- if (!string.IsNullOrWhiteSpace(key))
- {
- key = key + "-trailer";
-
- // Make sure different trailers have their own data.
- if (RunTimeTicks.HasValue)
- {
- key += "-" + RunTimeTicks.Value.ToString(CultureInfo.InvariantCulture);
- }
-
- return key;
- }
-
- return base.CreateUserDataKey();
- }
-
public override UnratedItem GetBlockUnratedType()
{
return UnratedItem.Trailer;
diff --git a/MediaBrowser.Controller/Entities/UserItemData.cs b/MediaBrowser.Controller/Entities/UserItemData.cs
index 16c37e7d3..f95fd7036 100644
--- a/MediaBrowser.Controller/Entities/UserItemData.cs
+++ b/MediaBrowser.Controller/Entities/UserItemData.cs
@@ -88,6 +88,8 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <value>The index of the subtitle stream.</value>
public int? SubtitleStreamIndex { get; set; }
+
+ public const double MinLikeValue = 6.5;
/// <summary>
/// This is an interpreted property to indicate likes or dislikes
@@ -101,7 +103,7 @@ namespace MediaBrowser.Controller.Entities
{
if (Rating != null)
{
- return Rating >= 6.5;
+ return Rating >= MinLikeValue;
}
return null;
diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs
index 8ce39c697..b9e997d17 100644
--- a/MediaBrowser.Controller/Entities/UserRootFolder.cs
+++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs
@@ -102,10 +102,5 @@ namespace MediaBrowser.Controller.Entities
LibraryManager.RegisterItem(item);
}
}
-
- public override void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, User user)
- {
- // Nothing meaninful here and will only waste resources
- }
}
}
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
index db669ca37..ba496fa5c 100644
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
@@ -348,7 +348,7 @@ namespace MediaBrowser.Controller.Entities
.Where(i => !i.IsFolder)
.OfType<IHasAlbumArtist>();
- var artists = _libraryManager.GetAlbumArtists(items).Where(i => _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).IsFavorite);
+ var artists = _libraryManager.GetAlbumArtists(items).Where(i => _userDataManager.GetUserData(user, i).IsFavorite);
return GetResult(artists, parent, query);
}
@@ -1077,7 +1077,7 @@ namespace MediaBrowser.Controller.Entities
var e = i as Season;
if (e != null)
{
- return e.IsMissingSeason == val;
+ return (e.IsMissingSeason ?? false) == val;
}
return true;
});
@@ -1218,7 +1218,7 @@ namespace MediaBrowser.Controller.Entities
if (query.IsLiked.HasValue)
{
- userData = userData ?? userDataManager.GetUserData(user.Id, item.GetUserDataKey());
+ userData = userData ?? userDataManager.GetUserData(user, item);
if (!userData.Likes.HasValue || userData.Likes != query.IsLiked.Value)
{
@@ -1228,7 +1228,7 @@ namespace MediaBrowser.Controller.Entities
if (query.IsFavoriteOrLiked.HasValue)
{
- userData = userData ?? userDataManager.GetUserData(user.Id, item.GetUserDataKey());
+ userData = userData ?? userDataManager.GetUserData(user, item);
var isFavoriteOrLiked = userData.IsFavorite || (userData.Likes ?? false);
if (isFavoriteOrLiked != query.IsFavoriteOrLiked.Value)
@@ -1239,7 +1239,7 @@ namespace MediaBrowser.Controller.Entities
if (query.IsFavorite.HasValue)
{
- userData = userData ?? userDataManager.GetUserData(user.Id, item.GetUserDataKey());
+ userData = userData ?? userDataManager.GetUserData(user, item);
if (userData.IsFavorite != query.IsFavorite.Value)
{
@@ -1249,7 +1249,7 @@ namespace MediaBrowser.Controller.Entities
if (query.IsResumable.HasValue)
{
- userData = userData ?? userDataManager.GetUserData(user.Id, item.GetUserDataKey());
+ userData = userData ?? userDataManager.GetUserData(user, item);
var isResumable = userData.PlaybackPositionTicks > 0;
if (isResumable != query.IsResumable.Value)
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index 2c7d3856b..6a9d7cb51 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -28,7 +28,8 @@ namespace MediaBrowser.Controller.Entities
IThemeMedia,
IArchivable
{
- public Guid? PrimaryVersionId { get; set; }
+ [IgnoreDataMember]
+ public string PrimaryVersionId { get; set; }
public List<string> AdditionalParts { get; set; }
public List<string> LocalAlternateVersions { get; set; }
@@ -44,6 +45,20 @@ namespace MediaBrowser.Controller.Entities
}
}
+ [IgnoreDataMember]
+ public override string PresentationUniqueKey
+ {
+ get
+ {
+ if (!string.IsNullOrWhiteSpace(PrimaryVersionId))
+ {
+ return PrimaryVersionId;
+ }
+
+ return base.PresentationUniqueKey;
+ }
+ }
+
public long? Size { get; set; }
public string Container { get; set; }
public int? TotalBitrate { get; set; }
@@ -56,6 +71,72 @@ namespace MediaBrowser.Controller.Entities
/// <value>The timestamp.</value>
public TransportStreamTimestamp? Timestamp { get; set; }
+ /// <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; }
+
+ public bool IsPlaceHolder { get; set; }
+ public bool IsShortcut { get; set; }
+ public string ShortcutPath { get; set; }
+
+ /// <summary>
+ /// Gets or sets the video bit rate.
+ /// </summary>
+ /// <value>The video bit rate.</value>
+ public int? VideoBitRate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the default index of the video stream.
+ /// </summary>
+ /// <value>The default index of the video stream.</value>
+ public int? DefaultVideoStreamIndex { get; set; }
+
+ /// <summary>
+ /// Gets or sets the type of the video.
+ /// </summary>
+ /// <value>The type of the video.</value>
+ public VideoType VideoType { get; set; }
+
+ /// <summary>
+ /// Gets or sets the type of the iso.
+ /// </summary>
+ /// <value>The type of the iso.</value>
+ public IsoType? IsoType { get; set; }
+
+ /// <summary>
+ /// Gets or sets the video3 D format.
+ /// </summary>
+ /// <value>The video3 D format.</value>
+ public Video3DFormat? Video3DFormat { get; set; }
+
+ /// <summary>
+ /// If the video is a folder-rip, this will hold the file list for the largest playlist
+ /// </summary>
+ public List<string> PlayableStreamFileNames { get; set; }
+
+ /// <summary>
+ /// Gets the playable stream files.
+ /// </summary>
+ /// <returns>List{System.String}.</returns>
+ public List<string> GetPlayableStreamFiles()
+ {
+ return GetPlayableStreamFiles(Path);
+ }
+
+ /// <summary>
+ /// Gets or sets the aspect ratio.
+ /// </summary>
+ /// <value>The aspect ratio.</value>
+ public string AspectRatio { get; set; }
+
public Video()
{
PlayableStreamFileNames = new List<string>();
@@ -90,6 +171,14 @@ namespace MediaBrowser.Controller.Entities
{
get
{
+ if (!string.IsNullOrWhiteSpace(PrimaryVersionId))
+ {
+ var item = LibraryManager.GetItemById(PrimaryVersionId) as Video;
+ if (item != null)
+ {
+ return item.MediaSourceCount;
+ }
+ }
return LinkedAlternateVersions.Count + LocalAlternateVersions.Count + 1;
}
}
@@ -131,42 +220,65 @@ namespace MediaBrowser.Controller.Entities
return LocalAlternateVersions.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
}
- protected override string CreateUserDataKey()
+ [IgnoreDataMember]
+ protected virtual bool EnableDefaultVideoUserDataKeys
{
- if (ExtraType.HasValue)
+ get
{
- var key = this.GetProviderId(MetadataProviders.Imdb) ?? this.GetProviderId(MetadataProviders.Tmdb);
+ return true;
+ }
+ }
- if (!string.IsNullOrWhiteSpace(key))
+ public override List<string> GetUserDataKeys()
+ {
+ var list = base.GetUserDataKeys();
+
+ if (EnableDefaultVideoUserDataKeys)
+ {
+ if (ExtraType.HasValue)
{
- key = key + "-" + ExtraType.ToString().ToLower();
+ var key = this.GetProviderId(MetadataProviders.Tmdb);
+ if (!string.IsNullOrWhiteSpace(key))
+ {
+ list.Insert(0, GetUserDataKey(key));
+ }
- // Make sure different trailers have their own data.
- if (RunTimeTicks.HasValue)
+ key = this.GetProviderId(MetadataProviders.Imdb);
+ if (!string.IsNullOrWhiteSpace(key))
+ {
+ list.Insert(0, GetUserDataKey(key));
+ }
+ }
+ else
+ {
+ var key = this.GetProviderId(MetadataProviders.Imdb);
+ if (!string.IsNullOrWhiteSpace(key))
{
- key += "-" + RunTimeTicks.Value.ToString(CultureInfo.InvariantCulture);
+ list.Insert(0, key);
}
- return key;
+ key = this.GetProviderId(MetadataProviders.Tmdb);
+ if (!string.IsNullOrWhiteSpace(key))
+ {
+ list.Insert(0, key);
+ }
}
}
- return base.CreateUserDataKey();
+ return list;
}
- /// <summary>
- /// Gets the linked children.
- /// </summary>
- /// <returns>IEnumerable{BaseItem}.</returns>
- public IEnumerable<Video> GetAlternateVersions()
+ private string GetUserDataKey(string providerId)
{
- var filesWithinSameDirectory = GetLocalAlternateVersionIds()
- .Select(i => LibraryManager.GetItemById(i))
- .Where(i => i != null)
- .OfType<Video>();
+ var key = providerId + "-" + ExtraType.ToString().ToLower();
- return filesWithinSameDirectory.Concat(GetLinkedAlternateVersions())
- .OrderBy(i => i.SortName);
+ // Make sure different trailers have their own data.
+ if (RunTimeTicks.HasValue)
+ {
+ key += "-" + RunTimeTicks.Value.ToString(CultureInfo.InvariantCulture);
+ }
+
+ return key;
}
public IEnumerable<Video> GetLinkedAlternateVersions()
@@ -193,72 +305,6 @@ namespace MediaBrowser.Controller.Entities
.OrderBy(i => i.SortName);
}
- /// <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; }
-
- public bool IsPlaceHolder { get; set; }
- public bool IsShortcut { get; set; }
- public string ShortcutPath { get; set; }
-
- /// <summary>
- /// Gets or sets the video bit rate.
- /// </summary>
- /// <value>The video bit rate.</value>
- public int? VideoBitRate { get; set; }
-
- /// <summary>
- /// Gets or sets the default index of the video stream.
- /// </summary>
- /// <value>The default index of the video stream.</value>
- public int? DefaultVideoStreamIndex { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the video.
- /// </summary>
- /// <value>The type of the video.</value>
- public VideoType VideoType { get; set; }
-
- /// <summary>
- /// Gets or sets the type of the iso.
- /// </summary>
- /// <value>The type of the iso.</value>
- public IsoType? IsoType { get; set; }
-
- /// <summary>
- /// Gets or sets the video3 D format.
- /// </summary>
- /// <value>The video3 D format.</value>
- public Video3DFormat? Video3DFormat { get; set; }
-
- /// <summary>
- /// If the video is a folder-rip, this will hold the file list for the largest playlist
- /// </summary>
- public List<string> PlayableStreamFileNames { get; set; }
-
- /// <summary>
- /// Gets the playable stream files.
- /// </summary>
- /// <returns>List{System.String}.</returns>
- public List<string> GetPlayableStreamFiles()
- {
- return GetPlayableStreamFiles(Path);
- }
-
- /// <summary>
- /// Gets or sets the aspect ratio.
- /// </summary>
- /// <value>The aspect ratio.</value>
- public string AspectRatio { get; set; }
-
[IgnoreDataMember]
public override string ContainingFolderPath
{
@@ -317,6 +363,11 @@ namespace MediaBrowser.Controller.Entities
{
return false;
}
+
+ if (newAsVideo.VideoType != VideoType)
+ {
+ return false;
+ }
}
return base.IsValidFromResolver(newItem);
@@ -463,6 +514,36 @@ namespace MediaBrowser.Controller.Entities
}).FirstOrDefault();
}
+ private List<Tuple<Video, MediaSourceType>> GetAllVideosForMediaSources()
+ {
+ var list = new List<Tuple<Video, MediaSourceType>>();
+
+ list.Add(new Tuple<Video, MediaSourceType>(this, MediaSourceType.Default));
+ list.AddRange(GetLinkedAlternateVersions().Select(i => new Tuple<Video, MediaSourceType>(i, MediaSourceType.Grouping)));
+
+ if (!string.IsNullOrWhiteSpace(PrimaryVersionId))
+ {
+ var primary = LibraryManager.GetItemById(PrimaryVersionId) as Video;
+ if (primary != null)
+ {
+ var existingIds = list.Select(i => i.Item1.Id).ToList();
+ list.Add(new Tuple<Video, MediaSourceType>(primary, MediaSourceType.Grouping));
+ list.AddRange(primary.GetLinkedAlternateVersions().Where(i => !existingIds.Contains(i.Id)).Select(i => new Tuple<Video, MediaSourceType>(i, MediaSourceType.Grouping)));
+ }
+ }
+
+ var localAlternates = list
+ .SelectMany(i => i.Item1.GetLocalAlternateVersionIds())
+ .Select(LibraryManager.GetItemById)
+ .Where(i => i != null)
+ .OfType<Video>()
+ .ToList();
+
+ list.AddRange(localAlternates.Select(i => new Tuple<Video, MediaSourceType>(i, MediaSourceType.Default)));
+
+ return list;
+ }
+
public virtual IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
{
if (SourceType == SourceType.Channel)
@@ -481,13 +562,8 @@ namespace MediaBrowser.Controller.Entities
};
}
- var item = this;
-
- var result = item.GetAlternateVersions()
- .Select(i => GetVersionInfo(enablePathSubstitution, i, MediaSourceType.Grouping))
- .ToList();
-
- result.Add(GetVersionInfo(enablePathSubstitution, item, MediaSourceType.Default));
+ var list = GetAllVideosForMediaSources();
+ var result = list.Select(i => GetVersionInfo(enablePathSubstitution, i.Item1, i.Item2)).ToList();
return result.OrderBy(i =>
{
diff --git a/MediaBrowser.Controller/Entities/Year.cs b/MediaBrowser.Controller/Entities/Year.cs
index 163dcd667..db896f1fc 100644
--- a/MediaBrowser.Controller/Entities/Year.cs
+++ b/MediaBrowser.Controller/Entities/Year.cs
@@ -11,13 +11,12 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public class Year : BaseItem, IItemByName
{
- /// <summary>
- /// Gets the user data key.
- /// </summary>
- /// <returns>System.String.</returns>
- protected override string CreateUserDataKey()
+ public override List<string> GetUserDataKeys()
{
- return "Year-" + Name;
+ var list = base.GetUserDataKeys();
+
+ list.Insert(0, "Year-" + Name);
+ return list;
}
/// <summary>
@@ -71,6 +70,22 @@ namespace MediaBrowser.Controller.Entities
return inputItems.Where(i => i.ProductionYear.HasValue && i.ProductionYear.Value == year);
}
+ public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query)
+ {
+ int year;
+
+ var usCulture = new CultureInfo("en-US");
+
+ if (!int.TryParse(Name, NumberStyles.Integer, usCulture, out year))
+ {
+ return new List<BaseItem>();
+ }
+
+ query.Years = new[] { year };
+
+ return LibraryManager.GetItemList(query);
+ }
+
public int? GetYearValue()
{
int i;