Merge remote-tracking branch 'upstream/dev' into dev
This commit is contained in:
commit
4c52fc094f
|
@ -80,7 +80,7 @@ namespace MediaBrowser.Api
|
|||
.OrderBy(i => i)
|
||||
.ToArray();
|
||||
|
||||
result.Tags = items.OfType<IHasTags>()
|
||||
result.Tags = items
|
||||
.SelectMany(i => i.Tags)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.OrderBy(i => i)
|
||||
|
|
|
@ -280,11 +280,7 @@ namespace MediaBrowser.Api
|
|||
episode.AbsoluteEpisodeNumber = request.AbsoluteEpisodeNumber;
|
||||
}
|
||||
|
||||
var hasTags = item as IHasTags;
|
||||
if (hasTags != null)
|
||||
{
|
||||
hasTags.Tags = request.Tags;
|
||||
}
|
||||
item.Tags = request.Tags;
|
||||
|
||||
var hasTaglines = item as IHasTaglines;
|
||||
if (hasTaglines != null)
|
||||
|
|
|
@ -439,6 +439,12 @@ namespace MediaBrowser.Api.LiveTv
|
|||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
[Route("/LiveTv/ListingProviders/Default", "GET")]
|
||||
[Authenticated(AllowBeforeStartupWizard = true)]
|
||||
public class GetDefaultListingProvider : ListingsProviderInfo, IReturn<ListingsProviderInfo>
|
||||
{
|
||||
}
|
||||
|
||||
[Route("/LiveTv/ListingProviders", "POST", Summary = "Adds a listing provider")]
|
||||
[Authenticated(AllowBeforeStartupWizard = true)]
|
||||
public class AddListingProvider : ListingsProviderInfo, IReturn<ListingsProviderInfo>
|
||||
|
@ -525,6 +531,11 @@ namespace MediaBrowser.Api.LiveTv
|
|||
_dtoService = dtoService;
|
||||
}
|
||||
|
||||
public object Get(GetDefaultListingProvider request)
|
||||
{
|
||||
return ToOptimizedResult(new ListingsProviderInfo());
|
||||
}
|
||||
|
||||
public async Task<object> Get(GetSatChannnelScanResult request)
|
||||
{
|
||||
var result = await _liveTvManager.GetSatChannelScanResult(request, CancellationToken.None).ConfigureAwait(false);
|
||||
|
|
|
@ -116,13 +116,7 @@ namespace MediaBrowser.Api
|
|||
|
||||
private static IEnumerable<string> GetTags(BaseItem item)
|
||||
{
|
||||
var hasTags = item as IHasTags;
|
||||
if (hasTags != null)
|
||||
{
|
||||
return hasTags.Tags;
|
||||
}
|
||||
|
||||
return new List<string>();
|
||||
return item.Tags;
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetKeywords(BaseItem item)
|
||||
|
|
|
@ -113,7 +113,8 @@ namespace MediaBrowser.Api
|
|||
config.EnableCustomPathSubFolders = true;
|
||||
config.EnableStandaloneMusicKeys = true;
|
||||
config.EnableCaseSensitiveItemIds = true;
|
||||
config.SchemaVersion = 79;
|
||||
config.EnableFolderView = true;
|
||||
config.SchemaVersion = 89;
|
||||
}
|
||||
|
||||
public void Post(UpdateStartupConfiguration request)
|
||||
|
|
|
@ -101,6 +101,7 @@ namespace MediaBrowser.Api.Subtitles
|
|||
|
||||
[ApiMember(Name = "CopyTimestamps", Description = "CopyTimestamps", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||
public bool CopyTimestamps { get; set; }
|
||||
public bool AddVttTimeMap { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Videos/{Id}/{MediaSourceId}/Subtitles/{Index}/subtitles.m3u8", "GET", Summary = "Gets an HLS subtitle playlist.")]
|
||||
|
@ -178,7 +179,7 @@ namespace MediaBrowser.Api.Subtitles
|
|||
|
||||
var endPositionTicks = Math.Min(runtime, positionTicks + segmentLengthTicks);
|
||||
|
||||
var url = string.Format("stream.vtt?CopyTimestamps=true,StartPositionTicks={0}&EndPositionTicks={1}&api_key={2}",
|
||||
var url = string.Format("stream.vtt?CopyTimestamps=true&AddVttTimeMap=true&StartPositionTicks={0}&EndPositionTicks={1}&api_key={2}",
|
||||
positionTicks.ToString(CultureInfo.InvariantCulture),
|
||||
endPositionTicks.ToString(CultureInfo.InvariantCulture),
|
||||
accessToken);
|
||||
|
@ -193,7 +194,7 @@ namespace MediaBrowser.Api.Subtitles
|
|||
return ResultFactory.GetResult(builder.ToString(), MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
|
||||
}
|
||||
|
||||
public object Get(GetSubtitle request)
|
||||
public async Task<object> Get(GetSubtitle request)
|
||||
{
|
||||
if (string.Equals(request.Format, "js", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
|
@ -212,21 +213,32 @@ namespace MediaBrowser.Api.Subtitles
|
|||
return ToStaticFileResult(subtitleStream.Path);
|
||||
}
|
||||
|
||||
var stream = GetSubtitles(request).Result;
|
||||
using (var stream = await GetSubtitles(request).ConfigureAwait(false))
|
||||
{
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
var text = reader.ReadToEnd();
|
||||
|
||||
return ResultFactory.GetResult(stream, MimeTypes.GetMimeType("file." + request.Format));
|
||||
if (string.Equals(request.Format, "vtt", StringComparison.OrdinalIgnoreCase) && request.AddVttTimeMap)
|
||||
{
|
||||
text = text.Replace("WEBVTT", "WEBVTT\nX-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000");
|
||||
}
|
||||
|
||||
return ResultFactory.GetResult(text, MimeTypes.GetMimeType("file." + request.Format));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Stream> GetSubtitles(GetSubtitle request)
|
||||
private Task<Stream> GetSubtitles(GetSubtitle request)
|
||||
{
|
||||
return await _subtitleEncoder.GetSubtitles(request.Id,
|
||||
return _subtitleEncoder.GetSubtitles(request.Id,
|
||||
request.MediaSourceId,
|
||||
request.Index,
|
||||
request.Format,
|
||||
request.StartPositionTicks,
|
||||
request.EndPositionTicks,
|
||||
request.CopyTimestamps,
|
||||
CancellationToken.None).ConfigureAwait(false);
|
||||
CancellationToken.None);
|
||||
}
|
||||
|
||||
public object Get(SearchRemoteSubtitles request)
|
||||
|
|
|
@ -333,12 +333,7 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
var tags = request.GetTags();
|
||||
if (tags.Length > 0)
|
||||
{
|
||||
var hasTags = i as IHasTags;
|
||||
if (hasTags == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!tags.Any(v => hasTags.Tags.Contains(v, StringComparer.OrdinalIgnoreCase)))
|
||||
if (!tags.Any(v => i.Tags.Contains(v, StringComparer.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||
IHasArtist,
|
||||
IHasMusicGenres,
|
||||
IHasLookupInfo<SongInfo>,
|
||||
IHasTags,
|
||||
IHasMediaSources,
|
||||
IThemeMedia,
|
||||
IArchivable
|
||||
|
|
|
@ -1397,15 +1397,10 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
private bool IsVisibleViaTags(User user)
|
||||
{
|
||||
var hasTags = this as IHasTags;
|
||||
|
||||
if (hasTags != null)
|
||||
var policy = user.Policy;
|
||||
if (policy.BlockedTags.Any(i => Tags.Contains(i, StringComparer.OrdinalIgnoreCase)))
|
||||
{
|
||||
var policy = user.Policy;
|
||||
if (policy.BlockedTags.Any(i => hasTags.Tags.Contains(i, StringComparer.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -6,7 +6,7 @@ using MediaBrowser.Model.Entities;
|
|||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
public class Book : BaseItem, IHasTags, IHasLookupInfo<BookInfo>, IHasSeries
|
||||
public class Book : BaseItem, IHasLookupInfo<BookInfo>, IHasSeries
|
||||
{
|
||||
[IgnoreDataMember]
|
||||
public override string MediaType
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// <summary>
|
||||
/// Class Folder
|
||||
/// </summary>
|
||||
public class Folder : BaseItem, IHasThemeMedia, IHasTags
|
||||
public class Folder : BaseItem, IHasThemeMedia
|
||||
{
|
||||
public static IUserManager UserManager { get; set; }
|
||||
public static IUserViewManager UserViewManager { get; set; }
|
||||
|
@ -164,49 +164,15 @@ namespace MediaBrowser.Controller.Entities
|
|||
item.DateModified = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
AddChildInternal(item.Id);
|
||||
|
||||
await LibraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
protected void AddChildrenInternal(List<Guid> children)
|
||||
{
|
||||
lock (_childrenSyncLock)
|
||||
{
|
||||
var newChildren = ChildIds.ToList();
|
||||
newChildren.AddRange(children);
|
||||
_children = newChildren.ToList();
|
||||
}
|
||||
}
|
||||
protected void AddChildInternal(Guid child)
|
||||
{
|
||||
lock (_childrenSyncLock)
|
||||
{
|
||||
var childIds = ChildIds.ToList();
|
||||
if (!childIds.Contains(child))
|
||||
{
|
||||
childIds.Add(child);
|
||||
_children = childIds.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void RemoveChildrenInternal(List<Guid> children)
|
||||
{
|
||||
lock (_childrenSyncLock)
|
||||
{
|
||||
_children = ChildIds.Except(children).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the child.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
public void RemoveChild(BaseItem item)
|
||||
{
|
||||
RemoveChildrenInternal(new[] { item.Id }.ToList());
|
||||
|
||||
item.SetParent(null);
|
||||
}
|
||||
|
||||
|
@ -241,33 +207,6 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// The children
|
||||
/// </summary>
|
||||
private IReadOnlyList<Guid> _children;
|
||||
/// <summary>
|
||||
/// The _children sync lock
|
||||
/// </summary>
|
||||
private readonly object _childrenSyncLock = new object();
|
||||
/// <summary>
|
||||
/// Gets or sets the actual children.
|
||||
/// </summary>
|
||||
/// <value>The actual children.</value>
|
||||
protected virtual IEnumerable<Guid> ChildIds
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_childrenSyncLock)
|
||||
{
|
||||
if (_children == null)
|
||||
{
|
||||
_children = LoadChildren().ToList();
|
||||
}
|
||||
return _children.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actual children.
|
||||
/// </summary>
|
||||
|
@ -277,7 +216,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
{
|
||||
get
|
||||
{
|
||||
return ChildIds.Select(LibraryManager.GetItemById).Where(i => i != null);
|
||||
return LoadChildren().Select(LibraryManager.GetItemById).Where(i => i != null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -479,8 +418,6 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
if (actualRemovals.Count > 0)
|
||||
{
|
||||
RemoveChildrenInternal(actualRemovals.Select(i => i.Id).ToList());
|
||||
|
||||
foreach (var item in actualRemovals)
|
||||
{
|
||||
Logger.Debug("Removed item: " + item.Path);
|
||||
|
@ -493,8 +430,6 @@ namespace MediaBrowser.Controller.Entities
|
|||
}
|
||||
|
||||
await LibraryManager.CreateItems(newItems, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
AddChildrenInternal(newItems.Select(i => i.Id).ToList());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -766,7 +701,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
{
|
||||
if (!(this is ICollectionFolder))
|
||||
{
|
||||
Logger.Debug("Query requires post-filtering due to LinkedChildren");
|
||||
Logger.Debug("Query requires post-filtering due to LinkedChildren. Type: " + GetType().Name);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -889,12 +824,6 @@ namespace MediaBrowser.Controller.Entities
|
|||
return true;
|
||||
}
|
||||
|
||||
if (query.ImageTypes.Length > 0)
|
||||
{
|
||||
Logger.Debug("Query requires post-filtering due to ImageTypes");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Apply studio filter
|
||||
if (query.StudioIds.Length > 0)
|
||||
{
|
||||
|
@ -946,7 +875,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
return true;
|
||||
}
|
||||
|
||||
if (UserViewBuilder.CollapseBoxSetItems(query, this, query.User))
|
||||
if (UserViewBuilder.CollapseBoxSetItems(query, this, query.User, ConfigurationManager))
|
||||
{
|
||||
Logger.Debug("Query requires post-filtering due to CollapseBoxSetItems");
|
||||
return true;
|
||||
|
@ -1054,7 +983,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
protected QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items, InternalItemsQuery query)
|
||||
{
|
||||
return UserViewBuilder.PostFilterAndSort(items, this, null, query, LibraryManager);
|
||||
return UserViewBuilder.PostFilterAndSort(items, this, null, query, LibraryManager, ConfigurationManager);
|
||||
}
|
||||
|
||||
public virtual IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren)
|
||||
|
|
|
@ -7,7 +7,7 @@ using System.Linq;
|
|||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
public class Game : BaseItem, IHasTrailers, IHasThemeMedia, IHasTags, IHasScreenshots, ISupportsPlaceHolders, IHasLookupInfo<GameInfo>
|
||||
public class Game : BaseItem, IHasTrailers, IHasThemeMedia, IHasScreenshots, ISupportsPlaceHolders, IHasLookupInfo<GameInfo>
|
||||
{
|
||||
public List<Guid> ThemeSongIds { get; set; }
|
||||
public List<Guid> ThemeVideoIds { get; set; }
|
||||
|
|
|
@ -5,7 +5,7 @@ using System.Runtime.Serialization;
|
|||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
public class Photo : BaseItem, IHasTags, IHasTaglines
|
||||
public class Photo : BaseItem, IHasTaglines
|
||||
{
|
||||
public List<string> Taglines { get; set; }
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// <summary>
|
||||
/// Class Studio
|
||||
/// </summary>
|
||||
public class Studio : BaseItem, IItemByName, IHasTags
|
||||
public class Studio : BaseItem, IItemByName
|
||||
{
|
||||
public override List<string> GetUserDataKeys()
|
||||
{
|
||||
|
|
|
@ -108,7 +108,13 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||
var series = Series;
|
||||
if (series != null && ParentIndexNumber.HasValue && IndexNumber.HasValue)
|
||||
{
|
||||
list.InsertRange(0, series.GetUserDataKeys().Select(i => i + ParentIndexNumber.Value.ToString("000") + IndexNumber.Value.ToString("000")));
|
||||
var seriesUserDataKeys = series.GetUserDataKeys();
|
||||
var take = seriesUserDataKeys.Count;
|
||||
if (seriesUserDataKeys.Count > 1)
|
||||
{
|
||||
take--;
|
||||
}
|
||||
list.InsertRange(0, seriesUserDataKeys.Take(take).Select(i => i + ParentIndexNumber.Value.ToString("000") + IndexNumber.Value.ToString("000")));
|
||||
}
|
||||
|
||||
return list;
|
||||
|
|
|
@ -196,52 +196,17 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||
{
|
||||
var config = user.Configuration;
|
||||
|
||||
return GetEpisodes(user, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes);
|
||||
return GetEpisodes(Series, user, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes);
|
||||
}
|
||||
|
||||
public IEnumerable<Episode> GetEpisodes(User user, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes)
|
||||
public IEnumerable<Episode> GetEpisodes(Series series, User user, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes)
|
||||
{
|
||||
var series = Series;
|
||||
return GetEpisodes(series, user, includeMissingEpisodes, includeVirtualUnairedEpisodes, null);
|
||||
}
|
||||
|
||||
if (IndexNumber.HasValue && series != null)
|
||||
{
|
||||
return series.GetEpisodes(user, this, includeMissingEpisodes, includeVirtualUnairedEpisodes);
|
||||
}
|
||||
|
||||
var episodes = GetRecursiveChildren(user)
|
||||
.OfType<Episode>();
|
||||
|
||||
if (series != null && series.ContainsEpisodesWithoutSeasonFolders)
|
||||
{
|
||||
var seasonNumber = IndexNumber;
|
||||
var list = episodes.ToList();
|
||||
|
||||
if (seasonNumber.HasValue)
|
||||
{
|
||||
list.AddRange(series.GetRecursiveChildren(user).OfType<Episode>()
|
||||
.Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value));
|
||||
}
|
||||
else
|
||||
{
|
||||
list.AddRange(series.GetRecursiveChildren(user).OfType<Episode>()
|
||||
.Where(i => !i.ParentIndexNumber.HasValue));
|
||||
}
|
||||
|
||||
episodes = list.DistinctBy(i => i.Id);
|
||||
}
|
||||
|
||||
if (!includeMissingEpisodes)
|
||||
{
|
||||
episodes = episodes.Where(i => !i.IsMissingEpisode);
|
||||
}
|
||||
if (!includeVirtualUnairedEpisodes)
|
||||
{
|
||||
episodes = episodes.Where(i => !i.IsVirtualUnaired);
|
||||
}
|
||||
|
||||
return LibraryManager
|
||||
.Sort(episodes, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending)
|
||||
.Cast<Episode>();
|
||||
public IEnumerable<Episode> GetEpisodes(Series series, User user, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes, IEnumerable<Episode> allSeriesEpisodes)
|
||||
{
|
||||
return series.GetEpisodes(user, this, includeMissingEpisodes, includeVirtualUnairedEpisodes, allSeriesEpisodes);
|
||||
}
|
||||
|
||||
public IEnumerable<Episode> GetEpisodes()
|
||||
|
|
|
@ -107,9 +107,11 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||
{
|
||||
get
|
||||
{
|
||||
if (EnablePooling())
|
||||
var userdatakeys = GetUserDataKeys();
|
||||
|
||||
if (userdatakeys.Count > 1)
|
||||
{
|
||||
return GetUserDataKeys().First();
|
||||
return userdatakeys[0];
|
||||
}
|
||||
return base.PresentationUniqueKey;
|
||||
}
|
||||
|
@ -183,24 +185,20 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||
|
||||
protected override Task<QueryResult<BaseItem>> GetItemsInternal(InternalItemsQuery query)
|
||||
{
|
||||
if (query.User == null)
|
||||
{
|
||||
return base.GetItemsInternal(query);
|
||||
}
|
||||
|
||||
var user = query.User;
|
||||
|
||||
Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager);
|
||||
|
||||
IEnumerable<BaseItem> items;
|
||||
|
||||
if (query.User == null)
|
||||
{
|
||||
items = query.Recursive
|
||||
? GetRecursiveChildren(filter)
|
||||
: Children.Where(filter);
|
||||
}
|
||||
else
|
||||
{
|
||||
items = query.Recursive
|
||||
? GetSeasons(user).Cast<BaseItem>().Concat(GetEpisodes(user)).Where(filter)
|
||||
: GetSeasons(user).Where(filter);
|
||||
}
|
||||
items = query.Recursive
|
||||
? GetSeasons(user).Cast<BaseItem>().Concat(GetEpisodes(user)).Where(filter)
|
||||
: GetSeasons(user).Where(filter);
|
||||
|
||||
var result = PostFilterAndSort(items, query);
|
||||
|
||||
|
@ -211,33 +209,13 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||
{
|
||||
IEnumerable<Season> seasons;
|
||||
|
||||
if (EnablePooling())
|
||||
seasons = LibraryManager.GetItemList(new InternalItemsQuery(user)
|
||||
{
|
||||
var seriesIds = LibraryManager.GetItemIds(new InternalItemsQuery(user)
|
||||
{
|
||||
PresentationUniqueKey = PresentationUniqueKey,
|
||||
IncludeItemTypes = new[] { typeof(Series).Name }
|
||||
});
|
||||
AncestorWithPresentationUniqueKey = PresentationUniqueKey,
|
||||
IncludeItemTypes = new[] { typeof(Season).Name },
|
||||
SortBy = new[] { ItemSortBy.SortName }
|
||||
|
||||
if (seriesIds.Count > 1)
|
||||
{
|
||||
seasons = LibraryManager.GetItemList(new InternalItemsQuery(user)
|
||||
{
|
||||
AncestorIds = seriesIds.Select(i => i.ToString("N")).ToArray(),
|
||||
IncludeItemTypes = new[] { typeof(Season).Name },
|
||||
SortBy = new[] { ItemSortBy.SortName }
|
||||
|
||||
}).Cast<Season>();
|
||||
}
|
||||
else
|
||||
{
|
||||
seasons = LibraryManager.Sort(base.GetChildren(user, true), user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).OfType<Season>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
seasons = LibraryManager.Sort(base.GetChildren(user, true), user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).OfType<Season>();
|
||||
}
|
||||
}).Cast<Season>();
|
||||
|
||||
if (!includeMissingSeasons)
|
||||
{
|
||||
|
@ -260,8 +238,17 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||
|
||||
public IEnumerable<Episode> GetEpisodes(User user, bool includeMissing, bool includeVirtualUnaired)
|
||||
{
|
||||
var allEpisodes = GetSeasons(user, true, true)
|
||||
.SelectMany(i => i.GetEpisodes(user, includeMissing, includeVirtualUnaired))
|
||||
var allItems = LibraryManager.GetItemList(new InternalItemsQuery(user)
|
||||
{
|
||||
AncestorWithPresentationUniqueKey = PresentationUniqueKey,
|
||||
IncludeItemTypes = new[] { typeof(Episode).Name, typeof(Season).Name },
|
||||
SortBy = new[] { ItemSortBy.SortName }
|
||||
}).ToList();
|
||||
|
||||
var allSeriesEpisodes = allItems.OfType<Episode>().ToList();
|
||||
|
||||
var allEpisodes = allItems.OfType<Season>()
|
||||
.SelectMany(i => i.GetEpisodes(this, user, includeMissing, includeVirtualUnaired, allSeriesEpisodes))
|
||||
.Reverse()
|
||||
.ToList();
|
||||
|
||||
|
@ -345,50 +332,32 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||
return GetEpisodes(user, season, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes);
|
||||
}
|
||||
|
||||
private bool EnablePooling()
|
||||
private IEnumerable<Episode> GetAllEpisodes(User user)
|
||||
{
|
||||
return false;
|
||||
return LibraryManager.GetItemList(new InternalItemsQuery(user)
|
||||
{
|
||||
AncestorWithPresentationUniqueKey = PresentationUniqueKey,
|
||||
IncludeItemTypes = new[] { typeof(Episode).Name },
|
||||
SortBy = new[] { ItemSortBy.SortName }
|
||||
|
||||
}).Cast<Episode>();
|
||||
}
|
||||
|
||||
public IEnumerable<Episode> GetEpisodes(User user, Season parentSeason, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes)
|
||||
{
|
||||
IEnumerable<Episode> episodes;
|
||||
IEnumerable<Episode> episodes = GetAllEpisodes(user);
|
||||
|
||||
if (EnablePooling())
|
||||
return GetEpisodes(user, parentSeason, includeMissingEpisodes, includeVirtualUnairedEpisodes, episodes);
|
||||
}
|
||||
|
||||
public IEnumerable<Episode> GetEpisodes(User user, Season parentSeason, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes, IEnumerable<Episode> allSeriesEpisodes)
|
||||
{
|
||||
if (allSeriesEpisodes == null)
|
||||
{
|
||||
var seriesIds = LibraryManager.GetItemIds(new InternalItemsQuery(user)
|
||||
{
|
||||
PresentationUniqueKey = PresentationUniqueKey,
|
||||
IncludeItemTypes = new[] { typeof(Series).Name }
|
||||
});
|
||||
|
||||
if (seriesIds.Count > 1)
|
||||
{
|
||||
episodes = LibraryManager.GetItemList(new InternalItemsQuery(user)
|
||||
{
|
||||
AncestorIds = seriesIds.Select(i => i.ToString("N")).ToArray(),
|
||||
IncludeItemTypes = new[] { typeof(Episode).Name },
|
||||
SortBy = new[] { ItemSortBy.SortName }
|
||||
|
||||
}).Cast<Episode>();
|
||||
}
|
||||
else
|
||||
{
|
||||
episodes = GetRecursiveChildren(user, new InternalItemsQuery(user)
|
||||
{
|
||||
IncludeItemTypes = new[] { typeof(Episode).Name }
|
||||
}).Cast<Episode>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
episodes = GetRecursiveChildren(user, new InternalItemsQuery(user)
|
||||
{
|
||||
IncludeItemTypes = new[] { typeof(Episode).Name }
|
||||
}).Cast<Episode>();
|
||||
return GetEpisodes(user, parentSeason, includeMissingEpisodes, includeVirtualUnairedEpisodes);
|
||||
}
|
||||
|
||||
episodes = FilterEpisodesBySeason(episodes, parentSeason, DisplaySpecialsWithSeasons);
|
||||
var episodes = FilterEpisodesBySeason(allSeriesEpisodes, parentSeason, DisplaySpecialsWithSeasons);
|
||||
|
||||
if (!includeMissingEpisodes)
|
||||
{
|
||||
|
|
|
@ -1,24 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface IHasTags
|
||||
/// </summary>
|
||||
public interface IHasTags
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the tags.
|
||||
/// </summary>
|
||||
/// <value>The tags.</value>
|
||||
List<string> Tags { get; set; }
|
||||
}
|
||||
|
||||
public static class TagExtensions
|
||||
{
|
||||
public static void AddTag(this IHasTags item, string name)
|
||||
public static void AddTag(this BaseItem item, string name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
|
@ -58,7 +58,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
parent = LibraryManager.GetItemById(ParentId) as Folder ?? parent;
|
||||
}
|
||||
|
||||
return new UserViewBuilder(UserViewManager, LiveTvManager, ChannelManager, LibraryManager, Logger, UserDataManager, TVSeriesManager, CollectionManager, PlaylistManager)
|
||||
return new UserViewBuilder(UserViewManager, LiveTvManager, ChannelManager, LibraryManager, Logger, UserDataManager, TVSeriesManager, ConfigurationManager, PlaylistManager)
|
||||
.GetUserItems(parent, this, ViewType, query);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
|
@ -30,10 +32,10 @@ namespace MediaBrowser.Controller.Entities
|
|||
private readonly ILogger _logger;
|
||||
private readonly IUserDataManager _userDataManager;
|
||||
private readonly ITVSeriesManager _tvSeriesManager;
|
||||
private readonly ICollectionManager _collectionManager;
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly IPlaylistManager _playlistManager;
|
||||
|
||||
public UserViewBuilder(IUserViewManager userViewManager, ILiveTvManager liveTvManager, IChannelManager channelManager, ILibraryManager libraryManager, ILogger logger, IUserDataManager userDataManager, ITVSeriesManager tvSeriesManager, ICollectionManager collectionManager, IPlaylistManager playlistManager)
|
||||
public UserViewBuilder(IUserViewManager userViewManager, ILiveTvManager liveTvManager, IChannelManager channelManager, ILibraryManager libraryManager, ILogger logger, IUserDataManager userDataManager, ITVSeriesManager tvSeriesManager, IServerConfigurationManager config, IPlaylistManager playlistManager)
|
||||
{
|
||||
_userViewManager = userViewManager;
|
||||
_liveTvManager = liveTvManager;
|
||||
|
@ -42,7 +44,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
_logger = logger;
|
||||
_userDataManager = userDataManager;
|
||||
_tvSeriesManager = tvSeriesManager;
|
||||
_collectionManager = collectionManager;
|
||||
_config = config;
|
||||
_playlistManager = playlistManager;
|
||||
}
|
||||
|
||||
|
@ -159,7 +161,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
return await GetTvGenres(queryParent, user, query).ConfigureAwait(false);
|
||||
|
||||
case SpecialFolder.TvGenre:
|
||||
return await GetTvGenreItems(queryParent, displayParent, user, query).ConfigureAwait(false);
|
||||
return GetTvGenreItems(queryParent, displayParent, user, query);
|
||||
|
||||
case SpecialFolder.TvResume:
|
||||
return GetTvResume(queryParent, user, query);
|
||||
|
@ -740,7 +742,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
return GetResult(genres, parent, query);
|
||||
}
|
||||
|
||||
private async Task<QueryResult<BaseItem>> GetTvGenreItems(Folder queryParent, Folder displayParent, User user, InternalItemsQuery query)
|
||||
private QueryResult<BaseItem> GetTvGenreItems(Folder queryParent, Folder displayParent, User user, InternalItemsQuery query)
|
||||
{
|
||||
query.Recursive = true;
|
||||
query.ParentId = queryParent.Id;
|
||||
|
@ -769,7 +771,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
{
|
||||
items = items.Where(i => Filter(i, query.User, query, _userDataManager, _libraryManager));
|
||||
|
||||
return PostFilterAndSort(items, queryParent, null, query, _libraryManager);
|
||||
return PostFilterAndSort(items, queryParent, null, query, _libraryManager, _config);
|
||||
}
|
||||
|
||||
public static bool FilterItem(BaseItem item, InternalItemsQuery query)
|
||||
|
@ -782,14 +784,15 @@ namespace MediaBrowser.Controller.Entities
|
|||
int? totalRecordLimit,
|
||||
InternalItemsQuery query)
|
||||
{
|
||||
return PostFilterAndSort(items, queryParent, totalRecordLimit, query, _libraryManager);
|
||||
return PostFilterAndSort(items, queryParent, totalRecordLimit, query, _libraryManager, _config);
|
||||
}
|
||||
|
||||
public static QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items,
|
||||
BaseItem queryParent,
|
||||
int? totalRecordLimit,
|
||||
InternalItemsQuery query,
|
||||
ILibraryManager libraryManager)
|
||||
ILibraryManager libraryManager,
|
||||
IServerConfigurationManager configurationManager)
|
||||
{
|
||||
var user = query.User;
|
||||
|
||||
|
@ -798,7 +801,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
query.IsVirtualUnaired,
|
||||
query.IsUnaired);
|
||||
|
||||
items = CollapseBoxSetItemsIfNeeded(items, query, queryParent, user);
|
||||
items = CollapseBoxSetItemsIfNeeded(items, query, queryParent, user, configurationManager);
|
||||
|
||||
// This must be the last filter
|
||||
if (!string.IsNullOrEmpty(query.AdjacentTo))
|
||||
|
@ -812,14 +815,15 @@ namespace MediaBrowser.Controller.Entities
|
|||
public static IEnumerable<BaseItem> CollapseBoxSetItemsIfNeeded(IEnumerable<BaseItem> items,
|
||||
InternalItemsQuery query,
|
||||
BaseItem queryParent,
|
||||
User user)
|
||||
User user,
|
||||
IServerConfigurationManager configurationManager)
|
||||
{
|
||||
if (items == null)
|
||||
{
|
||||
throw new ArgumentNullException("items");
|
||||
}
|
||||
|
||||
if (CollapseBoxSetItems(query, queryParent, user))
|
||||
if (CollapseBoxSetItems(query, queryParent, user, configurationManager))
|
||||
{
|
||||
items = BaseItem.CollectionManager.CollapseItemsWithinBoxSets(items, user);
|
||||
}
|
||||
|
@ -852,7 +856,8 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
public static bool CollapseBoxSetItems(InternalItemsQuery query,
|
||||
BaseItem queryParent,
|
||||
User user)
|
||||
User user,
|
||||
IServerConfigurationManager configurationManager)
|
||||
{
|
||||
// Could end up stuck in a loop like this
|
||||
if (queryParent is BoxSet)
|
||||
|
@ -864,7 +869,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
if (!param.HasValue)
|
||||
{
|
||||
if (user != null && !user.Configuration.GroupMoviesIntoBoxSets)
|
||||
if (user != null && !configurationManager.Configuration.EnableGroupingIntoCollections)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -1646,12 +1651,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
var tags = query.Tags;
|
||||
if (tags.Length > 0)
|
||||
{
|
||||
var hasTags = item as IHasTags;
|
||||
if (hasTags == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!tags.Any(v => hasTags.Tags.Contains(v, StringComparer.OrdinalIgnoreCase)))
|
||||
if (!tags.Any(v => item.Tags.Contains(v, StringComparer.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// </summary>
|
||||
public class Video : BaseItem,
|
||||
IHasAspectRatio,
|
||||
IHasTags,
|
||||
ISupportsPlaceHolders,
|
||||
IHasMediaSources,
|
||||
IHasShortOverview,
|
||||
|
|
|
@ -154,7 +154,6 @@
|
|||
<Compile Include="Entities\IHasSpecialFeatures.cs" />
|
||||
<Compile Include="Entities\IHasStartDate.cs" />
|
||||
<Compile Include="Entities\IHasTaglines.cs" />
|
||||
<Compile Include="Entities\IHasTags.cs" />
|
||||
<Compile Include="Entities\IHasThemeMedia.cs" />
|
||||
<Compile Include="Entities\IHasTrailers.cs" />
|
||||
<Compile Include="Entities\IHasUserData.cs" />
|
||||
|
@ -177,6 +176,7 @@
|
|||
<Compile Include="Entities\PhotoAlbum.cs" />
|
||||
<Compile Include="Entities\Share.cs" />
|
||||
<Compile Include="Entities\SourceType.cs" />
|
||||
<Compile Include="Entities\TagExtensions.cs" />
|
||||
<Compile Include="Entities\UserView.cs" />
|
||||
<Compile Include="Entities\UserViewBuilder.cs" />
|
||||
<Compile Include="FileOrganization\IFileOrganizationService.cs" />
|
||||
|
|
|
@ -29,6 +29,8 @@ namespace MediaBrowser.Controller.Persistence
|
|||
/// <returns>Task{UserItemData}.</returns>
|
||||
UserItemData GetUserData(Guid userId, string key);
|
||||
|
||||
UserItemData GetUserData(Guid userId, List<string> keys);
|
||||
|
||||
/// <summary>
|
||||
/// Return all user data associated with the given user
|
||||
/// </summary>
|
||||
|
|
|
@ -803,11 +803,7 @@ namespace MediaBrowser.Controller.Providers
|
|||
{
|
||||
using (var subtree = reader.ReadSubtree())
|
||||
{
|
||||
var hasTags = item as IHasTags;
|
||||
if (hasTags != null)
|
||||
{
|
||||
FetchFromTagsNode(subtree, hasTags);
|
||||
}
|
||||
FetchFromTagsNode(subtree, item);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -1066,7 +1062,7 @@ namespace MediaBrowser.Controller.Providers
|
|||
}
|
||||
}
|
||||
|
||||
private void FetchFromTagsNode(XmlReader reader, IHasTags item)
|
||||
private void FetchFromTagsNode(XmlReader reader, BaseItem item)
|
||||
{
|
||||
reader.MoveToContent();
|
||||
|
||||
|
|
|
@ -593,20 +593,16 @@ namespace MediaBrowser.LocalMetadata.Savers
|
|||
builder.Append("</Studios>");
|
||||
}
|
||||
|
||||
var hasTags = item as IHasTags;
|
||||
if (hasTags != null)
|
||||
if (item.Tags.Count > 0)
|
||||
{
|
||||
if (hasTags.Tags.Count > 0)
|
||||
builder.Append("<Tags>");
|
||||
|
||||
foreach (var tag in item.Tags)
|
||||
{
|
||||
builder.Append("<Tags>");
|
||||
|
||||
foreach (var tag in hasTags.Tags)
|
||||
{
|
||||
builder.Append("<Tag>" + SecurityElement.Escape(tag) + "</Tag>");
|
||||
}
|
||||
|
||||
builder.Append("</Tags>");
|
||||
builder.Append("<Tag>" + SecurityElement.Escape(tag) + "</Tag>");
|
||||
}
|
||||
|
||||
builder.Append("</Tags>");
|
||||
}
|
||||
|
||||
if (item.Keywords.Count > 0)
|
||||
|
|
|
@ -198,6 +198,8 @@ namespace MediaBrowser.Model.Configuration
|
|||
public bool EnableAnonymousUsageReporting { get; set; }
|
||||
public bool EnableStandaloneMusicKeys { get; set; }
|
||||
public bool EnableLocalizedGuids { get; set; }
|
||||
public bool EnableFolderView { get; set; }
|
||||
public bool EnableGroupingIntoCollections { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
|
||||
|
|
|
@ -48,7 +48,8 @@ namespace MediaBrowser.Model.Configuration
|
|||
public bool RememberAudioSelections { get; set; }
|
||||
public bool RememberSubtitleSelections { get; set; }
|
||||
public bool EnableNextEpisodeAutoPlay { get; set; }
|
||||
|
||||
public bool DisplayFoldersView { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UserConfiguration" /> class.
|
||||
/// </summary>
|
||||
|
|
|
@ -9,50 +9,50 @@ namespace MediaBrowser.Model.Entities
|
|||
/// <summary>
|
||||
/// The primary
|
||||
/// </summary>
|
||||
Primary,
|
||||
Primary = 0,
|
||||
/// <summary>
|
||||
/// The art
|
||||
/// </summary>
|
||||
Art,
|
||||
Art = 1,
|
||||
/// <summary>
|
||||
/// The backdrop
|
||||
/// </summary>
|
||||
Backdrop,
|
||||
Backdrop = 2,
|
||||
/// <summary>
|
||||
/// The banner
|
||||
/// </summary>
|
||||
Banner,
|
||||
Banner = 3,
|
||||
/// <summary>
|
||||
/// The logo
|
||||
/// </summary>
|
||||
Logo,
|
||||
Logo = 4,
|
||||
/// <summary>
|
||||
/// The thumb
|
||||
/// </summary>
|
||||
Thumb,
|
||||
Thumb = 5,
|
||||
/// <summary>
|
||||
/// The disc
|
||||
/// </summary>
|
||||
Disc,
|
||||
Disc = 6,
|
||||
/// <summary>
|
||||
/// The box
|
||||
/// </summary>
|
||||
Box,
|
||||
Box = 7,
|
||||
/// <summary>
|
||||
/// The screenshot
|
||||
/// </summary>
|
||||
Screenshot,
|
||||
Screenshot = 8,
|
||||
/// <summary>
|
||||
/// The menu
|
||||
/// </summary>
|
||||
Menu,
|
||||
Menu = 9,
|
||||
/// <summary>
|
||||
/// The chapter image
|
||||
/// </summary>
|
||||
Chapter,
|
||||
Chapter = 10,
|
||||
/// <summary>
|
||||
/// The box rear
|
||||
/// </summary>
|
||||
BoxRear
|
||||
BoxRear = 11
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,9 +73,15 @@ namespace MediaBrowser.Model.LiveTv
|
|||
|
||||
public string[] EnabledTuners { get; set; }
|
||||
public bool EnableAllTuners { get; set; }
|
||||
public string[] NewsGenres { get; set; }
|
||||
public string[] SportsGenres { get; set; }
|
||||
public string[] KidsGenres { get; set; }
|
||||
|
||||
public ListingsProviderInfo()
|
||||
{
|
||||
NewsGenres = new string[] { "news" };
|
||||
SportsGenres = new string[] { "sports", "basketball", "baseball", "football" };
|
||||
KidsGenres = new string[] { "kids", "family", "children" };
|
||||
EnabledTuners = new string[] { };
|
||||
EnableAllTuners = true;
|
||||
}
|
||||
|
|
|
@ -151,15 +151,9 @@ namespace MediaBrowser.Providers.Manager
|
|||
|
||||
if (!lockedFields.Contains(MetadataFields.Tags))
|
||||
{
|
||||
var sourceHasTags = source as IHasTags;
|
||||
var targetHasTags = target as IHasTags;
|
||||
|
||||
if (sourceHasTags != null && targetHasTags != null)
|
||||
if (replaceData || target.Tags.Count == 0)
|
||||
{
|
||||
if (replaceData || targetHasTags.Tags.Count == 0)
|
||||
{
|
||||
targetHasTags.Tags = sourceHasTags.Tags;
|
||||
}
|
||||
target.Tags = source.Tags;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace MediaBrowser.Providers.Music
|
|||
private readonly IApplicationHost _appHost;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public static string MusicBrainzBaseUrl = "http://musicbrainz.fercasas.com:5000";
|
||||
public static string MusicBrainzBaseUrl = "https://www.musicbrainz.org";
|
||||
|
||||
public MusicBrainzAlbumProvider(IHttpClient httpClient, IApplicationHost appHost, ILogger logger)
|
||||
{
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using MediaBrowser.Common.Net;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
|
@ -6,7 +8,10 @@ using MediaBrowser.Controller.LiveTv;
|
|||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
@ -15,10 +20,16 @@ namespace MediaBrowser.Providers.Omdb
|
|||
public class OmdbImageProvider : IRemoteImageProvider, IHasOrder
|
||||
{
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly IJsonSerializer _jsonSerializer;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly IServerConfigurationManager _configurationManager;
|
||||
|
||||
public OmdbImageProvider(IHttpClient httpClient)
|
||||
public OmdbImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
|
||||
{
|
||||
_jsonSerializer = jsonSerializer;
|
||||
_httpClient = httpClient;
|
||||
_fileSystem = fileSystem;
|
||||
_configurationManager = configurationManager;
|
||||
}
|
||||
|
||||
public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
|
||||
|
@ -29,22 +40,29 @@ namespace MediaBrowser.Providers.Omdb
|
|||
};
|
||||
}
|
||||
|
||||
public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
|
||||
public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
|
||||
{
|
||||
var imdbId = item.GetProviderId(MetadataProviders.Imdb);
|
||||
|
||||
var list = new List<RemoteImageInfo>();
|
||||
|
||||
var provider = new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(imdbId))
|
||||
{
|
||||
list.Add(new RemoteImageInfo
|
||||
OmdbProvider.RootObject rootObject = await provider.GetRootObject(imdbId, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (!string.IsNullOrEmpty(rootObject.Poster))
|
||||
{
|
||||
ProviderName = Name,
|
||||
Url = string.Format("https://img.omdbapi.com/?i={0}&apikey=82e83907", imdbId)
|
||||
});
|
||||
list.Add(new RemoteImageInfo
|
||||
{
|
||||
ProviderName = Name,
|
||||
Url = string.Format("https://img.omdbapi.com/?i={0}&apikey=82e83907", imdbId)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return Task.FromResult<IEnumerable<RemoteImageInfo>>(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
|
||||
|
@ -65,18 +83,6 @@ namespace MediaBrowser.Providers.Omdb
|
|||
|
||||
public bool Supports(IHasImages item)
|
||||
{
|
||||
// We'll hammer Omdb if we enable this
|
||||
if (item is Person)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save the http requests since we know it's not currently supported
|
||||
if (item is Season || item is Episode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Supports images for tv movies
|
||||
var tvProgram = item as LiveTvProgram;
|
||||
if (tvProgram != null && tvProgram.IsMovie)
|
||||
|
@ -84,7 +90,7 @@ namespace MediaBrowser.Providers.Omdb
|
|||
return true;
|
||||
}
|
||||
|
||||
return item is Movie || item is Trailer;
|
||||
return item is Movie || item is Trailer || item is Episode;
|
||||
}
|
||||
|
||||
public int Order
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using MediaBrowser.Common.Net;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
|
@ -26,13 +28,17 @@ namespace MediaBrowser.Providers.Omdb
|
|||
private readonly IHttpClient _httpClient;
|
||||
private readonly ILogger _logger;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly IServerConfigurationManager _configurationManager;
|
||||
|
||||
public OmdbItemProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager)
|
||||
public OmdbItemProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
|
||||
{
|
||||
_jsonSerializer = jsonSerializer;
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
_libraryManager = libraryManager;
|
||||
_fileSystem = fileSystem;
|
||||
_configurationManager = configurationManager;
|
||||
}
|
||||
|
||||
public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken)
|
||||
|
@ -220,7 +226,7 @@ namespace MediaBrowser.Providers.Omdb
|
|||
result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
|
||||
result.HasMetadata = true;
|
||||
|
||||
await new OmdbProvider(_jsonSerializer, _httpClient).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
|
||||
await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -259,7 +265,7 @@ namespace MediaBrowser.Providers.Omdb
|
|||
result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
|
||||
result.HasMetadata = true;
|
||||
|
||||
await new OmdbProvider(_jsonSerializer, _httpClient).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
|
||||
await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
using MediaBrowser.Common.Net;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
@ -15,17 +20,17 @@ namespace MediaBrowser.Providers.Omdb
|
|||
{
|
||||
internal static readonly SemaphoreSlim ResourcePool = new SemaphoreSlim(1, 1);
|
||||
private readonly IJsonSerializer _jsonSerializer;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly IServerConfigurationManager _configurationManager;
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||
|
||||
public static OmdbProvider Current;
|
||||
|
||||
public OmdbProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient)
|
||||
public OmdbProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
|
||||
{
|
||||
_jsonSerializer = jsonSerializer;
|
||||
_httpClient = httpClient;
|
||||
|
||||
Current = this;
|
||||
_fileSystem = fileSystem;
|
||||
_configurationManager = configurationManager;
|
||||
}
|
||||
|
||||
public async Task Fetch(BaseItem item, string imdbId, string language, string country, CancellationToken cancellationToken)
|
||||
|
@ -35,19 +40,7 @@ namespace MediaBrowser.Providers.Omdb
|
|||
throw new ArgumentNullException("imdbId");
|
||||
}
|
||||
|
||||
var imdbParam = imdbId.StartsWith("tt", StringComparison.OrdinalIgnoreCase) ? imdbId : "tt" + imdbId;
|
||||
|
||||
var url = string.Format("https://www.omdbapi.com/?i={0}&tomatoes=true", imdbParam);
|
||||
|
||||
using (var stream = await _httpClient.Get(new HttpRequestOptions
|
||||
{
|
||||
Url = url,
|
||||
ResourcePool = ResourcePool,
|
||||
CancellationToken = cancellationToken
|
||||
|
||||
}).ConfigureAwait(false))
|
||||
{
|
||||
var result = _jsonSerializer.DeserializeFromStream<RootObject>(stream);
|
||||
var result = await GetRootObject(imdbId, cancellationToken);
|
||||
|
||||
// Only take the name and rating if the user's language is set to english, since Omdb has no localization
|
||||
if (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase))
|
||||
|
@ -84,7 +77,6 @@ namespace MediaBrowser.Providers.Omdb
|
|||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(result.tomatoConsensus)
|
||||
&& !string.Equals(result.tomatoConsensus, "n/a", StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(result.tomatoConsensus, "No consensus yet.", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
hasCriticRating.CriticRatingSummary = WebUtility.HtmlDecode(result.tomatoConsensus);
|
||||
|
@ -109,20 +101,90 @@ namespace MediaBrowser.Providers.Omdb
|
|||
item.CommunityRating = imdbRating;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(result.Website)
|
||||
&& !string.Equals(result.Website, "n/a", StringComparison.OrdinalIgnoreCase))
|
||||
if (!string.IsNullOrEmpty(result.Website))
|
||||
{
|
||||
item.HomePageUrl = result.Website;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(result.imdbID)
|
||||
&& !string.Equals(result.imdbID, "n/a", StringComparison.OrdinalIgnoreCase))
|
||||
if (!string.IsNullOrWhiteSpace(result.imdbID))
|
||||
{
|
||||
item.SetProviderId(MetadataProviders.Imdb, result.imdbID);
|
||||
}
|
||||
|
||||
ParseAdditionalMetadata(item, result);
|
||||
}
|
||||
|
||||
internal async Task<RootObject> GetRootObject(string imdbId, CancellationToken cancellationToken)
|
||||
{
|
||||
var path = await EnsureItemInfo(imdbId, cancellationToken);
|
||||
|
||||
string resultString;
|
||||
|
||||
using (Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 131072))
|
||||
{
|
||||
using (var reader = new StreamReader(stream, new UTF8Encoding(false)))
|
||||
{
|
||||
resultString = reader.ReadToEnd();
|
||||
resultString = resultString.Replace("\"N/A\"", "\"\"");
|
||||
}
|
||||
}
|
||||
|
||||
var result = _jsonSerializer.DeserializeFromString<RootObject>(resultString);
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<string> EnsureItemInfo(string imdbId, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(imdbId))
|
||||
{
|
||||
throw new ArgumentNullException("imdbId");
|
||||
}
|
||||
|
||||
var imdbParam = imdbId.StartsWith("tt", StringComparison.OrdinalIgnoreCase) ? imdbId : "tt" + imdbId;
|
||||
|
||||
var path = GetDataFilePath(imdbParam);
|
||||
|
||||
var fileInfo = _fileSystem.GetFileSystemInfo(path);
|
||||
|
||||
if (fileInfo.Exists)
|
||||
{
|
||||
// If it's recent or automatic updates are enabled, don't re-download
|
||||
if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 3)
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
var url = string.Format("https://www.omdbapi.com/?i={0}&tomatoes=true", imdbParam);
|
||||
|
||||
using (var stream = await _httpClient.Get(new HttpRequestOptions
|
||||
{
|
||||
Url = url,
|
||||
ResourcePool = ResourcePool,
|
||||
CancellationToken = cancellationToken
|
||||
|
||||
}).ConfigureAwait(false))
|
||||
{
|
||||
var rootObject = _jsonSerializer.DeserializeFromStream<RootObject>(stream);
|
||||
_fileSystem.CreateDirectory(Path.GetDirectoryName(path));
|
||||
_jsonSerializer.SerializeToFile(rootObject, path);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
internal string GetDataFilePath(string imdbId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(imdbId))
|
||||
{
|
||||
throw new ArgumentNullException("imdbId");
|
||||
}
|
||||
|
||||
var dataPath = Path.Combine(_configurationManager.ApplicationPaths.CachePath, "omdb");
|
||||
|
||||
var filename = string.Format("{0}.json", imdbId);
|
||||
|
||||
return Path.Combine(dataPath, filename);
|
||||
}
|
||||
|
||||
private void ParseAdditionalMetadata(BaseItem item, RootObject result)
|
||||
|
@ -130,8 +192,7 @@ namespace MediaBrowser.Providers.Omdb
|
|||
// Grab series genres because imdb data is better than tvdb. Leave movies alone
|
||||
// But only do it if english is the preferred language because this data will not be localized
|
||||
if (ShouldFetchGenres(item) &&
|
||||
!string.IsNullOrWhiteSpace(result.Genre) &&
|
||||
!string.Equals(result.Genre, "n/a", StringComparison.OrdinalIgnoreCase))
|
||||
!string.IsNullOrWhiteSpace(result.Genre))
|
||||
{
|
||||
item.Genres.Clear();
|
||||
|
||||
|
@ -156,8 +217,7 @@ namespace MediaBrowser.Providers.Omdb
|
|||
}
|
||||
|
||||
var hasAwards = item as IHasAwards;
|
||||
if (hasAwards != null && !string.IsNullOrEmpty(result.Awards) &&
|
||||
!string.Equals(result.Awards, "n/a", StringComparison.OrdinalIgnoreCase))
|
||||
if (hasAwards != null && !string.IsNullOrEmpty(result.Awards))
|
||||
{
|
||||
hasAwards.AwardSummary = WebUtility.HtmlDecode(result.Awards);
|
||||
}
|
||||
|
@ -178,7 +238,7 @@ namespace MediaBrowser.Providers.Omdb
|
|||
return string.Equals(lang, "en", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private class RootObject
|
||||
internal class RootObject
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string Year { get; set; }
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using MediaBrowser.Common.Net;
|
||||
using CommonIO;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
|
@ -21,12 +23,16 @@ namespace MediaBrowser.Providers.TV
|
|||
private readonly IJsonSerializer _jsonSerializer;
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly OmdbItemProvider _itemProvider;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly IServerConfigurationManager _configurationManager;
|
||||
|
||||
public OmdbEpisodeProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager)
|
||||
public OmdbEpisodeProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
|
||||
{
|
||||
_jsonSerializer = jsonSerializer;
|
||||
_httpClient = httpClient;
|
||||
_itemProvider = new OmdbItemProvider(jsonSerializer, httpClient, logger, libraryManager);
|
||||
_fileSystem = fileSystem;
|
||||
_configurationManager = configurationManager;
|
||||
_itemProvider = new OmdbItemProvider(jsonSerializer, httpClient, logger, libraryManager, fileSystem, configurationManager);
|
||||
}
|
||||
|
||||
public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken)
|
||||
|
@ -58,7 +64,7 @@ namespace MediaBrowser.Providers.TV
|
|||
result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
|
||||
result.HasMetadata = true;
|
||||
|
||||
await new OmdbProvider(_jsonSerializer, _httpClient).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
|
||||
await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager).Fetch(result.Item, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -114,6 +114,22 @@ namespace MediaBrowser.Providers.TV
|
|||
item.CommunityRating = (float)response.vote_average;
|
||||
item.VoteCount = response.vote_count;
|
||||
|
||||
if (response.videos != null && response.videos.results != null)
|
||||
{
|
||||
foreach (var video in response.videos.results)
|
||||
{
|
||||
if (video.type.Equals("trailer", System.StringComparison.OrdinalIgnoreCase)
|
||||
|| video.type.Equals("clip", System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (video.site.Equals("youtube", System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var videoUrl = string.Format("http://www.youtube.com/watch?v={0}", video.key);
|
||||
item.AddTrailerUrl(videoUrl, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.ResetPeople();
|
||||
|
||||
var credits = response.credits;
|
||||
|
|
|
@ -210,7 +210,19 @@ namespace MediaBrowser.Providers.TV
|
|||
|
||||
public class Videos
|
||||
{
|
||||
public List<object> results { get; set; }
|
||||
public List<Video> results { get; set; }
|
||||
}
|
||||
|
||||
public class Video
|
||||
{
|
||||
public string id { get; set; }
|
||||
public string iso_639_1 { get; set; }
|
||||
public string iso_3166_1 { get; set; }
|
||||
public string key { get; set; }
|
||||
public string name { get; set; }
|
||||
public string site { get; set; }
|
||||
public string size { get; set; }
|
||||
public string type { get; set; }
|
||||
}
|
||||
|
||||
public class RootObject
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace MediaBrowser.Providers.TV
|
|||
|
||||
result.HasMetadata = true;
|
||||
result.Item = new Season();
|
||||
result.Item.Name = info.Name;
|
||||
result.Item.Name = seasonInfo.name;
|
||||
result.Item.IndexNumber = seasonNumber;
|
||||
|
||||
result.Item.Overview = seasonInfo.overview;
|
||||
|
|
|
@ -168,7 +168,7 @@ namespace MediaBrowser.Providers.TV
|
|||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
result.Item = await FetchMovieData(tmdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
|
||||
result.Item = await FetchSeriesData(tmdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
result.HasMetadata = result.Item != null;
|
||||
}
|
||||
|
@ -176,7 +176,7 @@ namespace MediaBrowser.Providers.TV
|
|||
return result;
|
||||
}
|
||||
|
||||
private async Task<Series> FetchMovieData(string tmdbId, string language, string preferredCountryCode, CancellationToken cancellationToken)
|
||||
private async Task<Series> FetchSeriesData(string tmdbId, string language, string preferredCountryCode, CancellationToken cancellationToken)
|
||||
{
|
||||
string dataFilePath = null;
|
||||
RootObject seriesInfo = null;
|
||||
|
@ -285,6 +285,21 @@ namespace MediaBrowser.Providers.TV
|
|||
series.OfficialRating = minimumRelease.rating;
|
||||
}
|
||||
|
||||
if (seriesInfo.videos != null && seriesInfo.videos.results != null)
|
||||
{
|
||||
foreach (var video in seriesInfo.videos.results)
|
||||
{
|
||||
if (video.type.Equals("trailer", System.StringComparison.OrdinalIgnoreCase)
|
||||
|| video.type.Equals("clip", System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (video.site.Equals("youtube", System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var videoUrl = string.Format("http://www.youtube.com/watch?v={0}", video.key);
|
||||
series.AddTrailerUrl(videoUrl, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static string GetSeriesDataPath(IApplicationPaths appPaths, string tmdbId)
|
||||
|
@ -555,7 +570,19 @@ namespace MediaBrowser.Providers.TV
|
|||
|
||||
public class Videos
|
||||
{
|
||||
public List<object> results { get; set; }
|
||||
public List<Video> results { get; set; }
|
||||
}
|
||||
|
||||
public class Video
|
||||
{
|
||||
public string id { get; set; }
|
||||
public string iso_639_1 { get; set; }
|
||||
public string iso_3166_1 { get; set; }
|
||||
public string key { get; set; }
|
||||
public string name { get; set; }
|
||||
public string site { get; set; }
|
||||
public string size { get; set; }
|
||||
public string type { get; set; }
|
||||
}
|
||||
|
||||
public class ContentRating
|
||||
|
|
|
@ -88,7 +88,7 @@ namespace MediaBrowser.Providers.TV
|
|||
|
||||
public string UrlFormatString
|
||||
{
|
||||
get { return null; }
|
||||
get { return "https://thetvdb.com/index.php?tab=episode&id={0}"; }
|
||||
}
|
||||
|
||||
public bool Supports(IHasProviderIds item)
|
||||
|
|
|
@ -60,7 +60,6 @@ namespace MediaBrowser.Server.Implementations.Connect
|
|||
{
|
||||
return new ConnectUserPreferences
|
||||
{
|
||||
GroupMoviesIntoBoxSets = config.GroupMoviesIntoBoxSets,
|
||||
PlayDefaultAudioTrack = config.PlayDefaultAudioTrack,
|
||||
SubtitleMode = config.SubtitleMode,
|
||||
PreferredAudioLanguages = string.IsNullOrWhiteSpace(config.AudioLanguagePreference) ? new string[] { } : new[] { config.AudioLanguagePreference },
|
||||
|
|
|
@ -969,16 +969,7 @@ namespace MediaBrowser.Server.Implementations.Dto
|
|||
|
||||
if (fields.Contains(ItemFields.Tags))
|
||||
{
|
||||
var hasTags = item as IHasTags;
|
||||
if (hasTags != null)
|
||||
{
|
||||
dto.Tags = hasTags.Tags;
|
||||
}
|
||||
|
||||
if (dto.Tags == null)
|
||||
{
|
||||
dto.Tags = new List<string>();
|
||||
}
|
||||
dto.Tags = item.Tags;
|
||||
}
|
||||
|
||||
if (fields.Contains(ItemFields.Keywords))
|
||||
|
|
|
@ -562,9 +562,10 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
|||
series = _libraryManager.GetItemList(new Controller.Entities.InternalItemsQuery
|
||||
{
|
||||
IncludeItemTypes = new[] { typeof(Series).Name },
|
||||
Recursive = true
|
||||
}).Cast<Series>()
|
||||
.FirstOrDefault(i => string.Equals(i.Name, info.ItemName, StringComparison.OrdinalIgnoreCase));
|
||||
Recursive = true,
|
||||
Name = info.ItemName
|
||||
|
||||
}).Cast<Series>().FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,8 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
{
|
||||
public event EventHandler<UserDataSaveEventArgs> UserDataSaved;
|
||||
|
||||
private readonly Dictionary<string, UserItemData> _userData = new Dictionary<string, UserItemData>(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly ConcurrentDictionary<string, UserItemData> _userData =
|
||||
new ConcurrentDictionary<string, UserItemData>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
private readonly ILogger _logger;
|
||||
private readonly IServerConfigurationManager _config;
|
||||
|
@ -64,13 +65,6 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
try
|
||||
{
|
||||
await Repository.SaveUserData(userId, key, userData, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var newValue = userData;
|
||||
|
||||
lock (_userData)
|
||||
{
|
||||
_userData[GetCacheKey(userId, key)] = newValue;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -80,6 +74,9 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
}
|
||||
}
|
||||
|
||||
var cacheKey = GetCacheKey(userId, item.Id);
|
||||
_userData.AddOrUpdate(cacheKey, userData, (k, v) => userData);
|
||||
|
||||
EventHelper.FireEventIfNotNull(UserDataSaved, this, new UserDataSaveEventArgs
|
||||
{
|
||||
Keys = keys,
|
||||
|
@ -122,7 +119,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -140,7 +137,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
return Repository.GetAllUserData(userId);
|
||||
}
|
||||
|
||||
public UserItemData GetUserData(Guid userId, List<string> keys)
|
||||
public UserItemData GetUserData(Guid userId, Guid itemId, List<string> keys)
|
||||
{
|
||||
if (userId == Guid.Empty)
|
||||
{
|
||||
|
@ -150,26 +147,23 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
{
|
||||
throw new ArgumentNullException("keys");
|
||||
}
|
||||
|
||||
lock (_userData)
|
||||
if (keys.Count == 0)
|
||||
{
|
||||
foreach (var key in keys)
|
||||
{
|
||||
var cacheKey = GetCacheKey(userId, key);
|
||||
UserItemData value;
|
||||
if (_userData.TryGetValue(cacheKey, out value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
throw new ArgumentException("UserData keys cannot be empty.");
|
||||
}
|
||||
|
||||
value = Repository.GetUserData(userId, key);
|
||||
var cacheKey = GetCacheKey(userId, itemId);
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
_userData[cacheKey] = value;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return _userData.GetOrAdd(cacheKey, k => GetUserDataInternal(userId, keys));
|
||||
}
|
||||
|
||||
private UserItemData GetUserDataInternal(Guid userId, List<string> keys)
|
||||
{
|
||||
var userData = Repository.GetUserData(userId, keys);
|
||||
|
||||
if (userData != null)
|
||||
{
|
||||
return userData;
|
||||
}
|
||||
|
||||
if (keys.Count > 0)
|
||||
|
@ -184,57 +178,13 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user data.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user id.</param>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <returns>Task{UserItemData}.</returns>
|
||||
public UserItemData GetUserData(Guid userId, string key)
|
||||
{
|
||||
if (userId == Guid.Empty)
|
||||
{
|
||||
throw new ArgumentNullException("userId");
|
||||
}
|
||||
if (string.IsNullOrEmpty(key))
|
||||
{
|
||||
throw new ArgumentNullException("key");
|
||||
}
|
||||
|
||||
lock (_userData)
|
||||
{
|
||||
var cacheKey = GetCacheKey(userId, key);
|
||||
UserItemData value;
|
||||
if (_userData.TryGetValue(cacheKey, out value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
value = Repository.GetUserData(userId, key);
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
value = new UserItemData
|
||||
{
|
||||
UserId = userId,
|
||||
Key = key
|
||||
};
|
||||
}
|
||||
|
||||
_userData[cacheKey] = value;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the internal key.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user id.</param>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
private string GetCacheKey(Guid userId, string key)
|
||||
private string GetCacheKey(Guid userId, Guid itemId)
|
||||
{
|
||||
return userId + key;
|
||||
return userId.ToString("N") + itemId.ToString("N");
|
||||
}
|
||||
|
||||
public UserItemData GetUserData(IHasUserData user, IHasUserData item)
|
||||
|
@ -249,7 +199,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
|
||||
public UserItemData GetUserData(Guid userId, IHasUserData item)
|
||||
{
|
||||
return GetUserData(userId, item.GetUserDataKeys());
|
||||
return GetUserData(userId, item.Id, item.GetUserDataKeys());
|
||||
}
|
||||
|
||||
public UserItemDataDto GetUserDataDto(IHasUserData item, User user)
|
||||
|
|
|
@ -105,6 +105,12 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
}
|
||||
}
|
||||
|
||||
if (_config.Configuration.EnableFolderView)
|
||||
{
|
||||
var name = _localizationManager.GetLocalizedString("ViewType" + CollectionType.Folders);
|
||||
list.Add(await _libraryManager.GetNamedView(name, CollectionType.Folders, string.Empty, cancellationToken).ConfigureAwait(false));
|
||||
}
|
||||
|
||||
if (query.IncludeExternalContent)
|
||||
{
|
||||
var channelResult = await _channelManager.GetChannelsInternal(new ChannelQuery
|
||||
|
|
|
@ -6,6 +6,5 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
|||
public interface IDbConnector
|
||||
{
|
||||
Task<IDbConnection> Connect(string dbPath);
|
||||
void BindSimilarityScoreFunction(IDbConnection connection);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ using System.Data.SQLite;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Logging;
|
||||
|
||||
namespace MediaBrowser.Server.Implementations.Persistence
|
||||
|
@ -46,13 +48,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
|||
return connection;
|
||||
}
|
||||
|
||||
public static void BindGetSimilarityScore(IDbConnection connection, ILogger logger)
|
||||
{
|
||||
var sqlConnection = (SQLiteConnection) connection;
|
||||
SimiliarToFunction.Logger = logger;
|
||||
sqlConnection.BindFunction(new SimiliarToFunction());
|
||||
}
|
||||
|
||||
public static void BindFunction(this SQLiteConnection connection, SQLiteFunction function)
|
||||
{
|
||||
var attributes = function.GetType().GetCustomAttributes(typeof(SQLiteFunctionAttribute), true).Cast<SQLiteFunctionAttribute>().ToArray();
|
||||
|
@ -63,113 +58,4 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
|||
connection.BindFunction(attributes[0], function);
|
||||
}
|
||||
}
|
||||
|
||||
[SQLiteFunction(Name = "GetSimilarityScore", Arguments = 12, FuncType = FunctionType.Scalar)]
|
||||
public class SimiliarToFunction : SQLiteFunction
|
||||
{
|
||||
internal static ILogger Logger;
|
||||
|
||||
public override object Invoke(object[] args)
|
||||
{
|
||||
var score = 0;
|
||||
|
||||
var inputOfficialRating = args[0] as string;
|
||||
var rowOfficialRating = args[1] as string;
|
||||
if (!string.IsNullOrWhiteSpace(inputOfficialRating) && string.Equals(inputOfficialRating, rowOfficialRating))
|
||||
{
|
||||
score += 10;
|
||||
}
|
||||
|
||||
long? inputYear = args[2] == null ? (long?)null : (long)args[2];
|
||||
long? rowYear = args[3] == null ? (long?)null : (long)args[3];
|
||||
|
||||
if (inputYear.HasValue && rowYear.HasValue)
|
||||
{
|
||||
var diff = Math.Abs(inputYear.Value - rowYear.Value);
|
||||
|
||||
// Add if they came out within the same decade
|
||||
if (diff < 10)
|
||||
{
|
||||
score += 2;
|
||||
}
|
||||
|
||||
// And more if within five years
|
||||
if (diff < 5)
|
||||
{
|
||||
score += 2;
|
||||
}
|
||||
}
|
||||
|
||||
// genres
|
||||
score += GetListScore(args, 4, 5);
|
||||
|
||||
// tags
|
||||
score += GetListScore(args, 6, 7);
|
||||
|
||||
// keywords
|
||||
score += GetListScore(args, 8, 9);
|
||||
|
||||
// studios
|
||||
score += GetListScore(args, 10, 11, 3);
|
||||
|
||||
|
||||
// TODO: People
|
||||
// var item2PeopleNames = allPeople.Where(i => i.ItemId == item2.Id)
|
||||
//.Select(i => i.Name)
|
||||
//.Where(i => !string.IsNullOrWhiteSpace(i))
|
||||
//.DistinctNames()
|
||||
//.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// points += item1People.Where(i => item2PeopleNames.ContainsKey(i.Name)).Sum(i =>
|
||||
// {
|
||||
// if (string.Equals(i.Type, PersonType.Director, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Director, StringComparison.OrdinalIgnoreCase))
|
||||
// {
|
||||
// return 5;
|
||||
// }
|
||||
// if (string.Equals(i.Type, PersonType.Actor, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Actor, StringComparison.OrdinalIgnoreCase))
|
||||
// {
|
||||
// return 3;
|
||||
// }
|
||||
// if (string.Equals(i.Type, PersonType.Composer, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Composer, StringComparison.OrdinalIgnoreCase))
|
||||
// {
|
||||
// return 3;
|
||||
// }
|
||||
// if (string.Equals(i.Type, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase))
|
||||
// {
|
||||
// return 3;
|
||||
// }
|
||||
// if (string.Equals(i.Type, PersonType.Writer, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Writer, StringComparison.OrdinalIgnoreCase))
|
||||
// {
|
||||
// return 2;
|
||||
// }
|
||||
|
||||
// return 1;
|
||||
// });
|
||||
|
||||
// return points;
|
||||
|
||||
//Logger.Debug("Returning score {0}", score);
|
||||
return score;
|
||||
}
|
||||
|
||||
private int GetListScore(object[] args, int index1, int index2, int value = 10)
|
||||
{
|
||||
var score = 0;
|
||||
|
||||
var inputGenres = args[index1] as string;
|
||||
var rowGenres = args[index2] as string;
|
||||
var inputGenreList = string.IsNullOrWhiteSpace(inputGenres) ? new string[] { } : inputGenres.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var rowGenresList = string.IsNullOrWhiteSpace(rowGenres) ? new string[] { } : rowGenres.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
foreach (var genre in inputGenreList)
|
||||
{
|
||||
if (rowGenresList.Contains(genre, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
score += value;
|
||||
}
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,10 +88,13 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
|||
private IDbCommand _deleteProviderIdsCommand;
|
||||
private IDbCommand _saveProviderIdsCommand;
|
||||
|
||||
private IDbCommand _deleteImagesCommand;
|
||||
private IDbCommand _saveImagesCommand;
|
||||
|
||||
private IDbCommand _updateInheritedRatingCommand;
|
||||
private IDbCommand _updateInheritedTagsCommand;
|
||||
|
||||
public const int LatestSchemaVersion = 83;
|
||||
public const int LatestSchemaVersion = 89;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
|
||||
|
@ -132,9 +135,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
|||
string[] queries = {
|
||||
|
||||
"create table if not exists TypedBaseItems (guid GUID primary key, type TEXT, data BLOB, ParentId GUID, Path TEXT)",
|
||||
"create index if not exists idx_TypedBaseItems on TypedBaseItems(guid)",
|
||||
"create index if not exists idx_PathTypedBaseItems on TypedBaseItems(Path)",
|
||||
"create index if not exists idx_ParentIdTypedBaseItems on TypedBaseItems(ParentId)",
|
||||
"create index if not exists idx_TypedBaseItems2 on TypedBaseItems(Type,Guid)",
|
||||
|
||||
"create table if not exists AncestorIds (ItemId GUID, AncestorId GUID, AncestorIdText TEXT, PRIMARY KEY (ItemId, AncestorId))",
|
||||
"create index if not exists idx_AncestorIds1 on AncestorIds(AncestorId)",
|
||||
|
@ -145,10 +148,14 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
|||
|
||||
"create table if not exists ItemValues (ItemId GUID, Type INT, Value TEXT)",
|
||||
"create index if not exists idx_ItemValues on ItemValues(ItemId)",
|
||||
"create index if not exists idx_ItemValues2 on ItemValues(ItemId,Type)",
|
||||
|
||||
"create table if not exists ProviderIds (ItemId GUID, Name TEXT, Value TEXT)",
|
||||
"create table if not exists ProviderIds (ItemId GUID, Name TEXT, Value TEXT, PRIMARY KEY (ItemId, Name))",
|
||||
"create index if not exists Idx_ProviderIds on ProviderIds(ItemId)",
|
||||
|
||||
"create table if not exists Images (ItemId GUID NOT NULL, Path TEXT NOT NULL, ImageType INT NOT NULL, DateModified DATETIME, IsPlaceHolder BIT NOT NULL, SortOrder INT)",
|
||||
"create index if not exists idx_Images on Images(ItemId)",
|
||||
|
||||
"create table if not exists People (ItemId GUID, Name TEXT NOT NULL, Role TEXT, PersonType TEXT, SortOrder int, ListOrder int)",
|
||||
"create index if not exists idxPeopleItemId on People(ItemId)",
|
||||
"create index if not exists idxPeopleName on People(Name)",
|
||||
|
@ -265,8 +272,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
|||
new MediaStreamColumns(_connection, Logger).AddColumns();
|
||||
|
||||
DataExtensions.Attach(_connection, Path.Combine(_config.ApplicationPaths.DataPath, "userdata_v2.db"), "UserDataDb");
|
||||
|
||||
dbConnector.BindSimilarityScoreFunction(_connection);
|
||||
}
|
||||
|
||||
private readonly string[] _retriveItemColumns =
|
||||
|
@ -565,6 +570,19 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
|||
_saveProviderIdsCommand.Parameters.Add(_saveProviderIdsCommand, "@Name");
|
||||
_saveProviderIdsCommand.Parameters.Add(_saveProviderIdsCommand, "@Value");
|
||||
|
||||
// images
|
||||
_deleteImagesCommand = _connection.CreateCommand();
|
||||
_deleteImagesCommand.CommandText = "delete from Images where ItemId=@Id";
|
||||
_deleteImagesCommand.Parameters.Add(_deleteImagesCommand, "@Id");
|
||||
|
||||
_saveImagesCommand = _connection.CreateCommand();
|
||||
_saveImagesCommand.CommandText = "insert into Images (ItemId, ImageType, Path, DateModified, IsPlaceHolder, SortOrder) values (@ItemId, @ImageType, @Path, @DateModified, @IsPlaceHolder, @SortOrder)";
|
||||
_saveImagesCommand.Parameters.Add(_saveImagesCommand, "@ItemId");
|
||||
_saveImagesCommand.Parameters.Add(_saveImagesCommand, "@ImageType");
|
||||
_saveImagesCommand.Parameters.Add(_saveImagesCommand, "@Path");
|
||||
_saveImagesCommand.Parameters.Add(_saveImagesCommand, "@DateModified");
|
||||
_saveImagesCommand.Parameters.Add(_saveImagesCommand, "@IsPlaceHolder");
|
||||
_saveImagesCommand.Parameters.Add(_saveImagesCommand, "@SortOrder");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -879,6 +897,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
|||
}
|
||||
|
||||
UpdateUserDataKeys(item.Id, item.GetUserDataKeys().Distinct(StringComparer.OrdinalIgnoreCase).ToList(), transaction);
|
||||
UpdateImages(item.Id, item.ImageInfos, transaction);
|
||||
UpdateProviderIds(item.Id, item.ProviderIds, transaction);
|
||||
UpdateItemValues(item.Id, GetItemValues(item), transaction);
|
||||
}
|
||||
|
@ -956,7 +975,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
|||
|
||||
if (type == null)
|
||||
{
|
||||
Logger.Debug("Unknown type {0}", typeString);
|
||||
//Logger.Debug("Unknown type {0}", typeString);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -1621,34 +1640,34 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
|||
var item = query.SimilarTo;
|
||||
|
||||
var builder = new StringBuilder();
|
||||
builder.Append("GetSimilarityScore(");
|
||||
builder.Append("(");
|
||||
|
||||
builder.Append("@ItemOfficialRating,");
|
||||
builder.Append("OfficialRating,");
|
||||
builder.Append("((OfficialRating=@ItemOfficialRating) * 10)");
|
||||
//builder.Append("+ ((ProductionYear=@ItemProductionYear) * 10)");
|
||||
|
||||
builder.Append("@ItemProductionYear,");
|
||||
builder.Append("ProductionYear,");
|
||||
builder.Append("+(Select Case When Abs(COALESCE(ProductionYear, 0) - @ItemProductionYear) < 10 Then 2 Else 0 End )");
|
||||
builder.Append("+(Select Case When Abs(COALESCE(ProductionYear, 0) - @ItemProductionYear) < 5 Then 2 Else 0 End )");
|
||||
|
||||
builder.Append("@ItemGenres,");
|
||||
builder.Append("Genres,");
|
||||
//// genres
|
||||
builder.Append("+ ((Select count(value) from ItemValues where ItemId=Guid and Type=2 and value in (select value from itemvalues where ItemId=@SimilarItemId and type=2)) * 10)");
|
||||
|
||||
builder.Append("@ItemTags,");
|
||||
builder.Append("Tags,");
|
||||
//// tags
|
||||
builder.Append("+ ((Select count(value) from ItemValues where ItemId=Guid and Type=4 and value in (select value from itemvalues where ItemId=@SimilarItemId and type=4)) * 10)");
|
||||
|
||||
builder.Append("@ItemKeywords,");
|
||||
builder.Append("(select group_concat((Select Value from ItemValues where ItemId=Guid and Type=5), '|')),");
|
||||
builder.Append("+ ((Select count(value) from ItemValues where ItemId=Guid and Type=5 and value in (select value from itemvalues where ItemId=@SimilarItemId and type=5)) * 10)");
|
||||
|
||||
builder.Append("+ ((Select count(value) from ItemValues where ItemId=Guid and Type=3 and value in (select value from itemvalues where ItemId=@SimilarItemId and type=3)) * 3)");
|
||||
|
||||
//builder.Append("+ ((Select count(Name) from People where ItemId=Guid and Name in (select Name from People where ItemId=@SimilarItemId)) * 3)");
|
||||
|
||||
////builder.Append("(select group_concat((Select Name from People where ItemId=Guid and Name in (Select Name from People where ItemId=@SimilarItemId)), '|'))");
|
||||
|
||||
builder.Append("@ItemStudios,");
|
||||
builder.Append("Studios");
|
||||
builder.Append(") as SimilarityScore");
|
||||
|
||||
list.Add(builder.ToString());
|
||||
cmd.Parameters.Add(cmd, "@ItemOfficialRating", DbType.String).Value = item.OfficialRating;
|
||||
cmd.Parameters.Add(cmd, "@ItemProductionYear", DbType.Int32).Value = item.ProductionYear ?? -1;
|
||||
cmd.Parameters.Add(cmd, "@ItemGenres", DbType.String).Value = string.Join("|", item.Genres.ToArray());
|
||||
cmd.Parameters.Add(cmd, "@ItemTags", DbType.String).Value = string.Join("|", item.Tags.ToArray());
|
||||
cmd.Parameters.Add(cmd, "@ItemKeywords", DbType.String).Value = string.Join("|", item.Keywords.ToArray());
|
||||
cmd.Parameters.Add(cmd, "@ItemStudios", DbType.String).Value = string.Join("|", item.Studios.ToArray());
|
||||
cmd.Parameters.Add(cmd, "@ItemProductionYear", DbType.Int32).Value = item.ProductionYear ?? 0;
|
||||
cmd.Parameters.Add(cmd, "@SimilarItemId", DbType.Guid).Value = item.Id;
|
||||
|
||||
var excludeIds = query.ExcludeItemIds.ToList();
|
||||
excludeIds.Add(item.Id.ToString("N"));
|
||||
|
@ -1862,7 +1881,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
|||
{
|
||||
if (query.User != null)
|
||||
{
|
||||
query.SortBy = new[] { "SimilarityScore", "IsUnplayed", "Random" };
|
||||
query.SortBy = new[] { "SimilarityScore", "IsPlayed", "Random" };
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2475,6 +2494,19 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
|||
cmd.Parameters.Add(cmd, "@NameLessThan", DbType.String).Value = query.NameLessThan.ToLower();
|
||||
}
|
||||
|
||||
if (query.ImageTypes.Length > 0 && _config.Configuration.SchemaVersion >= 87)
|
||||
{
|
||||
var requiredImageIndex = 0;
|
||||
|
||||
foreach (var requiredImage in query.ImageTypes)
|
||||
{
|
||||
var paramName = "@RequiredImageType" + requiredImageIndex;
|
||||
whereClauses.Add("(select path from images where ItemId=Guid and ImageType=" + paramName + " limit 1) not null");
|
||||
cmd.Parameters.Add(cmd, paramName, DbType.Int32).Value = (int)requiredImage;
|
||||
requiredImageIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
if (query.IsLiked.HasValue)
|
||||
{
|
||||
if (query.IsLiked.Value)
|
||||
|
@ -2738,8 +2770,13 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
|||
var index = 0;
|
||||
foreach (var pair in query.ExcludeProviderIds)
|
||||
{
|
||||
if (string.Equals(pair.Key, MetadataProviders.TmdbCollection.ToString(), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var paramName = "@ExcludeProviderId" + index;
|
||||
excludeIds.Add("(COALESCE((select value from ProviderIds where ItemId=Guid and Name = 'Imdb'), '') <> " + paramName + ")");
|
||||
excludeIds.Add("(COALESCE((select value from ProviderIds where ItemId=Guid and Name = '" + pair.Key + "'), '') <> " + paramName + ")");
|
||||
cmd.Parameters.Add(cmd, paramName, DbType.String).Value = pair.Value;
|
||||
index++;
|
||||
}
|
||||
|
@ -3180,6 +3217,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
|||
_deleteProviderIdsCommand.Transaction = transaction;
|
||||
_deleteProviderIdsCommand.ExecuteNonQuery();
|
||||
|
||||
// Delete images
|
||||
_deleteImagesCommand.GetParameter(0).Value = id;
|
||||
_deleteImagesCommand.Transaction = transaction;
|
||||
_deleteImagesCommand.ExecuteNonQuery();
|
||||
|
||||
// Delete the item
|
||||
_deleteItemCommand.GetParameter(0).Value = id;
|
||||
_deleteItemCommand.Transaction = transaction;
|
||||
|
@ -3396,6 +3438,52 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
|||
return list;
|
||||
}
|
||||
|
||||
private void UpdateImages(Guid itemId, List<ItemImageInfo> images, IDbTransaction transaction)
|
||||
{
|
||||
if (itemId == Guid.Empty)
|
||||
{
|
||||
throw new ArgumentNullException("itemId");
|
||||
}
|
||||
|
||||
if (images == null)
|
||||
{
|
||||
throw new ArgumentNullException("images");
|
||||
}
|
||||
|
||||
CheckDisposed();
|
||||
|
||||
// First delete
|
||||
_deleteImagesCommand.GetParameter(0).Value = itemId;
|
||||
_deleteImagesCommand.Transaction = transaction;
|
||||
|
||||
_deleteImagesCommand.ExecuteNonQuery();
|
||||
|
||||
var index = 0;
|
||||
foreach (var image in images)
|
||||
{
|
||||
_saveImagesCommand.GetParameter(0).Value = itemId;
|
||||
_saveImagesCommand.GetParameter(1).Value = image.Type;
|
||||
_saveImagesCommand.GetParameter(2).Value = image.Path;
|
||||
|
||||
if (image.DateModified == default(DateTime))
|
||||
{
|
||||
_saveImagesCommand.GetParameter(3).Value = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
_saveImagesCommand.GetParameter(3).Value = image.DateModified;
|
||||
}
|
||||
|
||||
_saveImagesCommand.GetParameter(4).Value = image.IsPlaceholder;
|
||||
_saveImagesCommand.GetParameter(5).Value = index;
|
||||
|
||||
_saveImagesCommand.Transaction = transaction;
|
||||
|
||||
_saveImagesCommand.ExecuteNonQuery();
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateProviderIds(Guid itemId, Dictionary<string, string> values, IDbTransaction transaction)
|
||||
{
|
||||
if (itemId == Guid.Empty)
|
||||
|
@ -3405,7 +3493,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
|||
|
||||
if (values == null)
|
||||
{
|
||||
throw new ArgumentNullException("keys");
|
||||
throw new ArgumentNullException("values");
|
||||
}
|
||||
|
||||
CheckDisposed();
|
||||
|
|
|
@ -5,7 +5,9 @@ using MediaBrowser.Model.Logging;
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
@ -300,6 +302,52 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
|||
}
|
||||
}
|
||||
|
||||
public UserItemData GetUserData(Guid userId, List<string> keys)
|
||||
{
|
||||
if (userId == Guid.Empty)
|
||||
{
|
||||
throw new ArgumentNullException("userId");
|
||||
}
|
||||
if (keys == null)
|
||||
{
|
||||
throw new ArgumentNullException("keys");
|
||||
}
|
||||
|
||||
using (var cmd = _connection.CreateCommand())
|
||||
{
|
||||
var index = 0;
|
||||
var excludeIds = new List<string>();
|
||||
var builder = new StringBuilder();
|
||||
foreach (var key in keys)
|
||||
{
|
||||
var paramName = "@Key" + index;
|
||||
excludeIds.Add("Key =" + paramName);
|
||||
cmd.Parameters.Add(cmd, paramName, DbType.String).Value = key;
|
||||
builder.Append(" WHEN Key=" + paramName + " THEN " + index);
|
||||
index++;
|
||||
}
|
||||
|
||||
var keyText = string.Join(" OR ", excludeIds.ToArray());
|
||||
|
||||
cmd.CommandText = "select key,userid,rating,played,playCount,isFavorite,playbackPositionTicks,lastPlayedDate,AudioStreamIndex,SubtitleStreamIndex from userdata where userId=@userId AND (" + keyText + ") ";
|
||||
|
||||
cmd.CommandText += " ORDER BY (Case " + builder + " Else " + keys.Count.ToString(CultureInfo.InvariantCulture) + " End )";
|
||||
cmd.CommandText += " LIMIT 1";
|
||||
|
||||
cmd.Parameters.Add(cmd, "@userId", DbType.Guid).Value = userId;
|
||||
|
||||
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow))
|
||||
{
|
||||
if (reader.Read())
|
||||
{
|
||||
return ReadRow(reader);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return all user-data associated with the given user
|
||||
/// </summary>
|
||||
|
|
|
@ -111,24 +111,6 @@ namespace MediaBrowser.Server.Implementations.TV
|
|||
.Select(i => GetNextUp(i, currentUser))
|
||||
// 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)))
|
||||
//.OrderByDescending(i =>
|
||||
//{
|
||||
// var episode = i.Item1;
|
||||
|
||||
// var seriesUserData = _userDataManager.GetUserData(user, episode.Series);
|
||||
|
||||
// if (seriesUserData.IsFavorite)
|
||||
// {
|
||||
// return 2;
|
||||
// }
|
||||
|
||||
// if (seriesUserData.Likes.HasValue)
|
||||
// {
|
||||
// return seriesUserData.Likes.Value ? 1 : -1;
|
||||
// }
|
||||
|
||||
// return 0;
|
||||
//})
|
||||
.OrderByDescending(i => i.Item2)
|
||||
.ThenByDescending(i => i.Item1.PremiereDate ?? DateTime.MinValue)
|
||||
.Select(i => i.Item1);
|
||||
|
@ -143,9 +125,8 @@ namespace MediaBrowser.Server.Implementations.TV
|
|||
private Tuple<Episode, DateTime, bool> GetNextUp(Series series, User user)
|
||||
{
|
||||
// Get them in display order, then reverse
|
||||
var allEpisodes = series.GetSeasons(user, true, true)
|
||||
.Where(i => !i.IndexNumber.HasValue || i.IndexNumber.Value != 0)
|
||||
.SelectMany(i => i.GetEpisodes(user))
|
||||
var allEpisodes = series.GetEpisodes(user, false, false)
|
||||
.Where(i => !i.ParentIndexNumber.HasValue || i.ParentIndexNumber.Value != 0)
|
||||
.Reverse()
|
||||
.ToList();
|
||||
|
||||
|
@ -153,7 +134,7 @@ namespace MediaBrowser.Server.Implementations.TV
|
|||
var lastWatchedDate = DateTime.MinValue;
|
||||
Episode nextUp = null;
|
||||
|
||||
var includeMissing = user.Configuration.DisplayMissingEpisodes;
|
||||
var unplayedEpisodes = new List<Episode>();
|
||||
|
||||
// Go back starting with the most recent episodes
|
||||
foreach (var episode in allEpisodes)
|
||||
|
@ -172,10 +153,9 @@ namespace MediaBrowser.Server.Implementations.TV
|
|||
}
|
||||
else
|
||||
{
|
||||
if (!episode.IsVirtualUnaired && (includeMissing || !episode.IsMissingEpisode))
|
||||
{
|
||||
nextUp = episode;
|
||||
}
|
||||
unplayedEpisodes.Add(episode);
|
||||
|
||||
nextUp = episode;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,7 +164,15 @@ namespace MediaBrowser.Server.Implementations.TV
|
|||
return new Tuple<Episode, DateTime, bool>(nextUp, lastWatchedDate, false);
|
||||
}
|
||||
|
||||
var firstEpisode = allEpisodes.LastOrDefault(i => !i.IsVirtualUnaired && (includeMissing || !i.IsMissingEpisode) && !i.IsPlayed(user));
|
||||
Episode firstEpisode = null;
|
||||
// Find the first unplayed episode. Start from the back of the list since they're in reverse order
|
||||
for (var i = unplayedEpisodes.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var unplayedEpisode = unplayedEpisodes[i];
|
||||
|
||||
firstEpisode = unplayedEpisode;
|
||||
break;
|
||||
}
|
||||
|
||||
// Return the first episode
|
||||
return new Tuple<Episode, DateTime, bool>(firstEpisode, DateTime.MinValue, true);
|
||||
|
|
|
@ -16,11 +16,6 @@ namespace MediaBrowser.Server.Mono.Native
|
|||
_logger = logger;
|
||||
}
|
||||
|
||||
public void BindSimilarityScoreFunction(IDbConnection connection)
|
||||
{
|
||||
SqliteExtensions.BindGetSimilarityScore(connection, _logger);
|
||||
}
|
||||
|
||||
public Task<IDbConnection> Connect(string dbPath)
|
||||
{
|
||||
return SqliteExtensions.ConnectToDb(dbPath, _logger);
|
||||
|
|
|
@ -380,7 +380,8 @@ namespace MediaBrowser.Server.Startup.Common
|
|||
{
|
||||
new OmdbEpisodeProviderMigration(ServerConfigurationManager),
|
||||
new MovieDbEpisodeProviderMigration(ServerConfigurationManager),
|
||||
new DbMigration(ServerConfigurationManager, TaskManager)
|
||||
new DbMigration(ServerConfigurationManager, TaskManager),
|
||||
new FolderViewSettingMigration(ServerConfigurationManager, UserManager)
|
||||
};
|
||||
|
||||
foreach (var task in migrations)
|
||||
|
@ -568,7 +569,7 @@ namespace MediaBrowser.Server.Startup.Common
|
|||
|
||||
SubtitleEncoder = new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager);
|
||||
RegisterSingleInstance(SubtitleEncoder);
|
||||
|
||||
|
||||
await displayPreferencesRepo.Initialize(NativeApp.GetDbConnector()).ConfigureAwait(false);
|
||||
await ConfigureUserDataRepositories().ConfigureAwait(false);
|
||||
await itemRepo.Initialize(NativeApp.GetDbConnector()).ConfigureAwait(false);
|
||||
|
|
|
@ -71,6 +71,8 @@
|
|||
<Compile Include="FFMpeg\FFmpegValidator.cs" />
|
||||
<Compile Include="INativeApp.cs" />
|
||||
<Compile Include="MbLinkShortcutHandler.cs" />
|
||||
<Compile Include="Migrations\CollectionGroupingMigration.cs" />
|
||||
<Compile Include="Migrations\FolderViewSettingMigration.cs" />
|
||||
<Compile Include="Migrations\IVersionMigration.cs" />
|
||||
<Compile Include="Migrations\DbMigration.cs" />
|
||||
<Compile Include="Migrations\MovieDbEpisodeProviderMigration.cs" />
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Library;
|
||||
|
||||
namespace MediaBrowser.Server.Startup.Common.Migrations
|
||||
{
|
||||
public class CollectionGroupingMigration : IVersionMigration
|
||||
{
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly IUserManager _userManager;
|
||||
|
||||
public CollectionGroupingMigration(IServerConfigurationManager config, IUserManager userManager)
|
||||
{
|
||||
_config = config;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
var migrationKey = this.GetType().Name;
|
||||
var migrationKeyList = _config.Configuration.Migrations.ToList();
|
||||
|
||||
if (!migrationKeyList.Contains(migrationKey))
|
||||
{
|
||||
if (_config.Configuration.IsStartupWizardCompleted)
|
||||
{
|
||||
if (_userManager.Users.Any(i => i.Configuration.GroupMoviesIntoBoxSets))
|
||||
{
|
||||
_config.Configuration.EnableGroupingIntoCollections = true;
|
||||
}
|
||||
}
|
||||
|
||||
migrationKeyList.Add(migrationKey);
|
||||
_config.Configuration.Migrations = migrationKeyList.ToArray();
|
||||
_config.SaveConfiguration();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
using System.Linq;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Library;
|
||||
|
||||
namespace MediaBrowser.Server.Startup.Common.Migrations
|
||||
{
|
||||
public class FolderViewSettingMigration : IVersionMigration
|
||||
{
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly IUserManager _userManager;
|
||||
|
||||
public FolderViewSettingMigration(IServerConfigurationManager config, IUserManager userManager)
|
||||
{
|
||||
_config = config;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
var migrationKey = this.GetType().Name;
|
||||
var migrationKeyList = _config.Configuration.Migrations.ToList();
|
||||
|
||||
if (!migrationKeyList.Contains(migrationKey))
|
||||
{
|
||||
if (_config.Configuration.IsStartupWizardCompleted)
|
||||
{
|
||||
if (_userManager.Users.Any(i => i.Configuration.DisplayFoldersView))
|
||||
{
|
||||
_config.Configuration.EnableFolderView = true;
|
||||
}
|
||||
}
|
||||
|
||||
migrationKeyList.Add(migrationKey);
|
||||
_config.Configuration.Migrations = migrationKeyList.ToArray();
|
||||
_config.SaveConfiguration();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,11 +16,6 @@ namespace MediaBrowser.ServerApplication.Native
|
|||
_logger = logger;
|
||||
}
|
||||
|
||||
public void BindSimilarityScoreFunction(IDbConnection connection)
|
||||
{
|
||||
SqliteExtensions.BindGetSimilarityScore(connection, _logger);
|
||||
}
|
||||
|
||||
public async Task<IDbConnection> Connect(string dbPath)
|
||||
{
|
||||
try
|
||||
|
|
|
@ -137,9 +137,6 @@
|
|||
<Content Include="dashboard-ui\components\remotecontrolautoplay.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\components\scrollthreshold.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\components\tvproviders\xmltv.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
@ -275,6 +272,9 @@
|
|||
<Content Include="dashboard-ui\legacy\selectmenu.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\librarydisplay.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\livetvguideprovider.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
@ -311,6 +311,9 @@
|
|||
<Content Include="dashboard-ui\scripts\homeupcoming.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\scripts\librarydisplay.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\scripts\livetvguideprovider.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
|
|
@ -919,11 +919,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
|
|||
var val = reader.ReadElementContentAsString();
|
||||
if (!string.IsNullOrWhiteSpace(val))
|
||||
{
|
||||
var hasTags = item as IHasTags;
|
||||
if (hasTags != null)
|
||||
{
|
||||
hasTags.AddTag(val);
|
||||
}
|
||||
item.AddTag(val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -736,19 +736,15 @@ namespace MediaBrowser.XbmcMetadata.Savers
|
|||
writer.WriteElementString("studio", studio);
|
||||
}
|
||||
|
||||
var hasTags = item as IHasTags;
|
||||
if (hasTags != null)
|
||||
foreach (var tag in item.Tags)
|
||||
{
|
||||
foreach (var tag in hasTags.Tags)
|
||||
if (item is MusicAlbum || item is MusicArtist)
|
||||
{
|
||||
if (item is MusicAlbum || item is MusicArtist)
|
||||
{
|
||||
writer.WriteElementString("style", tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteElementString("tag", tag);
|
||||
}
|
||||
writer.WriteElementString("style", tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WriteElementString("tag", tag);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>MediaBrowser.Common.Internal</id>
|
||||
<version>3.0.649</version>
|
||||
<version>3.0.650</version>
|
||||
<title>MediaBrowser.Common.Internal</title>
|
||||
<authors>Luke</authors>
|
||||
<owners>ebr,Luke,scottisafool</owners>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<description>Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.</description>
|
||||
<copyright>Copyright © Emby 2013</copyright>
|
||||
<dependencies>
|
||||
<dependency id="MediaBrowser.Common" version="3.0.649" />
|
||||
<dependency id="MediaBrowser.Common" version="3.0.650" />
|
||||
<dependency id="NLog" version="4.3.4" />
|
||||
<dependency id="SimpleInjector" version="3.1.5" />
|
||||
</dependencies>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>MediaBrowser.Common</id>
|
||||
<version>3.0.649</version>
|
||||
<version>3.0.650</version>
|
||||
<title>MediaBrowser.Common</title>
|
||||
<authors>Emby Team</authors>
|
||||
<owners>ebr,Luke,scottisafool</owners>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>MediaBrowser.Server.Core</id>
|
||||
<version>3.0.649</version>
|
||||
<version>3.0.650</version>
|
||||
<title>Media Browser.Server.Core</title>
|
||||
<authors>Emby Team</authors>
|
||||
<owners>ebr,Luke,scottisafool</owners>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<description>Contains core components required to build plugins for Emby Server.</description>
|
||||
<copyright>Copyright © Emby 2013</copyright>
|
||||
<dependencies>
|
||||
<dependency id="MediaBrowser.Common" version="3.0.649" />
|
||||
<dependency id="MediaBrowser.Common" version="3.0.650" />
|
||||
<dependency id="Interfaces.IO" version="1.0.0.5" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
|
Loading…
Reference in New Issue
Block a user