Merge branch 'dev' into beta

pull/702/head
Luke Pulverenti 9 years ago
commit ffff503e9b

@ -197,12 +197,7 @@ namespace MediaBrowser.Api.Movies
var parentIds = new string[] { }; var parentIds = new string[] { };
var list = _libraryManager.GetItemList(query, parentIds) var list = _libraryManager.GetItemList(query, parentIds)
.Where(i => .DistinctBy(i => i.PresentationUniqueKey, StringComparer.OrdinalIgnoreCase)
{
// Strip out secondary versions
var v = i as Video;
return v != null && !v.PrimaryVersionId.HasValue;
})
.DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N")) .DistinctBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N"))
.ToList(); .ToList();
@ -247,7 +242,7 @@ namespace MediaBrowser.Api.Movies
var recentlyPlayedMovies = allMoviesForCategories var recentlyPlayedMovies = allMoviesForCategories
.Select(i => .Select(i =>
{ {
var userdata = _userDataRepository.GetUserData(user.Id, i.GetUserDataKey()); var userdata = _userDataRepository.GetUserData(user, i);
return new Tuple<BaseItem, bool, DateTime>(i, userdata.Played, userdata.LastPlayedDate ?? DateTime.MinValue); return new Tuple<BaseItem, bool, DateTime>(i, userdata.Played, userdata.LastPlayedDate ?? DateTime.MinValue);
}) })
.Where(i => i.Item2) .Where(i => i.Item2)
@ -260,7 +255,7 @@ namespace MediaBrowser.Api.Movies
.Select(i => .Select(i =>
{ {
var score = 0; var score = 0;
var userData = _userDataRepository.GetUserData(user.Id, i.GetUserDataKey()); var userData = _userDataRepository.GetUserData(user, i);
if (userData.IsFavorite) if (userData.IsFavorite)
{ {

@ -274,7 +274,7 @@ namespace MediaBrowser.Api.UserLibrary
{ {
items = items.Where(i => items = items.Where(i =>
{ {
var userdata = UserDataRepository.GetUserData(user.Id, i.GetUserDataKey()); var userdata = UserDataRepository.GetUserData(user, i);
return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value; return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value;
}); });
@ -284,7 +284,7 @@ namespace MediaBrowser.Api.UserLibrary
{ {
items = items.Where(i => items = items.Where(i =>
{ {
var userdata = UserDataRepository.GetUserData(user.Id, i.GetUserDataKey()); var userdata = UserDataRepository.GetUserData(user, i);
return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value; return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value;
}); });
@ -294,7 +294,7 @@ namespace MediaBrowser.Api.UserLibrary
{ {
items = items.Where(i => items = items.Where(i =>
{ {
var userdata = UserDataRepository.GetUserData(user.Id, i.GetUserDataKey()); var userdata = UserDataRepository.GetUserData(user, i);
var likes = userdata.Likes ?? false; var likes = userdata.Likes ?? false;
var favorite = userdata.IsFavorite; var favorite = userdata.IsFavorite;
@ -307,7 +307,7 @@ namespace MediaBrowser.Api.UserLibrary
{ {
items = items.Where(i => items = items.Where(i =>
{ {
var userdata = UserDataRepository.GetUserData(user.Id, i.GetUserDataKey()); var userdata = UserDataRepository.GetUserData(user, i);
return userdata != null && userdata.IsFavorite; return userdata != null && userdata.IsFavorite;
}); });

@ -519,10 +519,8 @@ namespace MediaBrowser.Api.UserLibrary
var item = string.IsNullOrEmpty(itemId) ? user.RootFolder : _libraryManager.GetItemById(itemId); var item = string.IsNullOrEmpty(itemId) ? user.RootFolder : _libraryManager.GetItemById(itemId);
var key = item.GetUserDataKey();
// Get the user data for this item // Get the user data for this item
var data = _userDataRepository.GetUserData(user.Id, key); var data = _userDataRepository.GetUserData(user, item);
// Set favorite status // Set favorite status
data.IsFavorite = isFavorite; data.IsFavorite = isFavorite;
@ -567,10 +565,8 @@ namespace MediaBrowser.Api.UserLibrary
var item = string.IsNullOrEmpty(itemId) ? user.RootFolder : _libraryManager.GetItemById(itemId); var item = string.IsNullOrEmpty(itemId) ? user.RootFolder : _libraryManager.GetItemById(itemId);
var key = item.GetUserDataKey();
// Get the user data for this item // Get the user data for this item
var data = _userDataRepository.GetUserData(user.Id, key); var data = _userDataRepository.GetUserData(user, item);
data.Likes = likes; data.Likes = likes;

@ -130,6 +130,7 @@ namespace MediaBrowser.Api
var items = request.Ids.Split(',') var items = request.Ids.Split(',')
.Select(i => new Guid(i)) .Select(i => new Guid(i))
.Select(i => _libraryManager.GetItemById(i)) .Select(i => _libraryManager.GetItemById(i))
.OfType<Video>()
.ToList(); .ToList();
if (items.Count < 2) if (items.Count < 2)
@ -137,14 +138,7 @@ namespace MediaBrowser.Api
throw new ArgumentException("Please supply at least two videos to merge."); throw new ArgumentException("Please supply at least two videos to merge.");
} }
if (items.Any(i => !(i is Video))) var videosWithVersions = items.Where(i => i.MediaSourceCount > 1)
{
throw new ArgumentException("Only videos can be grouped together.");
}
var videos = items.Cast<Video>().ToList();
var videosWithVersions = videos.Where(i => i.MediaSourceCount > 1)
.ToList(); .ToList();
if (videosWithVersions.Count > 1) if (videosWithVersions.Count > 1)
@ -156,7 +150,7 @@ namespace MediaBrowser.Api
if (primaryVersion == null) if (primaryVersion == null)
{ {
primaryVersion = videos.OrderBy(i => primaryVersion = items.OrderBy(i =>
{ {
if (i.Video3DFormat.HasValue) if (i.Video3DFormat.HasValue)
{ {
@ -179,7 +173,7 @@ namespace MediaBrowser.Api
}).First(); }).First();
} }
foreach (var item in videos.Where(i => i.Id != primaryVersion.Id)) foreach (var item in items.Where(i => i.Id != primaryVersion.Id))
{ {
item.PrimaryVersionId = primaryVersion.Id; item.PrimaryVersionId = primaryVersion.Id;

@ -26,7 +26,7 @@ namespace MediaBrowser.Controller.Entities.Audio
IArchivable IArchivable
{ {
public List<ChannelMediaInfo> ChannelMediaSources { get; set; } public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
public long? Size { get; set; } public long? Size { get; set; }
public string Container { get; set; } public string Container { get; set; }
public int? TotalBitrate { get; set; } public int? TotalBitrate { get; set; }
@ -150,12 +150,10 @@ namespace MediaBrowser.Controller.Entities.Audio
+ (IndexNumber != null ? IndexNumber.Value.ToString("0000 - ") : "") + Name; + (IndexNumber != null ? IndexNumber.Value.ToString("0000 - ") : "") + Name;
} }
/// <summary> public override List<string> GetUserDataKeys()
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
protected override string CreateUserDataKey()
{ {
var list = base.GetUserDataKeys();
if (ConfigurationManager.Configuration.EnableStandaloneMusicKeys) if (ConfigurationManager.Configuration.EnableStandaloneMusicKeys)
{ {
var songKey = IndexNumber.HasValue ? IndexNumber.Value.ToString("0000") : string.Empty; var songKey = IndexNumber.HasValue ? IndexNumber.Value.ToString("0000") : string.Empty;
@ -165,7 +163,7 @@ namespace MediaBrowser.Controller.Entities.Audio
{ {
songKey = ParentIndexNumber.Value.ToString("0000") + "-" + songKey; songKey = ParentIndexNumber.Value.ToString("0000") + "-" + songKey;
} }
songKey+= Name; songKey += Name;
if (!string.IsNullOrWhiteSpace(Album)) if (!string.IsNullOrWhiteSpace(Album))
{ {
@ -178,25 +176,25 @@ namespace MediaBrowser.Controller.Entities.Audio
songKey = albumArtist + "-" + songKey; songKey = albumArtist + "-" + songKey;
} }
return songKey; list.Insert(0, songKey);
} }
else
var parent = AlbumEntity;
if (parent != null)
{ {
var parentKey = parent.GetUserDataKey(); var parent = AlbumEntity;
if (IndexNumber.HasValue) if (parent != null && IndexNumber.HasValue)
{ {
var songKey = (ParentIndexNumber != null ? ParentIndexNumber.Value.ToString("0000 - ") : "") list.InsertRange(0, parent.GetUserDataKeys().Select(i =>
+ IndexNumber.Value.ToString("0000 - "); {
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() public override UnratedItem GetBlockUnratedType()

@ -96,36 +96,34 @@ namespace MediaBrowser.Controller.Entities.Audio
public List<string> Artists { get; set; } public List<string> Artists { get; set; }
/// <summary> public override List<string> GetUserDataKeys()
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
protected override string CreateUserDataKey()
{ {
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)) 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; list.Insert(0, "MusicAlbum-MusicBrainzReleaseGroup-" + id);
if (!string.IsNullOrWhiteSpace(albumArtist))
{
return albumArtist + "-" + Name;
}
} }
return base.CreateUserDataKey(); return list;
} }
protected override bool GetBlockUnratedValue(UserPolicy config) protected override bool GetBlockUnratedValue(UserPolicy config)

@ -80,13 +80,12 @@ namespace MediaBrowser.Controller.Entities.Audio
ProductionLocations = new List<string>(); ProductionLocations = new List<string>();
} }
/// <summary> public override List<string> GetUserDataKeys()
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
protected override string CreateUserDataKey()
{ {
return GetUserDataKey(this); var list = base.GetUserDataKeys();
list.InsertRange(0, GetUserDataKeys(this));
return list;
} }
/// <summary> /// <summary>
@ -121,16 +120,18 @@ namespace MediaBrowser.Controller.Entities.Audio
/// </summary> /// </summary>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
/// <returns>System.String.</returns> /// <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); var id = item.GetProviderId(MetadataProviders.MusicBrainzArtist);
if (!string.IsNullOrEmpty(id)) 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) protected override bool GetBlockUnratedValue(UserPolicy config)

@ -10,13 +10,12 @@ namespace MediaBrowser.Controller.Entities.Audio
/// </summary> /// </summary>
public class MusicGenre : BaseItem, IItemByName public class MusicGenre : BaseItem, IItemByName
{ {
/// <summary> public override List<string> GetUserDataKeys()
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
protected override string CreateUserDataKey()
{ {
return "MusicGenre-" + Name; var list = base.GetUserDataKeys();
list.Insert(0, "MusicGenre-" + Name);
return list;
} }
[IgnoreDataMember] [IgnoreDataMember]

@ -1149,6 +1149,12 @@ namespace MediaBrowser.Controller.Entities
get { return null; } get { return null; }
} }
[IgnoreDataMember]
public virtual string PresentationUniqueKey
{
get { return Id.ToString("N"); }
}
private string _userDataKey; private string _userDataKey;
/// <summary> /// <summary>
/// Gets the user data key. /// Gets the user data key.
@ -1158,7 +1164,7 @@ namespace MediaBrowser.Controller.Entities
{ {
if (string.IsNullOrWhiteSpace(_userDataKey)) if (string.IsNullOrWhiteSpace(_userDataKey))
{ {
var key = CreateUserDataKey(); var key = GetUserDataKeys().First();
_userDataKey = key; _userDataKey = key;
return key; return key;
} }
@ -1166,16 +1172,20 @@ namespace MediaBrowser.Controller.Entities
return _userDataKey; return _userDataKey;
} }
protected virtual string CreateUserDataKey() public virtual List<string> GetUserDataKeys()
{ {
var list = new List<string>();
if (SourceType == SourceType.Channel) if (SourceType == SourceType.Channel)
{ {
if (!string.IsNullOrWhiteSpace(ExternalId)) if (!string.IsNullOrWhiteSpace(ExternalId))
{ {
return ExternalId; list.Add(ExternalId);
} }
} }
return Id.ToString();
list.Add(Id.ToString());
return list;
} }
internal virtual bool IsValidFromResolver(BaseItem newItem) internal virtual bool IsValidFromResolver(BaseItem newItem)
@ -1617,9 +1627,7 @@ namespace MediaBrowser.Controller.Entities
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
var key = GetUserDataKey(); var data = UserDataManager.GetUserData(user, this);
var data = UserDataManager.GetUserData(user.Id, key);
if (datePlayed.HasValue) if (datePlayed.HasValue)
{ {
@ -1654,9 +1662,7 @@ namespace MediaBrowser.Controller.Entities
throw new ArgumentNullException(); throw new ArgumentNullException();
} }
var key = GetUserDataKey(); var data = UserDataManager.GetUserData(user, this);
var data = UserDataManager.GetUserData(user.Id, key);
//I think it is okay to do this here. //I think it is okay to do this here.
// if this is only called when a user is manually forcing something to un-played // if this is only called when a user is manually forcing something to un-played
@ -1987,14 +1993,14 @@ namespace MediaBrowser.Controller.Entities
public virtual bool IsPlayed(User user) public virtual bool IsPlayed(User user)
{ {
var userdata = UserDataManager.GetUserData(user.Id, GetUserDataKey()); var userdata = UserDataManager.GetUserData(user, this);
return userdata != null && userdata.Played; return userdata != null && userdata.Played;
} }
public bool IsFavoriteOrLiked(User user) 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)); return userdata != null && (userdata.IsFavorite || (userdata.Likes ?? false));
} }
@ -2006,7 +2012,7 @@ namespace MediaBrowser.Controller.Entities
throw new ArgumentNullException("user"); throw new ArgumentNullException("user");
} }
var userdata = UserDataManager.GetUserData(user.Id, GetUserDataKey()); var userdata = UserDataManager.GetUserData(user, this);
return userdata == null || !userdata.Played; return userdata == null || !userdata.Played;
} }

@ -839,11 +839,6 @@ namespace MediaBrowser.Controller.Entities
Logger.Debug("Query requires post-filtering due to ItemSortBy.SeriesSortName"); Logger.Debug("Query requires post-filtering due to ItemSortBy.SeriesSortName");
return true; 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)) if (query.SortBy.Contains(ItemSortBy.Studio, StringComparer.OrdinalIgnoreCase))
{ {
Logger.Debug("Query requires post-filtering due to ItemSortBy.Studio"); Logger.Debug("Query requires post-filtering due to ItemSortBy.Studio");
@ -1059,30 +1054,6 @@ namespace MediaBrowser.Controller.Entities
return true; 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) if (query.AirDays.Length > 0)
{ {
Logger.Debug("Query requires post-filtering due to AirDays"); Logger.Debug("Query requires post-filtering due to AirDays");
@ -1635,7 +1606,7 @@ namespace MediaBrowser.Controller.Entities
var isUnplayed = true; var isUnplayed = true;
var itemUserData = UserDataManager.GetUserData(user.Id, child.GetUserDataKey()); var itemUserData = UserDataManager.GetUserData(user, child);
// Incrememt totalPercentPlayed // Incrememt totalPercentPlayed
if (itemUserData != null) if (itemUserData != null)

@ -76,15 +76,16 @@ namespace MediaBrowser.Controller.Entities
/// </summary> /// </summary>
public List<string> MultiPartGameFiles { get; set; } 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); var id = this.GetProviderId(MetadataProviders.Gamesdb);
if (!string.IsNullOrEmpty(id)) if (!string.IsNullOrEmpty(id))
{ {
return "Game-Gamesdb-" + id; list.Insert(0, "Game-Gamesdb-" + id);
} }
return base.CreateUserDataKey(); return list;
} }
public override IEnumerable<string> GetDeletePaths() public override IEnumerable<string> GetDeletePaths()

@ -7,13 +7,12 @@ namespace MediaBrowser.Controller.Entities
{ {
public class GameGenre : BaseItem, IItemByName public class GameGenre : BaseItem, IItemByName
{ {
/// <summary> public override List<string> GetUserDataKeys()
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
protected override string CreateUserDataKey()
{ {
return "GameGenre-" + Name; var list = base.GetUserDataKeys();
list.Insert(0, "GameGenre-" + Name);
return list;
} }
/// <summary> /// <summary>

@ -2,6 +2,7 @@
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using System; using System;
using System.Collections.Generic;
using MediaBrowser.Model.Users; using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities namespace MediaBrowser.Controller.Entities
@ -31,17 +32,15 @@ namespace MediaBrowser.Controller.Entities
/// <value>The game system.</value> /// <value>The game system.</value>
public string GameSystemName { get; set; } public string GameSystemName { get; set; }
/// <summary> public override List<string> GetUserDataKeys()
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
protected override string CreateUserDataKey()
{ {
var list = base.GetUserDataKeys();
if (!string.IsNullOrEmpty(GameSystemName)) if (!string.IsNullOrEmpty(GameSystemName))
{ {
return "GameSystem-" + GameSystemName; list.Insert(0, "GameSystem-" + GameSystemName);
} }
return base.CreateUserDataKey(); return list;
} }
protected override bool GetBlockUnratedValue(UserPolicy config) protected override bool GetBlockUnratedValue(UserPolicy config)

@ -11,13 +11,12 @@ namespace MediaBrowser.Controller.Entities
/// </summary> /// </summary>
public class Genre : BaseItem, IItemByName public class Genre : BaseItem, IItemByName
{ {
/// <summary> public override List<string> GetUserDataKeys()
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
protected override string CreateUserDataKey()
{ {
return "Genre-" + Name; var list = base.GetUserDataKeys();
list.Insert(0, "Genre-" + Name);
return list;
} }
/// <summary> /// <summary>

@ -1,4 +1,5 @@
using MediaBrowser.Model.Dto; using System.Collections.Generic;
using MediaBrowser.Model.Dto;
namespace MediaBrowser.Controller.Entities namespace MediaBrowser.Controller.Entities
{ {
@ -13,6 +14,8 @@ namespace MediaBrowser.Controller.Entities
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
string GetUserDataKey(); string GetUserDataKey();
List<string> GetUserDataKeys();
/// <summary> /// <summary>
/// Fills the user data dto values. /// Fills the user data dto values.
/// </summary> /// </summary>

@ -46,6 +46,7 @@ namespace MediaBrowser.Controller.Entities
public string NameLessThan { get; set; } public string NameLessThan { get; set; }
public string NameContains { get; set; } public string NameContains { get; set; }
public string PresentationUniqueKey { get; set; }
public string Path { get; set; } public string Path { get; set; }
public string Person { get; set; } public string Person { get; set; }

@ -75,34 +75,6 @@ namespace MediaBrowser.Controller.Entities.Movies
get { return TmdbCollectionName; } get { return TmdbCollectionName; }
set { TmdbCollectionName = value; } 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) protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
{ {

@ -44,15 +44,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() public override UnratedItem GetBlockUnratedType()
{ {
return UnratedItem.Music; return UnratedItem.Music;

@ -18,13 +18,12 @@ namespace MediaBrowser.Controller.Entities
/// <value>The place of birth.</value> /// <value>The place of birth.</value>
public string PlaceOfBirth { get; set; } public string PlaceOfBirth { get; set; }
/// <summary> public override List<string> GetUserDataKeys()
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
protected override string CreateUserDataKey()
{ {
return "Person-" + Name; var list = base.GetUserDataKeys();
list.Insert(0, "Person-" + Name);
return list;
} }
public PersonLookupInfo GetLookupInfo() public PersonLookupInfo GetLookupInfo()

@ -10,13 +10,12 @@ namespace MediaBrowser.Controller.Entities
/// </summary> /// </summary>
public class Studio : BaseItem, IItemByName, IHasTags public class Studio : BaseItem, IItemByName, IHasTags
{ {
/// <summary> public override List<string> GetUserDataKeys()
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
protected override string CreateUserDataKey()
{ {
return "Studio-" + Name; var list = base.GetUserDataKeys();
list.Insert(0, "Studio-" + Name);
return list;
} }
/// <summary> /// <summary>

@ -58,60 +58,48 @@ namespace MediaBrowser.Controller.Entities.TV
{ {
get get
{ {
return AirsAfterSeasonNumber ?? AirsBeforeSeasonNumber ?? PhysicalSeasonNumber; return AirsAfterSeasonNumber ?? AirsBeforeSeasonNumber ?? ParentIndexNumber;
} }
} }
[IgnoreDataMember] [IgnoreDataMember]
public int? PhysicalSeasonNumber public override Folder LatestItemsIndexContainer
{ {
get get
{ {
var value = ParentIndexNumber; return Series;
if (value.HasValue)
{
return value;
}
var season = Season;
return season != null ? season.IndexNumber : null;
} }
} }
[IgnoreDataMember] [IgnoreDataMember]
public override Folder LatestItemsIndexContainer public override Guid? DisplayParentId
{ {
get get
{ {
return Series; return SeasonId;
} }
} }
[IgnoreDataMember] [IgnoreDataMember]
public override Guid? DisplayParentId protected override bool EnableDefaultVideoUserDataKeys
{ {
get get
{ {
return SeasonId; return false;
} }
} }
/// <summary> public override List<string> GetUserDataKeys()
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
protected override string CreateUserDataKey()
{ {
var series = Series; var list = base.GetUserDataKeys();
var series = Series;
if (series != null && ParentIndexNumber.HasValue && IndexNumber.HasValue) 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> /// <summary>
@ -310,6 +298,19 @@ namespace MediaBrowser.Controller.Entities.TV
Logger.ErrorException("Error in FillMissingEpisodeNumbersFromPath. Episode: {0}", ex, Path ?? Name ?? Id.ToString()); 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; return hasChanges;
} }
} }

@ -53,19 +53,17 @@ namespace MediaBrowser.Controller.Entities.TV
}; };
} }
/// <summary> public override List<string> GetUserDataKeys()
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
protected override string CreateUserDataKey()
{ {
if (Series != null) var list = base.GetUserDataKeys();
var series = Series;
if (series != null)
{ {
var seasonNo = IndexNumber ?? 0; list.InsertRange(0, series.GetUserDataKeys().Select(i => i + (IndexNumber ?? 0).ToString("000")));
return Series.GetUserDataKey() + seasonNo.ToString("000");
} }
return base.CreateUserDataKey(); return list;
} }
/// <summary> /// <summary>
@ -94,6 +92,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> /// <summary>
/// Creates the name of the sort. /// Creates the name of the sort.
/// </summary> /// </summary>
@ -171,16 +187,16 @@ namespace MediaBrowser.Controller.Entities.TV
public IEnumerable<Episode> GetEpisodes(User user, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes) public IEnumerable<Episode> GetEpisodes(User user, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes)
{ {
var episodes = GetRecursiveChildren(user)
.OfType<Episode>();
var series = Series; var series = Series;
if (IndexNumber.HasValue && series != null) 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) if (series != null && series.ContainsEpisodesWithoutSeasonFolders)
{ {
var seasonNumber = IndexNumber; var seasonNumber = IndexNumber;

@ -91,25 +91,33 @@ namespace MediaBrowser.Controller.Entities.TV
} }
} }
[IgnoreDataMember]
public override string PresentationUniqueKey
{
get { return GetUserDataKeys().First(); }
}
/// <summary> /// <summary>
/// Gets the user data key. /// Gets the user data key.
/// </summary> /// </summary>
/// <returns>System.String.</returns> /// <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> /// <summary>
@ -126,8 +134,8 @@ namespace MediaBrowser.Controller.Entities.TV
// Studio, Genre and Rating will all be the same so makes no sense to index by these // Studio, Genre and Rating will all be the same so makes no sense to index by these
protected override IEnumerable<string> GetIndexByOptions() protected override IEnumerable<string> GetIndexByOptions()
{ {
return new List<string> { return new List<string> {
{"None"}, {"None"},
{"Performer"}, {"Performer"},
{"Director"}, {"Director"},
{"Year"}, {"Year"},
@ -183,8 +191,28 @@ namespace MediaBrowser.Controller.Entities.TV
public IEnumerable<Season> GetSeasons(User user, bool includeMissingSeasons, bool includeVirtualUnaired) public IEnumerable<Season> GetSeasons(User user, bool includeMissingSeasons, bool includeVirtualUnaired)
{ {
var seasons = base.GetChildren(user, true) var seriesIds = LibraryManager.GetItemIds(new InternalItemsQuery(user)
.OfType<Season>(); {
PresentationUniqueKey = PresentationUniqueKey,
IncludeItemTypes = new[] { typeof(Series).Name }
});
IEnumerable<Season> seasons;
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 }
}).OfType<Season>();
}
else
{
seasons = base.GetChildren(user, true).OfType<Season>();
}
if (!includeMissingSeasons && !includeVirtualUnaired) if (!includeMissingSeasons && !includeVirtualUnaired)
{ {
@ -202,9 +230,7 @@ namespace MediaBrowser.Controller.Entities.TV
} }
} }
return LibraryManager return seasons;
.Sort(seasons, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending)
.Cast<Season>();
} }
public IEnumerable<Episode> GetEpisodes(User user) public IEnumerable<Episode> GetEpisodes(User user)
@ -280,9 +306,9 @@ namespace MediaBrowser.Controller.Entities.TV
if (episode != null if (episode != null
&& refreshOptions.MetadataRefreshMode != MetadataRefreshMode.FullRefresh && refreshOptions.MetadataRefreshMode != MetadataRefreshMode.FullRefresh
&& !refreshOptions.ReplaceAllMetadata && !refreshOptions.ReplaceAllMetadata
&& episode.IsMissingEpisode && episode.IsMissingEpisode
&& episode.LocationType == Model.Entities.LocationType.Virtual && episode.LocationType == Model.Entities.LocationType.Virtual
&& episode.PremiereDate.HasValue && episode.PremiereDate.HasValue
&& (DateTime.UtcNow - episode.PremiereDate.Value).TotalDays > 30) && (DateTime.UtcNow - episode.PremiereDate.Value).TotalDays > 30)
{ {
skipItem = true; skipItem = true;
@ -313,18 +339,31 @@ namespace MediaBrowser.Controller.Entities.TV
public IEnumerable<Episode> GetEpisodes(User user, int seasonNumber, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes) public IEnumerable<Episode> GetEpisodes(User user, int seasonNumber, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes)
{ {
return GetEpisodes(user, seasonNumber, includeMissingEpisodes, includeVirtualUnairedEpisodes, var seriesIds = LibraryManager.GetItemIds(new InternalItemsQuery(user)
new List<Episode>()); {
} PresentationUniqueKey = PresentationUniqueKey,
IncludeItemTypes = new[] { typeof(Series).Name }
});
internal IEnumerable<Episode> GetEpisodes(User user, int seasonNumber, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes, IEnumerable<Episode> additionalEpisodes) IEnumerable<Episode> episodes;
{
var episodes = GetRecursiveChildren(user, i => i is Episode)
.Cast<Episode>();
episodes = FilterEpisodesBySeason(episodes, seasonNumber, DisplaySpecialsWithSeasons); 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 }
episodes = episodes.Concat(additionalEpisodes).Distinct(); }).OfType<Episode>();
}
else
{
episodes = GetRecursiveChildren(user, i => i is Episode)
.Cast<Episode>();
}
episodes = FilterEpisodesBySeason(episodes, seasonNumber, DisplaySpecialsWithSeasons);
if (!includeMissingEpisodes) if (!includeMissingEpisodes)
{ {
@ -352,7 +391,7 @@ namespace MediaBrowser.Controller.Entities.TV
{ {
if (!includeSpecials || seasonNumber < 1) if (!includeSpecials || seasonNumber < 1)
{ {
return episodes.Where(i => (i.PhysicalSeasonNumber ?? -1) == seasonNumber); return episodes.Where(i => (i.ParentIndexNumber ?? -1) == seasonNumber);
} }
return episodes.Where(i => return episodes.Where(i =>

@ -56,26 +56,6 @@ namespace MediaBrowser.Controller.Entities
/// <value>The revenue.</value> /// <value>The revenue.</value>
public double? Revenue { get; set; } 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() public override UnratedItem GetBlockUnratedType()
{ {
return UnratedItem.Trailer; return UnratedItem.Trailer;

@ -348,7 +348,7 @@ namespace MediaBrowser.Controller.Entities
.Where(i => !i.IsFolder) .Where(i => !i.IsFolder)
.OfType<IHasAlbumArtist>(); .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); return GetResult(artists, parent, query);
} }
@ -1218,7 +1218,7 @@ namespace MediaBrowser.Controller.Entities
if (query.IsLiked.HasValue) 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) if (!userData.Likes.HasValue || userData.Likes != query.IsLiked.Value)
{ {
@ -1228,7 +1228,7 @@ namespace MediaBrowser.Controller.Entities
if (query.IsFavoriteOrLiked.HasValue) 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); var isFavoriteOrLiked = userData.IsFavorite || (userData.Likes ?? false);
if (isFavoriteOrLiked != query.IsFavoriteOrLiked.Value) if (isFavoriteOrLiked != query.IsFavoriteOrLiked.Value)
@ -1239,7 +1239,7 @@ namespace MediaBrowser.Controller.Entities
if (query.IsFavorite.HasValue) if (query.IsFavorite.HasValue)
{ {
userData = userData ?? userDataManager.GetUserData(user.Id, item.GetUserDataKey()); userData = userData ?? userDataManager.GetUserData(user, item);
if (userData.IsFavorite != query.IsFavorite.Value) if (userData.IsFavorite != query.IsFavorite.Value)
{ {
@ -1249,7 +1249,7 @@ namespace MediaBrowser.Controller.Entities
if (query.IsResumable.HasValue) if (query.IsResumable.HasValue)
{ {
userData = userData ?? userDataManager.GetUserData(user.Id, item.GetUserDataKey()); userData = userData ?? userDataManager.GetUserData(user, item);
var isResumable = userData.PlaybackPositionTicks > 0; var isResumable = userData.PlaybackPositionTicks > 0;
if (isResumable != query.IsResumable.Value) if (isResumable != query.IsResumable.Value)

@ -44,6 +44,20 @@ namespace MediaBrowser.Controller.Entities
} }
} }
[IgnoreDataMember]
public override string PresentationUniqueKey
{
get
{
if (PrimaryVersionId.HasValue)
{
return PrimaryVersionId.Value.ToString("N");
}
return base.PresentationUniqueKey;
}
}
public long? Size { get; set; } public long? Size { get; set; }
public string Container { get; set; } public string Container { get; set; }
public int? TotalBitrate { get; set; } public int? TotalBitrate { get; set; }
@ -90,6 +104,14 @@ namespace MediaBrowser.Controller.Entities
{ {
get get
{ {
if (PrimaryVersionId.HasValue)
{
var item = LibraryManager.GetItemById(PrimaryVersionId.Value) as Video;
if (item != null)
{
return item.MediaSourceCount;
}
}
return LinkedAlternateVersions.Count + LocalAlternateVersions.Count + 1; return LinkedAlternateVersions.Count + LocalAlternateVersions.Count + 1;
} }
} }
@ -131,42 +153,65 @@ namespace MediaBrowser.Controller.Entities
return LocalAlternateVersions.Select(i => LibraryManager.GetNewItemId(i, typeof(Video))); 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. key = this.GetProviderId(MetadataProviders.Imdb);
if (RunTimeTicks.HasValue) if (!string.IsNullOrWhiteSpace(key))
{ {
key += "-" + RunTimeTicks.Value.ToString(CultureInfo.InvariantCulture); list.Insert(0, GetUserDataKey(key));
}
}
else
{
var key = this.GetProviderId(MetadataProviders.Imdb);
if (!string.IsNullOrWhiteSpace(key))
{
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> private string GetUserDataKey(string providerId)
/// Gets the linked children.
/// </summary>
/// <returns>IEnumerable{BaseItem}.</returns>
public IEnumerable<Video> GetAlternateVersions()
{ {
var filesWithinSameDirectory = GetLocalAlternateVersionIds() var key = providerId + "-" + ExtraType.ToString().ToLower();
.Select(i => LibraryManager.GetItemById(i))
.Where(i => i != null)
.OfType<Video>();
return filesWithinSameDirectory.Concat(GetLinkedAlternateVersions()) // Make sure different trailers have their own data.
.OrderBy(i => i.SortName); if (RunTimeTicks.HasValue)
{
key += "-" + RunTimeTicks.Value.ToString(CultureInfo.InvariantCulture);
}
return key;
} }
public IEnumerable<Video> GetLinkedAlternateVersions() public IEnumerable<Video> GetLinkedAlternateVersions()
@ -468,6 +513,36 @@ namespace MediaBrowser.Controller.Entities
}).FirstOrDefault(); }).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 (PrimaryVersionId.HasValue)
{
var primary = LibraryManager.GetItemById(PrimaryVersionId.Value) 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) public virtual IEnumerable<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
{ {
if (SourceType == SourceType.Channel) if (SourceType == SourceType.Channel)
@ -486,13 +561,8 @@ namespace MediaBrowser.Controller.Entities
}; };
} }
var item = this; var list = GetAllVideosForMediaSources();
var result = list.Select(i => GetVersionInfo(enablePathSubstitution, i.Item1, i.Item2)).ToList();
var result = item.GetAlternateVersions()
.Select(i => GetVersionInfo(enablePathSubstitution, i, MediaSourceType.Grouping))
.ToList();
result.Add(GetVersionInfo(enablePathSubstitution, item, MediaSourceType.Default));
return result.OrderBy(i => return result.OrderBy(i =>
{ {

@ -11,13 +11,12 @@ namespace MediaBrowser.Controller.Entities
/// </summary> /// </summary>
public class Year : BaseItem, IItemByName public class Year : BaseItem, IItemByName
{ {
/// <summary> public override List<string> GetUserDataKeys()
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
protected override string CreateUserDataKey()
{ {
return "Year-" + Name; var list = base.GetUserDataKeys();
list.Insert(0, "Year-" + Name);
return list;
} }
/// <summary> /// <summary>

@ -45,6 +45,11 @@ namespace MediaBrowser.Controller.Library
/// <returns>Task{UserItemData}.</returns> /// <returns>Task{UserItemData}.</returns>
UserItemData GetUserData(Guid userId, string key); UserItemData GetUserData(Guid userId, string key);
UserItemData GetUserData(IHasUserData user, IHasUserData item);
UserItemData GetUserData(string userId, IHasUserData item);
UserItemData GetUserData(Guid userId, IHasUserData item);
/// <summary> /// <summary>
/// Gets the user data dto. /// Gets the user data dto.
/// </summary> /// </summary>

@ -1,6 +1,7 @@
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using System; using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Library namespace MediaBrowser.Controller.Library
{ {
@ -15,11 +16,7 @@ namespace MediaBrowser.Controller.Library
/// <value>The user id.</value> /// <value>The user id.</value>
public Guid UserId { get; set; } public Guid UserId { get; set; }
/// <summary> public List<string> Keys { get; set; }
/// Gets or sets the key.
/// </summary>
/// <value>The key.</value>
public string Key { get; set; }
/// <summary> /// <summary>
/// Gets or sets the save reason. /// Gets or sets the save reason.

@ -45,17 +45,6 @@ namespace MediaBrowser.Controller.LiveTv
set { } set { }
} }
/// <summary>
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
protected override string CreateUserDataKey()
{
var name = GetClientTypeName();
return name + "-" + Name + (EpisodeTitle ?? string.Empty);
}
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is owned item. /// Gets a value indicating whether this instance is owned item.
/// </summary> /// </summary>

@ -11,13 +11,12 @@ namespace MediaBrowser.Controller.LiveTv
{ {
public class LiveTvChannel : BaseItem, IHasMediaSources public class LiveTvChannel : BaseItem, IHasMediaSources
{ {
/// <summary> public override List<string> GetUserDataKeys()
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
protected override string CreateUserDataKey()
{ {
return GetClientTypeName() + "-" + Name; var list = base.GetUserDataKeys();
list.Insert(0, GetClientTypeName() + "-" + Name);
return list;
} }
public override UnratedItem GetBlockUnratedType() public override UnratedItem GetBlockUnratedType()

@ -4,36 +4,40 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.LiveTv;
using System; using System;
using System.Collections.Generic;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Controller.LiveTv namespace MediaBrowser.Controller.LiveTv
{ {
public class LiveTvProgram : BaseItem, IHasLookupInfo<LiveTvProgramLookupInfo>, IHasStartDate, IHasProgramAttributes public class LiveTvProgram : BaseItem, IHasLookupInfo<LiveTvProgramLookupInfo>, IHasStartDate, IHasProgramAttributes
{ {
/// <summary> public override List<string> GetUserDataKeys()
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
protected override string CreateUserDataKey()
{ {
if (IsMovie) var list = base.GetUserDataKeys();
if (!IsSeries)
{ {
var key = Movie.GetMovieUserDataKey(this); var key = this.GetProviderId(MetadataProviders.Imdb);
if (!string.IsNullOrWhiteSpace(key))
{
list.Insert(0, key);
}
key = this.GetProviderId(MetadataProviders.Tmdb);
if (!string.IsNullOrWhiteSpace(key)) if (!string.IsNullOrWhiteSpace(key))
{ {
return key; list.Insert(0, key);
} }
} }
else if (!string.IsNullOrWhiteSpace(EpisodeTitle))
if (IsSeries && !string.IsNullOrWhiteSpace(EpisodeTitle))
{ {
var name = GetClientTypeName(); var name = GetClientTypeName();
return name + "-" + Name + (EpisodeTitle ?? string.Empty); list.Insert(0, name + "-" + Name + (EpisodeTitle ?? string.Empty));
} }
return base.CreateUserDataKey(); return list;
} }
[IgnoreDataMember] [IgnoreDataMember]

@ -45,32 +45,6 @@ namespace MediaBrowser.Controller.LiveTv
set { } set { }
} }
/// <summary>
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
protected override string CreateUserDataKey()
{
if (IsMovie)
{
var key = Movie.GetMovieUserDataKey(this);
if (!string.IsNullOrWhiteSpace(key))
{
return key;
}
}
if (IsSeries && !string.IsNullOrWhiteSpace(EpisodeTitle))
{
var name = GetClientTypeName();
return name + "-" + Name + (EpisodeTitle ?? string.Empty);
}
return base.CreateUserDataKey();
}
[IgnoreDataMember] [IgnoreDataMember]
public override string MediaType public override string MediaType
{ {

@ -19,12 +19,6 @@ namespace MediaBrowser.Controller.Notifications
/// Occurs when [notifications marked read]. /// Occurs when [notifications marked read].
/// </summary> /// </summary>
event EventHandler<NotificationReadEventArgs> NotificationsMarkedRead; event EventHandler<NotificationReadEventArgs> NotificationsMarkedRead;
/// <summary>
/// Opens the connection to the repository
/// </summary>
/// <returns>Task.</returns>
Task Initialize();
/// <summary> /// <summary>
/// Gets the notifications. /// Gets the notifications.

@ -11,12 +11,6 @@ namespace MediaBrowser.Controller.Persistence
/// </summary> /// </summary>
public interface IDisplayPreferencesRepository : IRepository public interface IDisplayPreferencesRepository : IRepository
{ {
/// <summary>
/// Opens the connection to the repository
/// </summary>
/// <returns>Task.</returns>
Task Initialize();
/// <summary> /// <summary>
/// Saves display preferences for an item /// Saves display preferences for an item
/// </summary> /// </summary>

@ -13,12 +13,6 @@ namespace MediaBrowser.Controller.Persistence
/// </summary> /// </summary>
public interface IItemRepository : IRepository public interface IItemRepository : IRepository
{ {
/// <summary>
/// Opens the connection to the repository
/// </summary>
/// <returns>Task.</returns>
Task Initialize();
/// <summary> /// <summary>
/// Saves an item /// Saves an item
/// </summary> /// </summary>

@ -11,12 +11,6 @@ namespace MediaBrowser.Controller.Persistence
/// </summary> /// </summary>
public interface IUserDataRepository : IRepository public interface IUserDataRepository : IRepository
{ {
/// <summary>
/// Opens the connection to the repository
/// </summary>
/// <returns>Task.</returns>
Task Initialize();
/// <summary> /// <summary>
/// Saves the user data. /// Saves the user data.
/// </summary> /// </summary>

@ -21,11 +21,5 @@ namespace MediaBrowser.Controller.Providers
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task SaveMetadataStatus(MetadataStatus status, CancellationToken cancellationToken); Task SaveMetadataStatus(MetadataStatus status, CancellationToken cancellationToken);
/// <summary>
/// Initializes this instance.
/// </summary>
/// <returns>Task.</returns>
Task Initialize();
} }
} }

@ -111,7 +111,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
var newbookmark = int.Parse(sparams["PosSecond"], _usCulture); var newbookmark = int.Parse(sparams["PosSecond"], _usCulture);
var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey()); var userdata = _userDataManager.GetUserData(user, item);
userdata.PlaybackPositionTicks = TimeSpan.FromSeconds(newbookmark).Ticks; userdata.PlaybackPositionTicks = TimeSpan.FromSeconds(newbookmark).Ticks;

@ -201,6 +201,7 @@ namespace MediaBrowser.Model.Configuration
public string[] Migrations { get; set; } public string[] Migrations { get; set; }
public int MigrationVersion { get; set; } public int MigrationVersion { get; set; }
public int SchemaVersion { get; set; }
public bool DownloadImagesInAdvance { get; set; } public bool DownloadImagesInAdvance { get; set; }

@ -444,7 +444,11 @@ namespace MediaBrowser.Providers.MediaInfo
{ {
if (string.IsNullOrWhiteSpace(video.Name) || string.Equals(video.Name, Path.GetFileNameWithoutExtension(video.Path), StringComparison.OrdinalIgnoreCase)) if (string.IsNullOrWhiteSpace(video.Name) || string.Equals(video.Name, Path.GetFileNameWithoutExtension(video.Path), StringComparison.OrdinalIgnoreCase))
{ {
video.Name = data.Name; // Don't use the embedded name for extras because it will often be the same name as the movie
if (!video.ExtraType.HasValue && !video.IsOwnedItem)
{
video.Name = data.Name;
}
} }
} }

@ -27,11 +27,11 @@ namespace MediaBrowser.Server.Implementations.Activity
_appPaths = appPaths; _appPaths = appPaths;
} }
public async Task Initialize() public async Task Initialize(IDbConnector dbConnector)
{ {
var dbFile = Path.Combine(_appPaths.DataPath, "activitylog.db"); var dbFile = Path.Combine(_appPaths.DataPath, "activitylog.db");
_connection = await SqliteExtensions.ConnectToDb(dbFile, Logger).ConfigureAwait(false); _connection = await dbConnector.Connect(dbFile).ConfigureAwait(false);
string[] queries = { string[] queries = {

@ -133,7 +133,7 @@ namespace MediaBrowser.Server.Implementations.Channels
if (query.IsFavorite.HasValue) if (query.IsFavorite.HasValue)
{ {
var val = query.IsFavorite.Value; var val = query.IsFavorite.Value;
channels = channels.Where(i => _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).IsFavorite == val) channels = channels.Where(i => _userDataManager.GetUserData(user, i).IsFavorite == val)
.ToList(); .ToList();
} }
@ -1437,7 +1437,7 @@ namespace MediaBrowser.Server.Implementations.Channels
case ItemFilter.IsFavoriteOrLikes: case ItemFilter.IsFavoriteOrLikes:
return items.Where(item => return items.Where(item =>
{ {
var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey()); var userdata = _userDataManager.GetUserData(user, item);
if (userdata == null) if (userdata == null)
{ {
@ -1453,7 +1453,7 @@ namespace MediaBrowser.Server.Implementations.Channels
case ItemFilter.Likes: case ItemFilter.Likes:
return items.Where(item => return items.Where(item =>
{ {
var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey()); var userdata = _userDataManager.GetUserData(user, item);
return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value; return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value;
}); });
@ -1461,7 +1461,7 @@ namespace MediaBrowser.Server.Implementations.Channels
case ItemFilter.Dislikes: case ItemFilter.Dislikes:
return items.Where(item => return items.Where(item =>
{ {
var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey()); var userdata = _userDataManager.GetUserData(user, item);
return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value; return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value;
}); });
@ -1469,7 +1469,7 @@ namespace MediaBrowser.Server.Implementations.Channels
case ItemFilter.IsFavorite: case ItemFilter.IsFavorite:
return items.Where(item => return items.Where(item =>
{ {
var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey()); var userdata = _userDataManager.GetUserData(user, item);
return userdata != null && userdata.IsFavorite; return userdata != null && userdata.IsFavorite;
}); });
@ -1477,7 +1477,7 @@ namespace MediaBrowser.Server.Implementations.Channels
case ItemFilter.IsResumable: case ItemFilter.IsResumable:
return items.Where(item => return items.Where(item =>
{ {
var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey()); var userdata = _userDataManager.GetUserData(user, item);
return userdata != null && userdata.PlaybackPositionTicks > 0; return userdata != null && userdata.PlaybackPositionTicks > 0;
}); });

@ -487,7 +487,7 @@ namespace MediaBrowser.Server.Implementations.Dto
{ {
if (item.IsFolder) if (item.IsFolder)
{ {
var userData = _userDataRepository.GetUserData(user.Id, item.GetUserDataKey()); var userData = _userDataRepository.GetUserData(user, item);
// Skip the user data manager because we've already looped through the recursive tree and don't want to do it twice // Skip the user data manager because we've already looped through the recursive tree and don't want to do it twice
// TODO: Improve in future // TODO: Improve in future
@ -1363,9 +1363,10 @@ namespace MediaBrowser.Server.Implementations.Dto
if (fields.Contains(ItemFields.MediaSourceCount)) if (fields.Contains(ItemFields.MediaSourceCount))
{ {
if (video.MediaSourceCount != 1) var mediaSourceCount = video.MediaSourceCount;
if (mediaSourceCount != 1)
{ {
dto.MediaSourceCount = video.MediaSourceCount; dto.MediaSourceCount = mediaSourceCount;
} }
} }
@ -1686,7 +1687,7 @@ namespace MediaBrowser.Server.Implementations.Dto
dateLastMediaAdded = new[] { dateLastMediaAdded.Value, child.DateCreated }.Max(); dateLastMediaAdded = new[] { dateLastMediaAdded.Value, child.DateCreated }.Max();
} }
var userdata = _userDataRepository.GetUserData(user.Id, child.GetUserDataKey()); var userdata = _userDataRepository.GetUserData(user, child);
recursiveItemCount++; recursiveItemCount++;

@ -522,29 +522,7 @@ namespace MediaBrowser.Server.Implementations.Library
throw new ArgumentNullException("items"); throw new ArgumentNullException("items");
} }
var dict = new Dictionary<Guid, BaseItem>(); return items.DistinctBy(i => i.PresentationUniqueKey, StringComparer.OrdinalIgnoreCase);
foreach (var item in items)
{
var video = item as Video;
if (video != null)
{
if (video.PrimaryVersionId.HasValue)
{
var primary = GetItemById(video.PrimaryVersionId.Value) as Video;
if (primary != null)
{
dict[primary.Id] = primary;
continue;
}
}
}
dict[item.Id] = item;
}
return dict.Values;
} }
/// <summary> /// <summary>

@ -267,7 +267,7 @@ namespace MediaBrowser.Server.Implementations.Library
private void SetUserProperties(IHasUserData item, MediaSourceInfo source, User user) private void SetUserProperties(IHasUserData item, MediaSourceInfo source, User user)
{ {
var userData = item == null ? new UserItemData() : _userDataManager.GetUserData(user.Id, item.GetUserDataKey()); var userData = item == null ? new UserItemData() : _userDataManager.GetUserData(user, item);
var allowRememberingSelection = item == null || item.EnableRememberingTrackSelections; var allowRememberingSelection = item == null || item.EnableRememberingTrackSelections;

@ -56,27 +56,30 @@ namespace MediaBrowser.Server.Implementations.Library
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
var key = item.GetUserDataKey(); var keys = item.GetUserDataKeys();
try foreach (var key in keys)
{ {
await Repository.SaveUserData(userId, key, userData, cancellationToken).ConfigureAwait(false); try
{
await Repository.SaveUserData(userId, key, userData, cancellationToken).ConfigureAwait(false);
var newValue = userData; var newValue = userData;
// Once it succeeds, put it into the dictionary to make it available to everyone else // Once it succeeds, put it into the dictionary to make it available to everyone else
_userData.AddOrUpdate(GetCacheKey(userId, key), newValue, delegate { return newValue; }); _userData.AddOrUpdate(GetCacheKey(userId, key), newValue, delegate { return newValue; });
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.ErrorException("Error saving user data", ex); _logger.ErrorException("Error saving user data", ex);
throw; throw;
}
} }
EventHelper.FireEventIfNotNull(UserDataSaved, this, new UserDataSaveEventArgs EventHelper.FireEventIfNotNull(UserDataSaved, this, new UserDataSaveEventArgs
{ {
Key = key, Keys = keys,
UserData = userData, UserData = userData,
SaveReason = reason, SaveReason = reason,
UserId = userId, UserId = userId,
@ -172,6 +175,21 @@ namespace MediaBrowser.Server.Implementations.Library
return userId + key; return userId + key;
} }
public UserItemData GetUserData(IHasUserData user, IHasUserData item)
{
return GetUserData(user.Id, item.GetUserDataKey());
}
public UserItemData GetUserData(string userId, IHasUserData item)
{
return GetUserData(userId, item.GetUserDataKey());
}
public UserItemData GetUserData(Guid userId, IHasUserData item)
{
return GetUserData(userId, item.GetUserDataKey());
}
public UserItemDataDto GetUserDataDto(IHasUserData item, User user) public UserItemDataDto GetUserDataDto(IHasUserData item, User user)
{ {
var userData = GetUserData(user.Id, item.GetUserDataKey()); var userData = GetUserData(user.Id, item.GetUserDataKey());

@ -164,7 +164,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var val = query.IsFavorite.Value; var val = query.IsFavorite.Value;
channels = channels channels = channels
.Where(i => _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).IsFavorite == val); .Where(i => _userDataManager.GetUserData(user, i).IsFavorite == val);
} }
if (query.IsLiked.HasValue) if (query.IsLiked.HasValue)
@ -174,7 +174,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
channels = channels channels = channels
.Where(i => .Where(i =>
{ {
var likes = _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).Likes; var likes = _userDataManager.GetUserData(user, i).Likes;
return likes.HasValue && likes.Value == val; return likes.HasValue && likes.Value == val;
}); });
@ -187,7 +187,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
channels = channels channels = channels
.Where(i => .Where(i =>
{ {
var likes = _userDataManager.GetUserData(user.Id, i.GetUserDataKey()).Likes; var likes = _userDataManager.GetUserData(user, i).Likes;
return likes.HasValue && likes.Value != val; return likes.HasValue && likes.Value != val;
}); });
@ -200,7 +200,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{ {
if (enableFavoriteSorting) if (enableFavoriteSorting)
{ {
var userData = _userDataManager.GetUserData(user.Id, i.GetUserDataKey()); var userData = _userDataManager.GetUserData(user, i);
if (userData.IsFavorite) if (userData.IsFavorite)
{ {
@ -1005,7 +1005,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var channel = GetInternalChannel(program.ChannelId); var channel = GetInternalChannel(program.ChannelId);
var channelUserdata = _userDataManager.GetUserData(userId, channel.GetUserDataKey()); var channelUserdata = _userDataManager.GetUserData(userId, channel);
if (channelUserdata.Likes ?? false) if (channelUserdata.Likes ?? false)
{ {
@ -1036,7 +1036,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
if (genres.TryGetValue(i, out genre)) if (genres.TryGetValue(i, out genre))
{ {
var genreUserdata = _userDataManager.GetUserData(userId, genre.GetUserDataKey()); var genreUserdata = _userDataManager.GetUserData(userId, genre);
if (genreUserdata.Likes ?? false) if (genreUserdata.Likes ?? false)
{ {

@ -78,9 +78,6 @@
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Data.SQLite">
<HintPath>..\ThirdParty\System.Data.SQLite.ManagedOnly\1.0.94.0\System.Data.SQLite.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Net" /> <Reference Include="System.Net" />
@ -261,6 +258,8 @@
<Compile Include="Notifications\IConfigurableNotificationService.cs" /> <Compile Include="Notifications\IConfigurableNotificationService.cs" />
<Compile Include="Persistence\BaseSqliteRepository.cs" /> <Compile Include="Persistence\BaseSqliteRepository.cs" />
<Compile Include="Persistence\CleanDatabaseScheduledTask.cs" /> <Compile Include="Persistence\CleanDatabaseScheduledTask.cs" />
<Compile Include="Persistence\DataExtensions.cs" />
<Compile Include="Persistence\IDbConnector.cs" />
<Compile Include="Persistence\MediaStreamColumns.cs" /> <Compile Include="Persistence\MediaStreamColumns.cs" />
<Compile Include="Social\SharingManager.cs" /> <Compile Include="Social\SharingManager.cs" />
<Compile Include="Social\SharingRepository.cs" /> <Compile Include="Social\SharingRepository.cs" />
@ -275,7 +274,6 @@
<Compile Include="Notifications\InternalNotificationService.cs" /> <Compile Include="Notifications\InternalNotificationService.cs" />
<Compile Include="Notifications\NotificationConfigurationFactory.cs" /> <Compile Include="Notifications\NotificationConfigurationFactory.cs" />
<Compile Include="Notifications\NotificationManager.cs" /> <Compile Include="Notifications\NotificationManager.cs" />
<Compile Include="Persistence\SqliteExtensions.cs" />
<Compile Include="Persistence\SqliteFileOrganizationRepository.cs" /> <Compile Include="Persistence\SqliteFileOrganizationRepository.cs" />
<Compile Include="Notifications\SqliteNotificationsRepository.cs" /> <Compile Include="Notifications\SqliteNotificationsRepository.cs" />
<Compile Include="Persistence\SqliteProviderInfoRepository.cs" /> <Compile Include="Persistence\SqliteProviderInfoRepository.cs" />

@ -32,11 +32,11 @@ namespace MediaBrowser.Server.Implementations.Notifications
_appPaths = appPaths; _appPaths = appPaths;
} }
public async Task Initialize() public async Task Initialize(IDbConnector dbConnector)
{ {
var dbFile = Path.Combine(_appPaths.DataPath, "notifications.db"); var dbFile = Path.Combine(_appPaths.DataPath, "notifications.db");
_connection = await SqliteExtensions.ConnectToDb(dbFile, Logger).ConfigureAwait(false); _connection = await dbConnector.Connect(dbFile).ConfigureAwait(false);
string[] queries = { string[] queries = {

@ -110,6 +110,12 @@ namespace MediaBrowser.Server.Implementations.Persistence
_config.SaveConfiguration(); _config.SaveConfiguration();
} }
if (_config.Configuration.SchemaVersion < SqliteItemRepository.LatestSchemaVersion)
{
_config.Configuration.SchemaVersion = SqliteItemRepository.LatestSchemaVersion;
_config.SaveConfiguration();
}
if (EnableUnavailableMessage) if (EnableUnavailableMessage)
{ {
EnableUnavailableMessage = false; EnableUnavailableMessage = false;

@ -3,16 +3,12 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using System; using System;
using System.Data; using System.Data;
using System.Data.SQLite;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Persistence namespace MediaBrowser.Server.Implementations.Persistence
{ {
/// <summary> static class DataExtensions
/// Class SQLiteExtensions
/// </summary>
static class SqliteExtensions
{ {
/// <summary> /// <summary>
/// Determines whether the specified conn is open. /// Determines whether the specified conn is open.
@ -28,11 +24,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
{ {
return (IDataParameter)cmd.Parameters[index]; return (IDataParameter)cmd.Parameters[index];
} }
public static IDataParameter Add(this IDataParameterCollection paramCollection, IDbCommand cmd, string name, DbType type) public static IDataParameter Add(this IDataParameterCollection paramCollection, IDbCommand cmd, string name, DbType type)
{ {
var param = cmd.CreateParameter(); var param = cmd.CreateParameter();
param.ParameterName = name; param.ParameterName = name;
param.DbType = type; param.DbType = type;
@ -48,11 +44,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
param.ParameterName = name; param.ParameterName = name;
paramCollection.Add(param); paramCollection.Add(param);
return param; return param;
} }
/// <summary> /// <summary>
/// Gets a stream from a DataReader at a given ordinal /// Gets a stream from a DataReader at a given ordinal
/// </summary> /// </summary>
@ -122,38 +118,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
} }
} }
/// <summary>
/// Connects to db.
/// </summary>
/// <param name="dbPath">The db path.</param>
/// <param name="logger">The logger.</param>
/// <returns>Task{IDbConnection}.</returns>
/// <exception cref="System.ArgumentNullException">dbPath</exception>
public static async Task<IDbConnection> ConnectToDb(string dbPath, ILogger logger)
{
if (string.IsNullOrEmpty(dbPath))
{
throw new ArgumentNullException("dbPath");
}
logger.Info("Sqlite {0} opening {1}", SQLiteConnection.SQLiteVersion, dbPath);
var connectionstr = new SQLiteConnectionStringBuilder
{
PageSize = 4096,
CacheSize = 2000,
SyncMode = SynchronizationModes.Full,
DataSource = dbPath,
JournalMode = SQLiteJournalModeEnum.Wal
};
var connection = new SQLiteConnection(connectionstr.ConnectionString);
await connection.OpenAsync().ConfigureAwait(false);
return connection;
}
public static void Attach(IDbConnection db, string path, string alias) public static void Attach(IDbConnection db, string path, string alias)
{ {
using (var cmd = db.CreateCommand()) using (var cmd = db.CreateCommand())

@ -0,0 +1,10 @@
using System.Data;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Persistence
{
public interface IDbConnector
{
Task<IDbConnection> Connect(string dbPath);
}
}

@ -52,11 +52,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// Opens the connection to the database /// Opens the connection to the database
/// </summary> /// </summary>
/// <returns>Task.</returns> /// <returns>Task.</returns>
public async Task Initialize() public async Task Initialize(IDbConnector dbConnector)
{ {
var dbFile = Path.Combine(_appPaths.DataPath, "displaypreferences.db"); var dbFile = Path.Combine(_appPaths.DataPath, "displaypreferences.db");
_connection = await SqliteExtensions.ConnectToDb(dbFile, Logger).ConfigureAwait(false); _connection = await dbConnector.Connect(dbFile).ConfigureAwait(false);
string[] queries = { string[] queries = {

@ -35,11 +35,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// Opens the connection to the database /// Opens the connection to the database
/// </summary> /// </summary>
/// <returns>Task.</returns> /// <returns>Task.</returns>
public async Task Initialize() public async Task Initialize(IDbConnector dbConnector)
{ {
var dbFile = Path.Combine(_appPaths.DataPath, "fileorganization.db"); var dbFile = Path.Combine(_appPaths.DataPath, "fileorganization.db");
_connection = await SqliteExtensions.ConnectToDb(dbFile, Logger).ConfigureAwait(false); _connection = await dbConnector.Connect(dbFile).ConfigureAwait(false);
string[] queries = { string[] queries = {

@ -20,6 +20,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Playlists;
using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.LiveTv;
@ -55,7 +56,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// <summary> /// <summary>
/// The _app paths /// The _app paths
/// </summary> /// </summary>
private readonly IApplicationPaths _appPaths; private readonly IServerConfigurationManager _config;
/// <summary> /// <summary>
/// The _save item command /// The _save item command
@ -81,35 +82,31 @@ namespace MediaBrowser.Server.Implementations.Persistence
private IDbCommand _updateInheritedRatingCommand; private IDbCommand _updateInheritedRatingCommand;
private IDbCommand _updateInheritedTagsCommand; private IDbCommand _updateInheritedTagsCommand;
private const int LatestSchemaVersion = 65; public const int LatestSchemaVersion = 68;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SqliteItemRepository"/> class. /// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
/// </summary> /// </summary>
/// <param name="appPaths">The app paths.</param>
/// <param name="jsonSerializer">The json serializer.</param>
/// <param name="logManager">The log manager.</param>
/// <exception cref="System.ArgumentNullException">
/// appPaths /// appPaths
/// or /// or
/// jsonSerializer /// jsonSerializer
/// </exception> /// </exception>
public SqliteItemRepository(IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILogManager logManager) public SqliteItemRepository(IServerConfigurationManager config, IJsonSerializer jsonSerializer, ILogManager logManager)
: base(logManager) : base(logManager)
{ {
if (appPaths == null) if (config == null)
{ {
throw new ArgumentNullException("appPaths"); throw new ArgumentNullException("config");
} }
if (jsonSerializer == null) if (jsonSerializer == null)
{ {
throw new ArgumentNullException("jsonSerializer"); throw new ArgumentNullException("jsonSerializer");
} }
_appPaths = appPaths; _config = config;
_jsonSerializer = jsonSerializer; _jsonSerializer = jsonSerializer;
_criticReviewsPath = Path.Combine(_appPaths.DataPath, "critic-reviews"); _criticReviewsPath = Path.Combine(_config.ApplicationPaths.DataPath, "critic-reviews");
} }
private const string ChaptersTableName = "Chapters2"; private const string ChaptersTableName = "Chapters2";
@ -118,11 +115,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// Opens the connection to the database /// Opens the connection to the database
/// </summary> /// </summary>
/// <returns>Task.</returns> /// <returns>Task.</returns>
public async Task Initialize() public async Task Initialize(IDbConnector dbConnector)
{ {
var dbFile = Path.Combine(_appPaths.DataPath, "library.db"); var dbFile = Path.Combine(_config.ApplicationPaths.DataPath, "library.db");
_connection = await SqliteExtensions.ConnectToDb(dbFile, Logger).ConfigureAwait(false); _connection = await dbConnector.Connect(dbFile).ConfigureAwait(false);
var createMediaStreamsTableCommand var createMediaStreamsTableCommand
= "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, PRIMARY KEY (ItemId, StreamIndex))"; = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, PRIMARY KEY (ItemId, StreamIndex))";
@ -228,18 +225,27 @@ namespace MediaBrowser.Server.Implementations.Persistence
_connection.AddColumn(Logger, "TypedBaseItems", "DateModifiedDuringLastRefresh", "DATETIME"); _connection.AddColumn(Logger, "TypedBaseItems", "DateModifiedDuringLastRefresh", "DATETIME");
_connection.AddColumn(Logger, "TypedBaseItems", "InheritedTags", "Text"); _connection.AddColumn(Logger, "TypedBaseItems", "InheritedTags", "Text");
_connection.AddColumn(Logger, "TypedBaseItems", "CleanName", "Text"); _connection.AddColumn(Logger, "TypedBaseItems", "CleanName", "Text");
_connection.AddColumn(Logger, "TypedBaseItems", "PresentationUniqueKey", "Text");
string[] postQueries =
{
"create index if not exists idx_PresentationUniqueKey on TypedBaseItems(PresentationUniqueKey)",
"create index if not exists idx_Type on TypedBaseItems(Type)"
};
_connection.RunQueries(postQueries, Logger);
PrepareStatements(); PrepareStatements();
new MediaStreamColumns(_connection, Logger).AddColumns(); new MediaStreamColumns(_connection, Logger).AddColumns();
var chapterDbFile = Path.Combine(_appPaths.DataPath, "chapters.db"); var chapterDbFile = Path.Combine(_config.ApplicationPaths.DataPath, "chapters.db");
if (File.Exists(chapterDbFile)) if (File.Exists(chapterDbFile))
{ {
MigrateChapters(chapterDbFile); MigrateChapters(chapterDbFile);
} }
var mediaStreamsDbFile = Path.Combine(_appPaths.DataPath, "mediainfo.db"); var mediaStreamsDbFile = Path.Combine(_config.ApplicationPaths.DataPath, "mediainfo.db");
if (File.Exists(mediaStreamsDbFile)) if (File.Exists(mediaStreamsDbFile))
{ {
MigrateMediaStreams(mediaStreamsDbFile); MigrateMediaStreams(mediaStreamsDbFile);
@ -252,7 +258,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
{ {
var backupFile = file + ".bak"; var backupFile = file + ".bak";
File.Copy(file, backupFile, true); File.Copy(file, backupFile, true);
SqliteExtensions.Attach(_connection, backupFile, "MediaInfoOld"); DataExtensions.Attach(_connection, backupFile, "MediaInfoOld");
var columns = string.Join(",", _mediaStreamSaveColumns); var columns = string.Join(",", _mediaStreamSaveColumns);
@ -278,7 +284,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
{ {
var backupFile = file + ".bak"; var backupFile = file + ".bak";
File.Copy(file, backupFile, true); File.Copy(file, backupFile, true);
SqliteExtensions.Attach(_connection, backupFile, "ChaptersOld"); DataExtensions.Attach(_connection, backupFile, "ChaptersOld");
string[] queries = { string[] queries = {
"REPLACE INTO "+ChaptersTableName+"(ItemId, ChapterIndex, StartPositionTicks, Name, ImagePath) SELECT ItemId, ChapterIndex, StartPositionTicks, Name, ImagePath FROM ChaptersOld.Chapters;" "REPLACE INTO "+ChaptersTableName+"(ItemId, ChapterIndex, StartPositionTicks, Name, ImagePath) SELECT ItemId, ChapterIndex, StartPositionTicks, Name, ImagePath FROM ChaptersOld.Chapters;"
@ -469,7 +475,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
"CriticRatingSummary", "CriticRatingSummary",
"DateModifiedDuringLastRefresh", "DateModifiedDuringLastRefresh",
"InheritedTags", "InheritedTags",
"CleanName" "CleanName",
"PresentationUniqueKey"
}; };
_saveItemCommand = _connection.CreateCommand(); _saveItemCommand = _connection.CreateCommand();
_saveItemCommand.CommandText = "replace into TypedBaseItems (" + string.Join(",", saveColumns.ToArray()) + ") values ("; _saveItemCommand.CommandText = "replace into TypedBaseItems (" + string.Join(",", saveColumns.ToArray()) + ") values (";
@ -803,6 +810,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
{ {
_saveItemCommand.GetParameter(index++).Value = item.Name.RemoveDiacritics(); _saveItemCommand.GetParameter(index++).Value = item.Name.RemoveDiacritics();
} }
_saveItemCommand.GetParameter(index++).Value = item.PresentationUniqueKey;
_saveItemCommand.Transaction = transaction; _saveItemCommand.Transaction = transaction;
@ -1463,6 +1471,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
CheckDisposed(); CheckDisposed();
var now = DateTime.UtcNow;
using (var cmd = _connection.CreateCommand()) using (var cmd = _connection.CreateCommand())
{ {
cmd.CommandText = "select " + string.Join(",", _retriveItemColumns) + " from TypedBaseItems"; cmd.CommandText = "select " + string.Join(",", _retriveItemColumns) + " from TypedBaseItems";
@ -1475,6 +1485,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
cmd.CommandText += whereText; cmd.CommandText += whereText;
if (EnableGroupByPresentationUniqueKey(query) && _config.Configuration.SchemaVersion >= 66)
{
cmd.CommandText += " Group by PresentationUniqueKey";
}
cmd.CommandText += GetOrderByText(query); cmd.CommandText += GetOrderByText(query);
if (query.Limit.HasValue) if (query.Limit.HasValue)
@ -1482,10 +1497,12 @@ namespace MediaBrowser.Server.Implementations.Persistence
cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(CultureInfo.InvariantCulture); cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(CultureInfo.InvariantCulture);
} }
//Logger.Debug(cmd.CommandText);
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
{ {
Logger.Debug("GetItemList query time: {0}ms. Query: {1}",
Convert.ToInt32((DateTime.UtcNow - now).TotalMilliseconds),
cmd.CommandText);
while (reader.Read()) while (reader.Read())
{ {
var item = GetItem(reader); var item = GetItem(reader);
@ -1507,6 +1524,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
CheckDisposed(); CheckDisposed();
var now = DateTime.UtcNow;
using (var cmd = _connection.CreateCommand()) using (var cmd = _connection.CreateCommand())
{ {
cmd.CommandText = "select " + string.Join(",", _retriveItemColumns) + " from TypedBaseItems"; cmd.CommandText = "select " + string.Join(",", _retriveItemColumns) + " from TypedBaseItems";
@ -1525,6 +1544,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
cmd.CommandText += whereText; cmd.CommandText += whereText;
if (EnableGroupByPresentationUniqueKey(query) && _config.Configuration.SchemaVersion >= 66)
{
cmd.CommandText += " Group by PresentationUniqueKey";
}
cmd.CommandText += GetOrderByText(query); cmd.CommandText += GetOrderByText(query);
if (query.Limit.HasValue) if (query.Limit.HasValue)
@ -1532,15 +1556,24 @@ namespace MediaBrowser.Server.Implementations.Persistence
cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(CultureInfo.InvariantCulture); cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(CultureInfo.InvariantCulture);
} }
cmd.CommandText += "; select count (guid) from TypedBaseItems" + whereTextWithoutPaging; if (_config.Configuration.SchemaVersion >= 66)
{
//Logger.Debug(cmd.CommandText); cmd.CommandText += "; select count (distinct PresentationUniqueKey) from TypedBaseItems" + whereTextWithoutPaging;
}
else
{
cmd.CommandText += "; select count (guid) from TypedBaseItems" + whereTextWithoutPaging;
}
var list = new List<BaseItem>(); var list = new List<BaseItem>();
var count = 0; var count = 0;
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess)) using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
{ {
Logger.Debug("GetItems query time: {0}ms. Query: {1}",
Convert.ToInt32((DateTime.UtcNow - now).TotalMilliseconds),
cmd.CommandText);
while (reader.Read()) while (reader.Read())
{ {
var item = GetItem(reader); var item = GetItem(reader);
@ -1608,6 +1641,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
CheckDisposed(); CheckDisposed();
var now = DateTime.UtcNow;
using (var cmd = _connection.CreateCommand()) using (var cmd = _connection.CreateCommand())
{ {
cmd.CommandText = "select guid from TypedBaseItems"; cmd.CommandText = "select guid from TypedBaseItems";
@ -1620,6 +1655,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
cmd.CommandText += whereText; cmd.CommandText += whereText;
if (EnableGroupByPresentationUniqueKey(query) && _config.Configuration.SchemaVersion >= 66)
{
cmd.CommandText += " Group by PresentationUniqueKey";
}
cmd.CommandText += GetOrderByText(query); cmd.CommandText += GetOrderByText(query);
if (query.Limit.HasValue) if (query.Limit.HasValue)
@ -1629,10 +1669,12 @@ namespace MediaBrowser.Server.Implementations.Persistence
var list = new List<Guid>(); var list = new List<Guid>();
//Logger.Debug(cmd.CommandText);
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
{ {
Logger.Debug("GetItemIdsList query time: {0}ms. Query: {1}",
Convert.ToInt32((DateTime.UtcNow - now).TotalMilliseconds),
cmd.CommandText);
while (reader.Read()) while (reader.Read())
{ {
list.Add(reader.GetGuid(0)); list.Add(reader.GetGuid(0));
@ -1662,7 +1704,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
string.Empty : string.Empty :
" where " + string.Join(" AND ", whereClauses.ToArray()); " where " + string.Join(" AND ", whereClauses.ToArray());
whereClauses = GetWhereClauses(query, cmd, true); whereClauses = GetWhereClauses(query, cmd, true, false);
var whereText = whereClauses.Count == 0 ? var whereText = whereClauses.Count == 0 ?
string.Empty : string.Empty :
@ -1670,6 +1712,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
cmd.CommandText += whereText; cmd.CommandText += whereText;
if (EnableGroupByPresentationUniqueKey(query) && _config.Configuration.SchemaVersion >= 66)
{
cmd.CommandText += " Group by PresentationUniqueKey";
}
cmd.CommandText += GetOrderByText(query); cmd.CommandText += GetOrderByText(query);
if (query.Limit.HasValue) if (query.Limit.HasValue)
@ -1721,6 +1768,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
CheckDisposed(); CheckDisposed();
var now = DateTime.UtcNow;
using (var cmd = _connection.CreateCommand()) using (var cmd = _connection.CreateCommand())
{ {
cmd.CommandText = "select guid from TypedBaseItems"; cmd.CommandText = "select guid from TypedBaseItems";
@ -1739,6 +1788,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
cmd.CommandText += whereText; cmd.CommandText += whereText;
if (EnableGroupByPresentationUniqueKey(query) && _config.Configuration.SchemaVersion >= 66)
{
cmd.CommandText += " Group by PresentationUniqueKey";
}
cmd.CommandText += GetOrderByText(query); cmd.CommandText += GetOrderByText(query);
if (query.Limit.HasValue) if (query.Limit.HasValue)
@ -1746,15 +1800,24 @@ namespace MediaBrowser.Server.Implementations.Persistence
cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(CultureInfo.InvariantCulture); cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(CultureInfo.InvariantCulture);
} }
cmd.CommandText += "; select count (guid) from TypedBaseItems" + whereTextWithoutPaging; if (EnableGroupByPresentationUniqueKey(query) && _config.Configuration.SchemaVersion >= 66)
{
cmd.CommandText += "; select count (distinct PresentationUniqueKey) from TypedBaseItems" + whereTextWithoutPaging;
}
else
{
cmd.CommandText += "; select count (guid) from TypedBaseItems" + whereTextWithoutPaging;
}
var list = new List<Guid>(); var list = new List<Guid>();
var count = 0; var count = 0;
//Logger.Debug(cmd.CommandText);
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess)) using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
{ {
Logger.Debug("GetItemIds query time: {0}ms. Query: {1}",
Convert.ToInt32((DateTime.UtcNow - now).TotalMilliseconds),
cmd.CommandText);
while (reader.Read()) while (reader.Read())
{ {
list.Add(reader.GetGuid(0)); list.Add(reader.GetGuid(0));
@ -1774,7 +1837,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
} }
} }
private List<string> GetWhereClauses(InternalItemsQuery query, IDbCommand cmd, bool addPaging) private List<string> GetWhereClauses(InternalItemsQuery query, IDbCommand cmd, bool addPaging, bool enablePresentationUniqueKey = true)
{ {
var whereClauses = new List<string>(); var whereClauses = new List<string>();
@ -1873,6 +1936,12 @@ namespace MediaBrowser.Server.Implementations.Persistence
cmd.Parameters.Add(cmd, "@Path", DbType.String).Value = query.Path; cmd.Parameters.Add(cmd, "@Path", DbType.String).Value = query.Path;
} }
if (!string.IsNullOrWhiteSpace(query.PresentationUniqueKey))
{
whereClauses.Add("PresentationUniqueKey=@PresentationUniqueKey");
cmd.Parameters.Add(cmd, "@PresentationUniqueKey", DbType.String).Value = query.PresentationUniqueKey;
}
if (query.MinCommunityRating.HasValue) if (query.MinCommunityRating.HasValue)
{ {
whereClauses.Add("CommunityRating>=@MinCommunityRating"); whereClauses.Add("CommunityRating>=@MinCommunityRating");
@ -1998,9 +2067,40 @@ namespace MediaBrowser.Server.Implementations.Persistence
if (!string.IsNullOrWhiteSpace(query.NameContains)) if (!string.IsNullOrWhiteSpace(query.NameContains))
{ {
whereClauses.Add("CleanName like @NameContains"); if (_config.Configuration.SchemaVersion >= 66)
{
whereClauses.Add("CleanName like @NameContains");
}
else
{
whereClauses.Add("Name like @NameContains");
}
cmd.Parameters.Add(cmd, "@NameContains", DbType.String).Value = "%" + query.NameContains + "%"; cmd.Parameters.Add(cmd, "@NameContains", DbType.String).Value = "%" + query.NameContains + "%";
} }
if (!string.IsNullOrWhiteSpace(query.NameStartsWith))
{
if (_config.Configuration.SchemaVersion >= 66)
{
whereClauses.Add("CleanName like @NameStartsWith");
}
else
{
whereClauses.Add("Name like @NameStartsWith");
}
cmd.Parameters.Add(cmd, "@NameStartsWith", DbType.String).Value = query.NameStartsWith + "%";
}
if (!string.IsNullOrWhiteSpace(query.NameStartsWithOrGreater))
{
whereClauses.Add("SortName >= @NameStartsWithOrGreater");
// lowercase this because SortName is stored as lowercase
cmd.Parameters.Add(cmd, "@NameStartsWithOrGreater", DbType.String).Value = query.NameStartsWithOrGreater.ToLower();
}
if (!string.IsNullOrWhiteSpace(query.NameLessThan))
{
whereClauses.Add("SortName < @NameLessThan");
// lowercase this because SortName is stored as lowercase
cmd.Parameters.Add(cmd, "@NameLessThan", DbType.String).Value = query.NameLessThan.ToLower();
}
if (query.Genres.Length > 0) if (query.Genres.Length > 0)
{ {
@ -2134,7 +2234,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
whereClauses.Add("MediaType in (" + val + ")"); whereClauses.Add("MediaType in (" + val + ")");
} }
var enableItemsByName = query.IncludeItemsByName ?? query.IncludeItemTypes.Length > 0; //var enableItemsByName = query.IncludeItemsByName ?? query.IncludeItemTypes.Length > 0;
var enableItemsByName = query.IncludeItemsByName ?? false;
if (query.TopParentIds.Length == 1) if (query.TopParentIds.Length == 1)
{ {
@ -2197,7 +2298,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
excludeTagIndex = 0; excludeTagIndex = 0;
foreach (var excludeTag in query.ExcludeInheritedTags) foreach (var excludeTag in query.ExcludeInheritedTags)
{ {
whereClauses.Add("(InheritedTags is null OR InheritedTags not like @excludeInheritedTag" + excludeTagIndex +")"); whereClauses.Add("(InheritedTags is null OR InheritedTags not like @excludeInheritedTag" + excludeTagIndex + ")");
cmd.Parameters.Add(cmd, "@excludeInheritedTag" + excludeTagIndex, DbType.String).Value = "%" + excludeTag + "%"; cmd.Parameters.Add(cmd, "@excludeInheritedTag" + excludeTagIndex, DbType.String).Value = "%" + excludeTag + "%";
excludeTagIndex++; excludeTagIndex++;
} }
@ -2210,6 +2311,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
string.Empty : string.Empty :
" where " + string.Join(" AND ", whereClauses.ToArray()); " where " + string.Join(" AND ", whereClauses.ToArray());
if (enablePresentationUniqueKey && EnableGroupByPresentationUniqueKey(query) && _config.Configuration.SchemaVersion >= 66)
{
pagingWhereText += " Group by PresentationUniqueKey";
}
var orderBy = GetOrderByText(query); var orderBy = GetOrderByText(query);
whereClauses.Add(string.Format("guid NOT IN (SELECT guid FROM TypedBaseItems {0}" + orderBy + " LIMIT {1})", whereClauses.Add(string.Format("guid NOT IN (SELECT guid FROM TypedBaseItems {0}" + orderBy + " LIMIT {1})",
@ -2221,6 +2327,34 @@ namespace MediaBrowser.Server.Implementations.Persistence
return whereClauses; return whereClauses;
} }
private bool EnableGroupByPresentationUniqueKey(InternalItemsQuery query)
{
if (!string.IsNullOrWhiteSpace(query.PresentationUniqueKey))
{
return false;
}
if (query.IncludeItemTypes.Length == 0)
{
return true;
}
var types = new[] {
typeof(Episode).Name,
typeof(Video).Name ,
typeof(Movie).Name ,
typeof(MusicVideo).Name ,
typeof(Series).Name ,
typeof(Season).Name };
if (types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase)))
{
return true;
}
return false;
}
private static readonly Type[] KnownTypes = private static readonly Type[] KnownTypes =
{ {
typeof(LiveTvProgram), typeof(LiveTvProgram),
@ -2299,7 +2433,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
try try
{ {
transaction = _connection.BeginTransaction(); transaction = _connection.BeginTransaction();
foreach (var item in newValues) foreach (var item in newValues)
{ {
_updateInheritedTagsCommand.GetParameter(0).Value = item.Item1; _updateInheritedTagsCommand.GetParameter(0).Value = item.Item1;

@ -39,11 +39,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// Opens the connection to the database /// Opens the connection to the database
/// </summary> /// </summary>
/// <returns>Task.</returns> /// <returns>Task.</returns>
public async Task Initialize() public async Task Initialize(IDbConnector dbConnector)
{ {
var dbFile = Path.Combine(_appPaths.DataPath, "refreshinfo.db"); var dbFile = Path.Combine(_appPaths.DataPath, "refreshinfo.db");
_connection = await SqliteExtensions.ConnectToDb(dbFile, Logger).ConfigureAwait(false); _connection = await dbConnector.Connect(dbFile).ConfigureAwait(false);
string[] queries = { string[] queries = {

@ -37,11 +37,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// Opens the connection to the database /// Opens the connection to the database
/// </summary> /// </summary>
/// <returns>Task.</returns> /// <returns>Task.</returns>
public async Task Initialize() public async Task Initialize(IDbConnector dbConnector)
{ {
var dbFile = Path.Combine(_appPaths.DataPath, "userdata_v2.db"); var dbFile = Path.Combine(_appPaths.DataPath, "userdata_v2.db");
_connection = await SqliteExtensions.ConnectToDb(dbFile, Logger).ConfigureAwait(false); _connection = await dbConnector.Connect(dbFile).ConfigureAwait(false);
string[] queries = { string[] queries = {

@ -43,12 +43,12 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// Opens the connection to the database /// Opens the connection to the database
/// </summary> /// </summary>
/// <returns>Task.</returns> /// <returns>Task.</returns>
public async Task Initialize() public async Task Initialize(IDbConnector dbConnector)
{ {
var dbFile = Path.Combine(_appPaths.DataPath, "users.db"); var dbFile = Path.Combine(_appPaths.DataPath, "users.db");
_connection = await SqliteExtensions.ConnectToDb(dbFile, Logger).ConfigureAwait(false); _connection = await dbConnector.Connect(dbFile).ConfigureAwait(false);
string[] queries = { string[] queries = {
"create table if not exists users (guid GUID primary key, data BLOB)", "create table if not exists users (guid GUID primary key, data BLOB)",

@ -27,11 +27,11 @@ namespace MediaBrowser.Server.Implementations.Security
_appPaths = appPaths; _appPaths = appPaths;
} }
public async Task Initialize() public async Task Initialize(IDbConnector dbConnector)
{ {
var dbFile = Path.Combine(_appPaths.DataPath, "authentication.db"); var dbFile = Path.Combine(_appPaths.DataPath, "authentication.db");
_connection = await SqliteExtensions.ConnectToDb(dbFile, Logger).ConfigureAwait(false); _connection = await dbConnector.Connect(dbFile).ConfigureAwait(false);
string[] queries = { string[] queries = {

@ -601,11 +601,9 @@ namespace MediaBrowser.Server.Implementations.Session
if (libraryItem != null) if (libraryItem != null)
{ {
var key = libraryItem.GetUserDataKey();
foreach (var user in users) foreach (var user in users)
{ {
await OnPlaybackStart(user.Id, key, libraryItem).ConfigureAwait(false); await OnPlaybackStart(user.Id, libraryItem).ConfigureAwait(false);
} }
} }
@ -632,12 +630,11 @@ namespace MediaBrowser.Server.Implementations.Session
/// Called when [playback start]. /// Called when [playback start].
/// </summary> /// </summary>
/// <param name="userId">The user identifier.</param> /// <param name="userId">The user identifier.</param>
/// <param name="userDataKey">The user data key.</param>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
private async Task OnPlaybackStart(Guid userId, string userDataKey, IHasUserData item) private async Task OnPlaybackStart(Guid userId, IHasUserData item)
{ {
var data = _userDataRepository.GetUserData(userId, userDataKey); var data = _userDataRepository.GetUserData(userId, item);
data.PlayCount++; data.PlayCount++;
data.LastPlayedDate = DateTime.UtcNow; data.LastPlayedDate = DateTime.UtcNow;
@ -676,11 +673,9 @@ namespace MediaBrowser.Server.Implementations.Session
if (libraryItem != null) if (libraryItem != null)
{ {
var key = libraryItem.GetUserDataKey();
foreach (var user in users) foreach (var user in users)
{ {
await OnPlaybackProgress(user, key, libraryItem, info).ConfigureAwait(false); await OnPlaybackProgress(user, libraryItem, info).ConfigureAwait(false);
} }
} }
@ -714,9 +709,9 @@ namespace MediaBrowser.Server.Implementations.Session
StartIdleCheckTimer(); StartIdleCheckTimer();
} }
private async Task OnPlaybackProgress(User user, string userDataKey, BaseItem item, PlaybackProgressInfo info) private async Task OnPlaybackProgress(User user, BaseItem item, PlaybackProgressInfo info)
{ {
var data = _userDataRepository.GetUserData(user.Id, userDataKey); var data = _userDataRepository.GetUserData(user.Id, item);
var positionTicks = info.PositionTicks; var positionTicks = info.PositionTicks;
@ -811,11 +806,9 @@ namespace MediaBrowser.Server.Implementations.Session
if (libraryItem != null) if (libraryItem != null)
{ {
var key = libraryItem.GetUserDataKey();
foreach (var user in users) foreach (var user in users)
{ {
playedToCompletion = await OnPlaybackStopped(user.Id, key, libraryItem, info.PositionTicks, info.Failed).ConfigureAwait(false); playedToCompletion = await OnPlaybackStopped(user.Id, libraryItem, info.PositionTicks, info.Failed).ConfigureAwait(false);
} }
} }
@ -848,13 +841,13 @@ namespace MediaBrowser.Server.Implementations.Session
await SendPlaybackStoppedNotification(session, CancellationToken.None).ConfigureAwait(false); await SendPlaybackStoppedNotification(session, CancellationToken.None).ConfigureAwait(false);
} }
private async Task<bool> OnPlaybackStopped(Guid userId, string userDataKey, BaseItem item, long? positionTicks, bool playbackFailed) private async Task<bool> OnPlaybackStopped(Guid userId, BaseItem item, long? positionTicks, bool playbackFailed)
{ {
bool playedToCompletion = false; bool playedToCompletion = false;
if (!playbackFailed) if (!playbackFailed)
{ {
var data = _userDataRepository.GetUserData(userId, userDataKey); var data = _userDataRepository.GetUserData(userId, item);
if (positionTicks.HasValue) if (positionTicks.HasValue)
{ {

@ -26,11 +26,11 @@ namespace MediaBrowser.Server.Implementations.Social
/// Opens the connection to the database /// Opens the connection to the database
/// </summary> /// </summary>
/// <returns>Task.</returns> /// <returns>Task.</returns>
public async Task Initialize() public async Task Initialize(IDbConnector dbConnector)
{ {
var dbFile = Path.Combine(_appPaths.DataPath, "shares.db"); var dbFile = Path.Combine(_appPaths.DataPath, "shares.db");
_connection = await SqliteExtensions.ConnectToDb(dbFile, Logger).ConfigureAwait(false); _connection = await dbConnector.Connect(dbFile).ConfigureAwait(false);
string[] queries = { string[] queries = {

@ -49,8 +49,8 @@ namespace MediaBrowser.Server.Implementations.Sorting
private int Compare(Episode x, Episode y) private int Compare(Episode x, Episode y)
{ {
var isXSpecial = (x.PhysicalSeasonNumber ?? -1) == 0; var isXSpecial = (x.ParentIndexNumber ?? -1) == 0;
var isYSpecial = (y.PhysicalSeasonNumber ?? -1) == 0; var isYSpecial = (y.ParentIndexNumber ?? -1) == 0;
if (isXSpecial && isYSpecial) if (isXSpecial && isYSpecial)
{ {
@ -74,7 +74,7 @@ namespace MediaBrowser.Server.Implementations.Sorting
{ {
// http://thetvdb.com/wiki/index.php?title=Special_Episodes // http://thetvdb.com/wiki/index.php?title=Special_Episodes
var xSeason = x.PhysicalSeasonNumber ?? -1; var xSeason = x.ParentIndexNumber ?? -1;
var ySeason = y.AirsAfterSeasonNumber ?? y.AirsBeforeSeasonNumber ?? -1; var ySeason = y.AirsAfterSeasonNumber ?? y.AirsBeforeSeasonNumber ?? -1;
if (xSeason != ySeason) if (xSeason != ySeason)
@ -142,8 +142,8 @@ namespace MediaBrowser.Server.Implementations.Sorting
private int CompareEpisodes(Episode x, Episode y) private int CompareEpisodes(Episode x, Episode y)
{ {
var xValue = (x.PhysicalSeasonNumber ?? -1) * 1000 + (x.IndexNumber ?? -1); var xValue = (x.ParentIndexNumber ?? -1) * 1000 + (x.IndexNumber ?? -1);
var yValue = (y.PhysicalSeasonNumber ?? -1) * 1000 + (y.IndexNumber ?? -1); var yValue = (y.ParentIndexNumber ?? -1) * 1000 + (y.IndexNumber ?? -1);
return xValue.CompareTo(yValue); return xValue.CompareTo(yValue);
} }

@ -47,7 +47,7 @@ namespace MediaBrowser.Server.Implementations.Sorting
/// <returns>DateTime.</returns> /// <returns>DateTime.</returns>
private DateTime GetDate(BaseItem x) private DateTime GetDate(BaseItem x)
{ {
var userdata = UserDataRepository.GetUserData(User.Id, x.GetUserDataKey()); var userdata = UserDataRepository.GetUserData(User, x);
if (userdata != null && userdata.LastPlayedDate.HasValue) if (userdata != null && userdata.LastPlayedDate.HasValue)
{ {

@ -34,7 +34,7 @@ namespace MediaBrowser.Server.Implementations.Sorting
/// <returns>DateTime.</returns> /// <returns>DateTime.</returns>
private int GetValue(BaseItem x) private int GetValue(BaseItem x)
{ {
var userdata = UserDataRepository.GetUserData(User.Id, x.GetUserDataKey()); var userdata = UserDataRepository.GetUserData(User, x);
return userdata == null ? 0 : userdata.PlayCount; return userdata == null ? 0 : userdata.PlayCount;
} }

@ -687,7 +687,7 @@ namespace MediaBrowser.Server.Implementations.Sync
private Task ReportOfflinePlayedItem(UserAction action) private Task ReportOfflinePlayedItem(UserAction action)
{ {
var item = _libraryManager.GetItemById(action.ItemId); var item = _libraryManager.GetItemById(action.ItemId);
var userData = _userDataManager.GetUserData(new Guid(action.UserId), item.GetUserDataKey()); var userData = _userDataManager.GetUserData(action.UserId, item);
userData.LastPlayedDate = action.Date; userData.LastPlayedDate = action.Date;
_userDataManager.UpdatePlayState(item, userData, action.PositionTicks); _userDataManager.UpdatePlayState(item, userData, action.PositionTicks);

@ -39,11 +39,11 @@ namespace MediaBrowser.Server.Implementations.Sync
_appPaths = appPaths; _appPaths = appPaths;
} }
public async Task Initialize() public async Task Initialize(IDbConnector dbConnector)
{ {
var dbFile = Path.Combine(_appPaths.DataPath, "sync14.db"); var dbFile = Path.Combine(_appPaths.DataPath, "sync14.db");
_connection = await SqliteExtensions.ConnectToDb(dbFile, Logger).ConfigureAwait(false); _connection = await dbConnector.Connect(dbFile).ConfigureAwait(false);
string[] queries = { string[] queries = {

@ -36,10 +36,25 @@ namespace MediaBrowser.Server.Implementations.TV
? new string[] { } ? new string[] { }
: new[] { request.ParentId }; : new[] { request.ParentId };
string presentationUniqueKey = null;
int? limit = null;
if (!string.IsNullOrWhiteSpace(request.SeriesId))
{
var series = _libraryManager.GetItemById(request.SeriesId);
if (series != null)
{
presentationUniqueKey = series.PresentationUniqueKey;
limit = 1;
}
}
var items = _libraryManager.GetItemList(new InternalItemsQuery(user) var items = _libraryManager.GetItemList(new InternalItemsQuery(user)
{ {
IncludeItemTypes = new[] { typeof(Series).Name }, IncludeItemTypes = new[] { typeof(Series).Name },
SortOrder = SortOrder.Ascending SortOrder = SortOrder.Ascending,
PresentationUniqueKey = presentationUniqueKey,
Limit = limit
}, parentIds).Cast<Series>(); }, parentIds).Cast<Series>();
@ -58,10 +73,25 @@ namespace MediaBrowser.Server.Implementations.TV
throw new ArgumentException("User not found"); throw new ArgumentException("User not found");
} }
string presentationUniqueKey = null;
int? limit = null;
if (!string.IsNullOrWhiteSpace(request.SeriesId))
{
var series = _libraryManager.GetItemById(request.SeriesId);
if (series != null)
{
presentationUniqueKey = series.PresentationUniqueKey;
limit = 1;
}
}
var items = _libraryManager.GetItemList(new InternalItemsQuery(user) var items = _libraryManager.GetItemList(new InternalItemsQuery(user)
{ {
IncludeItemTypes = new[] { typeof(Series).Name }, IncludeItemTypes = new[] { typeof(Series).Name },
SortOrder = SortOrder.Ascending SortOrder = SortOrder.Ascending,
PresentationUniqueKey = presentationUniqueKey,
Limit = limit
}, parentsFolders.Select(i => i.Id.ToString("N"))).Cast<Series>(); }, parentsFolders.Select(i => i.Id.ToString("N"))).Cast<Series>();
@ -76,30 +106,30 @@ namespace MediaBrowser.Server.Implementations.TV
// Avoid implicitly captured closure // Avoid implicitly captured closure
var currentUser = user; var currentUser = user;
return FilterSeries(request, series) return series
.AsParallel() .AsParallel()
.Select(i => GetNextUp(i, currentUser)) .Select(i => GetNextUp(i, currentUser))
// Include if an episode was found, and either the series is not unwatched or the specific series was requested // Include if an episode was found, and either the series is not unwatched or the specific series was requested
.Where(i => i.Item1 != null && (!i.Item3 || !string.IsNullOrWhiteSpace(request.SeriesId))) .Where(i => i.Item1 != null && (!i.Item3 || !string.IsNullOrWhiteSpace(request.SeriesId)))
.OrderByDescending(i => //.OrderByDescending(i =>
{ //{
var episode = i.Item1; // var episode = i.Item1;
var seriesUserData = _userDataManager.GetUserData(user.Id, episode.Series.GetUserDataKey()); // var seriesUserData = _userDataManager.GetUserData(user, episode.Series);
if (seriesUserData.IsFavorite) // if (seriesUserData.IsFavorite)
{ // {
return 2; // return 2;
} // }
if (seriesUserData.Likes.HasValue) // if (seriesUserData.Likes.HasValue)
{ // {
return seriesUserData.Likes.Value ? 1 : -1; // return seriesUserData.Likes.Value ? 1 : -1;
} // }
return 0; // return 0;
}) //})
.ThenByDescending(i => i.Item2) .OrderByDescending(i => i.Item2)
.ThenByDescending(i => i.Item1.PremiereDate ?? DateTime.MinValue) .ThenByDescending(i => i.Item1.PremiereDate ?? DateTime.MinValue)
.Select(i => i.Item1); .Select(i => i.Item1);
} }
@ -128,7 +158,7 @@ namespace MediaBrowser.Server.Implementations.TV
// Go back starting with the most recent episodes // Go back starting with the most recent episodes
foreach (var episode in allEpisodes) foreach (var episode in allEpisodes)
{ {
var userData = _userDataManager.GetUserData(user.Id, episode.GetUserDataKey()); var userData = _userDataManager.GetUserData(user, episode);
if (userData.Played) if (userData.Played)
{ {
@ -142,7 +172,7 @@ namespace MediaBrowser.Server.Implementations.TV
} }
else else
{ {
if (!episode.IsVirtualUnaired && (!episode.IsMissingEpisode || includeMissing)) if (!episode.IsVirtualUnaired && (includeMissing || !episode.IsMissingEpisode))
{ {
nextUp = episode; nextUp = episode;
} }
@ -154,24 +184,12 @@ namespace MediaBrowser.Server.Implementations.TV
return new Tuple<Episode, DateTime, bool>(nextUp, lastWatchedDate, false); return new Tuple<Episode, DateTime, bool>(nextUp, lastWatchedDate, false);
} }
var firstEpisode = allEpisodes.LastOrDefault(i => !i.IsVirtualUnaired && (!i.IsMissingEpisode || includeMissing) && !i.IsPlayed(user)); var firstEpisode = allEpisodes.LastOrDefault(i => !i.IsVirtualUnaired && (includeMissing || !i.IsMissingEpisode) && !i.IsPlayed(user));
// Return the first episode // Return the first episode
return new Tuple<Episode, DateTime, bool>(firstEpisode, DateTime.MinValue, true); return new Tuple<Episode, DateTime, bool>(firstEpisode, DateTime.MinValue, true);
} }
private IEnumerable<Series> FilterSeries(NextUpQuery request, IEnumerable<Series> items)
{
if (!string.IsNullOrWhiteSpace(request.SeriesId))
{
var id = new Guid(request.SeriesId);
items = items.Where(i => i.Id == id);
}
return items;
}
private QueryResult<BaseItem> GetResult(IEnumerable<BaseItem> items, int? totalRecordLimit, NextUpQuery query) private QueryResult<BaseItem> GetResult(IEnumerable<BaseItem> items, int? totalRecordLimit, NextUpQuery query)
{ {
var itemsArray = totalRecordLimit.HasValue ? items.Take(totalRecordLimit.Value).ToArray() : items.ToArray(); var itemsArray = totalRecordLimit.HasValue ? items.Take(totalRecordLimit.Value).ToArray() : items.ToArray();

@ -76,12 +76,17 @@
<Reference Include="MediaBrowser.IsoMounting.Linux"> <Reference Include="MediaBrowser.IsoMounting.Linux">
<HintPath>..\ThirdParty\MediaBrowser.IsoMounting.Linux\MediaBrowser.IsoMounting.Linux.dll</HintPath> <HintPath>..\ThirdParty\MediaBrowser.IsoMounting.Linux\MediaBrowser.IsoMounting.Linux.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Data" />
<Reference Include="System.Data.SQLite">
<HintPath>..\ThirdParty\System.Data.SQLite.ManagedOnly\1.0.94.0\System.Data.SQLite.dll</HintPath>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\SharedVersion.cs"> <Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link> <Link>Properties\SharedVersion.cs</Link>
</Compile> </Compile>
<Compile Include="Native\BaseMonoApp.cs" /> <Compile Include="Native\BaseMonoApp.cs" />
<Compile Include="Native\SqliteExtensions.cs" />
<Compile Include="Networking\CertificateGenerator.cs" /> <Compile Include="Networking\CertificateGenerator.cs" />
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />

@ -9,6 +9,7 @@ using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using MediaBrowser.Controller.Power; using MediaBrowser.Controller.Power;
using MediaBrowser.Server.Implementations.Persistence;
using MediaBrowser.Server.Startup.Common.FFMpeg; using MediaBrowser.Server.Startup.Common.FFMpeg;
using OperatingSystem = MediaBrowser.Server.Startup.Common.OperatingSystem; using OperatingSystem = MediaBrowser.Server.Startup.Common.OperatingSystem;
@ -17,9 +18,12 @@ namespace MediaBrowser.Server.Mono.Native
public abstract class BaseMonoApp : INativeApp public abstract class BaseMonoApp : INativeApp
{ {
protected StartupOptions StartupOptions { get; private set; } protected StartupOptions StartupOptions { get; private set; }
protected BaseMonoApp(StartupOptions startupOptions) protected ILogger Logger { get; private set; }
protected BaseMonoApp(StartupOptions startupOptions, ILogger logger)
{ {
StartupOptions = startupOptions; StartupOptions = startupOptions;
Logger = logger;
} }
/// <summary> /// <summary>
@ -227,6 +231,11 @@ namespace MediaBrowser.Server.Mono.Native
throw new NotImplementedException(); throw new NotImplementedException();
} }
public IDbConnector GetDbConnector()
{
return new DbConnector(Logger);
}
public static FFMpegInstallInfo GetInfo(NativeEnvironment environment) public static FFMpegInstallInfo GetInfo(NativeEnvironment environment)
{ {
var info = new FFMpegInstallInfo(); var info = new FFMpegInstallInfo();

@ -1,4 +1,5 @@
using MediaBrowser.Server.Startup.Common; using MediaBrowser.Model.Logging;
using MediaBrowser.Server.Startup.Common;
namespace MediaBrowser.Server.Mono.Native namespace MediaBrowser.Server.Mono.Native
{ {
@ -7,8 +8,8 @@ namespace MediaBrowser.Server.Mono.Native
/// </summary> /// </summary>
internal class NativeApp : BaseMonoApp internal class NativeApp : BaseMonoApp
{ {
public NativeApp(StartupOptions startupOptions) public NativeApp(StartupOptions startupOptions, ILogger logger)
: base(startupOptions) : base(startupOptions, logger)
{ {
} }

@ -0,0 +1,62 @@
using System;
using System.Data;
using System.Data.SQLite;
using System.Threading.Tasks;
using MediaBrowser.Model.Logging;
using MediaBrowser.Server.Implementations.Persistence;
namespace MediaBrowser.Server.Mono.Native
{
/// <summary>
/// Class SQLiteExtensions
/// </summary>
static class SqliteExtensions
{
/// <summary>
/// Connects to db.
/// </summary>
/// <param name="dbPath">The db path.</param>
/// <param name="logger">The logger.</param>
/// <returns>Task{IDbConnection}.</returns>
/// <exception cref="System.ArgumentNullException">dbPath</exception>
public static async Task<IDbConnection> ConnectToDb(string dbPath, ILogger logger)
{
if (string.IsNullOrEmpty(dbPath))
{
throw new ArgumentNullException("dbPath");
}
logger.Info("Sqlite {0} opening {1}", SQLiteConnection.SQLiteVersion, dbPath);
var connectionstr = new SQLiteConnectionStringBuilder
{
PageSize = 4096,
CacheSize = 2000,
SyncMode = SynchronizationModes.Full,
DataSource = dbPath,
JournalMode = SQLiteJournalModeEnum.Wal
};
var connection = new SQLiteConnection(connectionstr.ConnectionString);
await connection.OpenAsync().ConfigureAwait(false);
return connection;
}
}
public class DbConnector : IDbConnector
{
private readonly ILogger _logger;
public DbConnector(ILogger logger)
{
_logger = logger;
}
public Task<IDbConnection> Connect(string dbPath)
{
return SqliteExtensions.ConnectToDb(dbPath, _logger);
}
}
}

@ -79,7 +79,7 @@ namespace MediaBrowser.Server.Mono
var fileSystem = new ManagedFileSystem(new PatternsLogger(logManager.GetLogger("FileSystem")), false, false); var fileSystem = new ManagedFileSystem(new PatternsLogger(logManager.GetLogger("FileSystem")), false, false);
fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem)); fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
var nativeApp = new NativeApp(options); var nativeApp = new NativeApp(options, logManager.GetLogger("App"));
_appHost = new ApplicationHost(appPaths, logManager, options, fileSystem, "emby.mono.zip", nativeApp); _appHost = new ApplicationHost(appPaths, logManager, options, fileSystem, "emby.mono.zip", nativeApp);

@ -410,13 +410,16 @@ namespace MediaBrowser.Server.Startup.Common
UserRepository = await GetUserRepository().ConfigureAwait(false); UserRepository = await GetUserRepository().ConfigureAwait(false);
RegisterSingleInstance(UserRepository); RegisterSingleInstance(UserRepository);
DisplayPreferencesRepository = new SqliteDisplayPreferencesRepository(LogManager, JsonSerializer, ApplicationPaths); var displayPreferencesRepo = new SqliteDisplayPreferencesRepository(LogManager, JsonSerializer, ApplicationPaths);
DisplayPreferencesRepository = displayPreferencesRepo;
RegisterSingleInstance(DisplayPreferencesRepository); RegisterSingleInstance(DisplayPreferencesRepository);
ItemRepository = new SqliteItemRepository(ApplicationPaths, JsonSerializer, LogManager); var itemRepo = new SqliteItemRepository(ServerConfigurationManager, JsonSerializer, LogManager);
ItemRepository = itemRepo;
RegisterSingleInstance(ItemRepository); RegisterSingleInstance(ItemRepository);
ProviderRepository = new SqliteProviderInfoRepository(LogManager, ApplicationPaths); var providerRepo = new SqliteProviderInfoRepository(LogManager, ApplicationPaths);
ProviderRepository = providerRepo;
RegisterSingleInstance(ProviderRepository); RegisterSingleInstance(ProviderRepository);
FileOrganizationRepository = await GetFileOrganizationRepository().ConfigureAwait(false); FileOrganizationRepository = await GetFileOrganizationRepository().ConfigureAwait(false);
@ -541,7 +544,7 @@ namespace MediaBrowser.Server.Startup.Common
RegisterSingleInstance(NativeApp.GetPowerManagement()); RegisterSingleInstance(NativeApp.GetPowerManagement());
var sharingRepo = new SharingRepository(LogManager, ApplicationPaths); var sharingRepo = new SharingRepository(LogManager, ApplicationPaths);
await sharingRepo.Initialize().ConfigureAwait(false); await sharingRepo.Initialize(NativeApp.GetDbConnector()).ConfigureAwait(false);
RegisterSingleInstance<ISharingManager>(new SharingManager(sharingRepo, ServerConfigurationManager, LibraryManager, this)); RegisterSingleInstance<ISharingManager>(new SharingManager(sharingRepo, ServerConfigurationManager, LibraryManager, this));
RegisterSingleInstance<ISsdpHandler>(new SsdpHandler(LogManager.GetLogger("SsdpHandler"), ServerConfigurationManager, this)); RegisterSingleInstance<ISsdpHandler>(new SsdpHandler(LogManager.GetLogger("SsdpHandler"), ServerConfigurationManager, this));
@ -557,9 +560,11 @@ namespace MediaBrowser.Server.Startup.Common
SubtitleEncoder = new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager); SubtitleEncoder = new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager);
RegisterSingleInstance(SubtitleEncoder); RegisterSingleInstance(SubtitleEncoder);
await ConfigureDisplayPreferencesRepositories().ConfigureAwait(false); await displayPreferencesRepo.Initialize(NativeApp.GetDbConnector()).ConfigureAwait(false);
await ConfigureItemRepositories().ConfigureAwait(false); await itemRepo.Initialize(NativeApp.GetDbConnector()).ConfigureAwait(false);
await providerRepo.Initialize(NativeApp.GetDbConnector()).ConfigureAwait(false);
((LibraryManager)LibraryManager).ItemRepository = ItemRepository;
await ConfigureUserDataRepositories().ConfigureAwait(false); await ConfigureUserDataRepositories().ConfigureAwait(false);
await ConfigureNotificationsRepository().ConfigureAwait(false); await ConfigureNotificationsRepository().ConfigureAwait(false);
progress.Report(100); progress.Report(100);
@ -658,7 +663,7 @@ namespace MediaBrowser.Server.Startup.Common
{ {
var repo = new SqliteUserRepository(LogManager, ApplicationPaths, JsonSerializer); var repo = new SqliteUserRepository(LogManager, ApplicationPaths, JsonSerializer);
await repo.Initialize().ConfigureAwait(false); await repo.Initialize(NativeApp.GetDbConnector()).ConfigureAwait(false);
return repo; return repo;
} }
@ -677,7 +682,7 @@ namespace MediaBrowser.Server.Startup.Common
{ {
var repo = new SqliteFileOrganizationRepository(LogManager, ServerConfigurationManager.ApplicationPaths); var repo = new SqliteFileOrganizationRepository(LogManager, ServerConfigurationManager.ApplicationPaths);
await repo.Initialize().ConfigureAwait(false); await repo.Initialize(NativeApp.GetDbConnector()).ConfigureAwait(false);
return repo; return repo;
} }
@ -686,7 +691,7 @@ namespace MediaBrowser.Server.Startup.Common
{ {
var repo = new AuthenticationRepository(LogManager, ServerConfigurationManager.ApplicationPaths); var repo = new AuthenticationRepository(LogManager, ServerConfigurationManager.ApplicationPaths);
await repo.Initialize().ConfigureAwait(false); await repo.Initialize(NativeApp.GetDbConnector()).ConfigureAwait(false);
return repo; return repo;
} }
@ -695,7 +700,7 @@ namespace MediaBrowser.Server.Startup.Common
{ {
var repo = new ActivityRepository(LogManager, ServerConfigurationManager.ApplicationPaths); var repo = new ActivityRepository(LogManager, ServerConfigurationManager.ApplicationPaths);
await repo.Initialize().ConfigureAwait(false); await repo.Initialize(NativeApp.GetDbConnector()).ConfigureAwait(false);
return repo; return repo;
} }
@ -704,7 +709,7 @@ namespace MediaBrowser.Server.Startup.Common
{ {
var repo = new SyncRepository(LogManager, JsonSerializer, ServerConfigurationManager.ApplicationPaths); var repo = new SyncRepository(LogManager, JsonSerializer, ServerConfigurationManager.ApplicationPaths);
await repo.Initialize().ConfigureAwait(false); await repo.Initialize(NativeApp.GetDbConnector()).ConfigureAwait(false);
return repo; return repo;
} }
@ -717,35 +722,13 @@ namespace MediaBrowser.Server.Startup.Common
{ {
var repo = new SqliteNotificationsRepository(LogManager, ApplicationPaths); var repo = new SqliteNotificationsRepository(LogManager, ApplicationPaths);
await repo.Initialize().ConfigureAwait(false); await repo.Initialize(NativeApp.GetDbConnector()).ConfigureAwait(false);
NotificationsRepository = repo; NotificationsRepository = repo;
RegisterSingleInstance(NotificationsRepository); RegisterSingleInstance(NotificationsRepository);
} }
/// <summary>
/// Configures the repositories.
/// </summary>
/// <returns>Task.</returns>
private async Task ConfigureDisplayPreferencesRepositories()
{
await DisplayPreferencesRepository.Initialize().ConfigureAwait(false);
}
/// <summary>
/// Configures the item repositories.
/// </summary>
/// <returns>Task.</returns>
private async Task ConfigureItemRepositories()
{
await ItemRepository.Initialize().ConfigureAwait(false);
await ProviderRepository.Initialize().ConfigureAwait(false);
((LibraryManager)LibraryManager).ItemRepository = ItemRepository;
}
/// <summary> /// <summary>
/// Configures the user data repositories. /// Configures the user data repositories.
/// </summary> /// </summary>
@ -754,7 +737,7 @@ namespace MediaBrowser.Server.Startup.Common
{ {
var repo = new SqliteUserDataRepository(LogManager, ApplicationPaths); var repo = new SqliteUserDataRepository(LogManager, ApplicationPaths);
await repo.Initialize().ConfigureAwait(false); await repo.Initialize(NativeApp.GetDbConnector()).ConfigureAwait(false);
((UserDataManager)UserDataManager).Repository = repo; ((UserDataManager)UserDataManager).Repository = repo;
} }

@ -3,6 +3,7 @@ using MediaBrowser.Model.Logging;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using MediaBrowser.Controller.Power; using MediaBrowser.Controller.Power;
using MediaBrowser.Server.Implementations.Persistence;
using MediaBrowser.Server.Startup.Common.FFMpeg; using MediaBrowser.Server.Startup.Common.FFMpeg;
namespace MediaBrowser.Server.Startup.Common namespace MediaBrowser.Server.Startup.Common
@ -104,5 +105,7 @@ namespace MediaBrowser.Server.Startup.Common
FFMpegInstallInfo GetFfmpegInstallInfo(); FFMpegInstallInfo GetFfmpegInstallInfo();
void LaunchUrl(string url); void LaunchUrl(string url);
IDbConnector GetDbConnector();
} }
} }

@ -13,6 +13,8 @@
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir> <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<TargetFrameworkProfile /> <TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
@ -81,9 +83,9 @@
<Reference Include="System.Configuration" /> <Reference Include="System.Configuration" />
<Reference Include="System.Configuration.Install" /> <Reference Include="System.Configuration.Install" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Data.SQLite, Version=1.0.94.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL"> <Reference Include="System.Data.SQLite, Version=1.0.101.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <HintPath>..\packages\System.Data.SQLite.Core.1.0.101.0\lib\net46\System.Data.SQLite.dll</HintPath>
<HintPath>..\packages\System.Data.SQLite.Core.1.0.94.0\lib\net45\System.Data.SQLite.dll</HintPath> <Private>True</Private>
</Reference> </Reference>
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
<Reference Include="System.ServiceProcess" /> <Reference Include="System.ServiceProcess" />
@ -112,6 +114,7 @@
</Compile> </Compile>
<Compile Include="MainStartup.cs" /> <Compile Include="MainStartup.cs" />
<Compile Include="Native\LnkShortcutHandler.cs" /> <Compile Include="Native\LnkShortcutHandler.cs" />
<Compile Include="Native\SqliteExtensions.cs" />
<Compile Include="Native\Standby.cs" /> <Compile Include="Native\Standby.cs" />
<Compile Include="Native\ServerAuthorization.cs" /> <Compile Include="Native\ServerAuthorization.cs" />
<Compile Include="Native\WindowsApp.cs" /> <Compile Include="Native\WindowsApp.cs" />
@ -156,14 +159,6 @@
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="..\packages\System.Data.SQLite.Core.1.0.94.0\build\net45\x64\SQLite.Interop.dll">
<Link>x64\SQLite.Interop.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\packages\System.Data.SQLite.Core.1.0.94.0\build\net45\x86\SQLite.Interop.dll">
<Link>x86\SQLite.Interop.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\Tools\Installation\MediaBrowser.InstallUtil.dll"> <Content Include="..\Tools\Installation\MediaBrowser.InstallUtil.dll">
<Link>MediaBrowser.InstallUtil.dll</Link> <Link>MediaBrowser.InstallUtil.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@ -1116,6 +1111,13 @@
<PostBuildEvent> <PostBuildEvent>
</PostBuildEvent> </PostBuildEvent>
</PropertyGroup> </PropertyGroup>
<Import Project="..\packages\System.Data.SQLite.Core.1.0.101.0\build\net46\System.Data.SQLite.Core.targets" Condition="Exists('..\packages\System.Data.SQLite.Core.1.0.101.0\build\net46\System.Data.SQLite.Core.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\System.Data.SQLite.Core.1.0.101.0\build\net46\System.Data.SQLite.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\System.Data.SQLite.Core.1.0.101.0\build\net46\System.Data.SQLite.Core.targets'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">
@ -1123,5 +1125,4 @@
<Target Name="AfterBuild"> <Target Name="AfterBuild">
</Target> </Target>
--> -->
<Import Project="..\packages\System.Data.SQLite.Core.1.0.94.0\build\net45\System.Data.SQLite.Core.targets" Condition="Exists('..\packages\System.Data.SQLite.Core.1.0.94.0\build\net45\System.Data.SQLite.Core.targets')" />
</Project> </Project>

@ -0,0 +1,62 @@
using System;
using System.Data;
using System.Data.SQLite;
using System.Threading.Tasks;
using MediaBrowser.Model.Logging;
using MediaBrowser.Server.Implementations.Persistence;
namespace MediaBrowser.ServerApplication.Native
{
/// <summary>
/// Class SQLiteExtensions
/// </summary>
static class SqliteExtensions
{
/// <summary>
/// Connects to db.
/// </summary>
/// <param name="dbPath">The db path.</param>
/// <param name="logger">The logger.</param>
/// <returns>Task{IDbConnection}.</returns>
/// <exception cref="System.ArgumentNullException">dbPath</exception>
public static async Task<IDbConnection> ConnectToDb(string dbPath, ILogger logger)
{
if (string.IsNullOrEmpty(dbPath))
{
throw new ArgumentNullException("dbPath");
}
logger.Info("Sqlite {0} opening {1}", SQLiteConnection.SQLiteVersion, dbPath);
var connectionstr = new SQLiteConnectionStringBuilder
{
PageSize = 4096,
CacheSize = 2000,
SyncMode = SynchronizationModes.Full,
DataSource = dbPath,
JournalMode = SQLiteJournalModeEnum.Wal
};
var connection = new SQLiteConnection(connectionstr.ConnectionString);
await connection.OpenAsync().ConfigureAwait(false);
return connection;
}
}
public class DbConnector : IDbConnector
{
private readonly ILogger _logger;
public DbConnector(ILogger logger)
{
_logger = logger;
}
public Task<IDbConnection> Connect(string dbPath)
{
return SqliteExtensions.ConnectToDb(dbPath, _logger);
}
}
}

@ -10,6 +10,7 @@ using System.Reflection;
using System.Windows.Forms; using System.Windows.Forms;
using CommonIO; using CommonIO;
using MediaBrowser.Controller.Power; using MediaBrowser.Controller.Power;
using MediaBrowser.Server.Implementations.Persistence;
using MediaBrowser.Server.Startup.Common.FFMpeg; using MediaBrowser.Server.Startup.Common.FFMpeg;
using OperatingSystem = MediaBrowser.Server.Startup.Common.OperatingSystem; using OperatingSystem = MediaBrowser.Server.Startup.Common.OperatingSystem;
@ -191,6 +192,11 @@ namespace MediaBrowser.ServerApplication.Native
} }
} }
public IDbConnector GetDbConnector()
{
return new DbConnector(_logger);
}
/// <summary> /// <summary>
/// Processes the exited. /// Processes the exited.
/// </summary> /// </summary>

@ -3,5 +3,5 @@
<package id="CommonIO" version="1.0.0.9" targetFramework="net45" /> <package id="CommonIO" version="1.0.0.9" targetFramework="net45" />
<package id="ImageMagickSharp" version="1.0.0.18" targetFramework="net45" /> <package id="ImageMagickSharp" version="1.0.0.18" targetFramework="net45" />
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" /> <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
<package id="System.Data.SQLite.Core" version="1.0.94.0" targetFramework="net45" requireReinstallation="true" /> <package id="System.Data.SQLite.Core" version="1.0.101.0" targetFramework="net46" />
</packages> </packages>

@ -936,7 +936,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
return; return;
} }
var userdata = userDataRepo.GetUserData(user.Id, item.GetUserDataKey()); var userdata = userDataRepo.GetUserData(user, item);
writer.WriteElementString("isuserfavorite", userdata.IsFavorite.ToString().ToLower()); writer.WriteElementString("isuserfavorite", userdata.IsFavorite.ToString().ToLower());

@ -1,4 +1,4 @@
using System.Reflection; using System.Reflection;
//[assembly: AssemblyVersion("3.0.*")] [assembly: AssemblyVersion("3.0.*")]
[assembly: AssemblyVersion("3.0.5947")] //[assembly: AssemblyVersion("3.0.5930")]

Loading…
Cancel
Save