Merge branch 'dev' of https://github.com/MediaBrowser/MediaBrowser into dev
1
.gitignore
vendored
|
@ -210,7 +210,6 @@ $RECYCLE.BIN/
|
|||
# Packages
|
||||
*.egg
|
||||
*.egg-info
|
||||
dist/
|
||||
build/
|
||||
eggs/
|
||||
parts/
|
||||
|
|
|
@ -340,7 +340,7 @@ namespace MediaBrowser.Api
|
|||
// We can really reduce the timeout for apps that are using the newer api
|
||||
if (!string.IsNullOrWhiteSpace(job.PlaySessionId))
|
||||
{
|
||||
timerDuration = 120000;
|
||||
timerDuration = 300000;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -344,9 +344,7 @@ namespace MediaBrowser.Api
|
|||
return name;
|
||||
}
|
||||
|
||||
return libraryManager.GetAllPeople()
|
||||
.Select(i => i.Name)
|
||||
.DistinctNames()
|
||||
return libraryManager.GetPeopleNames(new InternalPeopleQuery())
|
||||
.FirstOrDefault(i =>
|
||||
{
|
||||
i = _dashReplaceChars.Aggregate(i, (current, c) => current.Replace(c, SlugChar));
|
||||
|
|
|
@ -109,6 +109,7 @@
|
|||
<Compile Include="Reports\Stat\ReportStatGroup.cs" />
|
||||
<Compile Include="Reports\Stat\ReportStatItem.cs" />
|
||||
<Compile Include="Reports\Stat\ReportStatResult.cs" />
|
||||
<Compile Include="Social\SharingService.cs" />
|
||||
<Compile Include="StartupWizardService.cs" />
|
||||
<Compile Include="Subtitles\SubtitleService.cs" />
|
||||
<Compile Include="Movies\CollectionService.cs" />
|
||||
|
|
|
@ -165,7 +165,7 @@ namespace MediaBrowser.Api.Movies
|
|||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
private async Task<ItemsResult> GetSimilarItemsResult(BaseGetSimilarItemsFromItem request, Func<BaseItem, bool> includeInSearch, Func<BaseItem, BaseItem, ILibraryManager, int> getSimilarityScore)
|
||||
private async Task<ItemsResult> GetSimilarItemsResult(BaseGetSimilarItemsFromItem request, Func<BaseItem, bool> includeInSearch, Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore)
|
||||
{
|
||||
var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
|
||||
|
||||
|
@ -358,12 +358,15 @@ namespace MediaBrowser.Api.Movies
|
|||
|
||||
private IEnumerable<RecommendationDto> GetWithActor(User user, List<BaseItem> allMovies, IEnumerable<string> names, int itemLimit, DtoOptions dtoOptions, RecommendationType type)
|
||||
{
|
||||
var userId = user.Id;
|
||||
|
||||
foreach (var name in names)
|
||||
{
|
||||
var itemsWithActor = _libraryManager.GetItemIds(new InternalItemsQuery
|
||||
{
|
||||
Person = name
|
||||
});
|
||||
|
||||
var items = allMovies
|
||||
.Where(i => _libraryManager.GetPeople(i).Any(p => string.Equals(p.Name, name, StringComparison.OrdinalIgnoreCase)))
|
||||
.Where(i => itemsWithActor.Contains(i.Id))
|
||||
.Take(itemLimit)
|
||||
.ToList();
|
||||
|
||||
|
@ -382,8 +385,6 @@ namespace MediaBrowser.Api.Movies
|
|||
|
||||
private IEnumerable<RecommendationDto> GetSimilarTo(User user, List<BaseItem> allMovies, IEnumerable<BaseItem> baselineItems, int itemLimit, DtoOptions dtoOptions, RecommendationType type)
|
||||
{
|
||||
var userId = user.Id;
|
||||
|
||||
foreach (var item in baselineItems)
|
||||
{
|
||||
var similar = SimilarItemsHelper
|
||||
|
@ -406,18 +407,37 @@ namespace MediaBrowser.Api.Movies
|
|||
|
||||
private IEnumerable<string> GetActors(IEnumerable<BaseItem> items)
|
||||
{
|
||||
// Get the two leading actors for all movies
|
||||
return items
|
||||
.SelectMany(i => _libraryManager.GetPeople(i).Where(p => !string.Equals(PersonType.Director, p.Type, StringComparison.OrdinalIgnoreCase)).Take(2))
|
||||
var people = _libraryManager.GetPeople(new InternalPeopleQuery
|
||||
{
|
||||
ExcludePersonTypes = new List<string>
|
||||
{
|
||||
PersonType.Director
|
||||
},
|
||||
MaxListOrder = 3
|
||||
});
|
||||
|
||||
var itemIds = items.Select(i => i.Id).ToList();
|
||||
|
||||
return people
|
||||
.Where(i => itemIds.Contains(i.ItemId))
|
||||
.Select(i => i.Name)
|
||||
.DistinctNames();
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetDirectors(IEnumerable<BaseItem> items)
|
||||
{
|
||||
return items
|
||||
.Select(i => _libraryManager.GetPeople(i).FirstOrDefault(p => string.Equals(PersonType.Director, p.Type, StringComparison.OrdinalIgnoreCase)))
|
||||
.Where(i => i != null)
|
||||
var people = _libraryManager.GetPeople(new InternalPeopleQuery
|
||||
{
|
||||
PersonTypes = new List<string>
|
||||
{
|
||||
PersonType.Director
|
||||
}
|
||||
});
|
||||
|
||||
var itemIds = items.Select(i => i.Id).ToList();
|
||||
|
||||
return people
|
||||
.Where(i => itemIds.Contains(i.ItemId))
|
||||
.Select(i => i.Name)
|
||||
.DistinctNames();
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ using MediaBrowser.Controller.Net;
|
|||
using MediaBrowser.Controller.Persistence;
|
||||
using ServiceStack;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Api.Music
|
||||
|
@ -68,12 +69,13 @@ namespace MediaBrowser.Api.Music
|
|||
/// Gets the album similarity score.
|
||||
/// </summary>
|
||||
/// <param name="item1">The item1.</param>
|
||||
/// <param name="item1People">The item1 people.</param>
|
||||
/// <param name="allPeople">All people.</param>
|
||||
/// <param name="item2">The item2.</param>
|
||||
/// <param name="libraryManager">The library manager.</param>
|
||||
/// <returns>System.Int32.</returns>
|
||||
private int GetAlbumSimilarityScore(BaseItem item1, BaseItem item2, ILibraryManager libraryManager)
|
||||
private int GetAlbumSimilarityScore(BaseItem item1, List<PersonInfo> item1People, List<PersonInfo> allPeople, BaseItem item2)
|
||||
{
|
||||
var points = SimilarItemsHelper.GetSimiliarityScore(item1, item2, libraryManager);
|
||||
var points = SimilarItemsHelper.GetSimiliarityScore(item1, item1People, allPeople, item2);
|
||||
|
||||
var album1 = (MusicAlbum)item1;
|
||||
var album2 = (MusicAlbum)item2;
|
||||
|
|
|
@ -632,7 +632,7 @@ namespace MediaBrowser.Api.Playback
|
|||
{
|
||||
var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
|
||||
|
||||
filters.Add(string.Format("scale=trunc(oh*a*2)/2:min(ih\\,{0})", maxHeightParam));
|
||||
filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(ih\\,{0})", maxHeightParam));
|
||||
}
|
||||
|
||||
if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))
|
||||
|
|
|
@ -313,17 +313,18 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
{
|
||||
var segmentPath = GetSegmentPath(state, playlist, i);
|
||||
|
||||
double length;
|
||||
if (SegmentLengths.TryGetValue(Path.GetFileName(segmentPath), out length))
|
||||
{
|
||||
Logger.Debug("Found segment length of {0} for index {1}", length, i);
|
||||
startSeconds += length;
|
||||
}
|
||||
else
|
||||
{
|
||||
//double length;
|
||||
//if (SegmentLengths.TryGetValue(Path.GetFileName(segmentPath), out length))
|
||||
//{
|
||||
// Logger.Debug("Found segment length of {0} for index {1}", length, i);
|
||||
// startSeconds += length;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// startSeconds += state.SegmentLength;
|
||||
//}
|
||||
startSeconds += state.SegmentLength;
|
||||
}
|
||||
}
|
||||
|
||||
var position = TimeSpan.FromSeconds(startSeconds).Ticks;
|
||||
return position;
|
||||
|
@ -441,7 +442,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
CancellationToken cancellationToken)
|
||||
{
|
||||
// If all transcoding has completed, just return immediately
|
||||
if (transcodingJob != null && transcodingJob.HasExited)
|
||||
if (transcodingJob != null && transcodingJob.HasExited && File.Exists(segmentPath))
|
||||
{
|
||||
return GetSegmentResult(segmentPath, segmentIndex, segmentLength, transcodingJob);
|
||||
}
|
||||
|
@ -462,9 +463,13 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
|
||||
// If it appears in the playlist, it's done
|
||||
if (text.IndexOf(segmentFilename, StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
if (File.Exists(segmentPath))
|
||||
{
|
||||
return GetSegmentResult(segmentPath, segmentIndex, segmentLength, transcodingJob);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -564,11 +569,11 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
|
||||
builder.AppendLine("#EXTM3U");
|
||||
|
||||
var isLiveStream = (state.RunTimeTicks ?? 0) == 0;
|
||||
|
||||
var queryStringIndex = Request.RawUrl.IndexOf('?');
|
||||
var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex);
|
||||
|
||||
var isLiveStream = (state.RunTimeTicks ?? 0) == 0;
|
||||
|
||||
// Main stream
|
||||
var playlistUrl = isLiveStream ? "live.m3u8" : "main.m3u8";
|
||||
playlistUrl += queryString;
|
||||
|
|
|
@ -68,7 +68,7 @@ namespace MediaBrowser.Api
|
|||
/// <param name="includeInSearch">The include in search.</param>
|
||||
/// <param name="getSimilarityScore">The get similarity score.</param>
|
||||
/// <returns>ItemsResult.</returns>
|
||||
internal static ItemsResult GetSimilarItemsResult(DtoOptions dtoOptions, IUserManager userManager, IItemRepository itemRepository, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, ILogger logger, BaseGetSimilarItemsFromItem request, Func<BaseItem, bool> includeInSearch, Func<BaseItem, BaseItem, ILibraryManager, int> getSimilarityScore)
|
||||
internal static ItemsResult GetSimilarItemsResult(DtoOptions dtoOptions, IUserManager userManager, IItemRepository itemRepository, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, ILogger logger, BaseGetSimilarItemsFromItem request, Func<BaseItem, bool> includeInSearch, Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore)
|
||||
{
|
||||
var user = !string.IsNullOrWhiteSpace(request.UserId) ? userManager.GetUserById(request.UserId) : null;
|
||||
|
||||
|
@ -110,12 +110,17 @@ namespace MediaBrowser.Api
|
|||
/// <param name="inputItems">The input items.</param>
|
||||
/// <param name="getSimilarityScore">The get similarity score.</param>
|
||||
/// <returns>IEnumerable{BaseItem}.</returns>
|
||||
internal static IEnumerable<BaseItem> GetSimilaritems(BaseItem item, ILibraryManager libraryManager, IEnumerable<BaseItem> inputItems, Func<BaseItem, BaseItem, ILibraryManager, int> getSimilarityScore)
|
||||
internal static IEnumerable<BaseItem> GetSimilaritems(BaseItem item, ILibraryManager libraryManager, IEnumerable<BaseItem> inputItems, Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore)
|
||||
{
|
||||
var itemId = item.Id;
|
||||
inputItems = inputItems.Where(i => i.Id != itemId);
|
||||
var itemPeople = libraryManager.GetPeople(item);
|
||||
var allPeople = libraryManager.GetPeople(new InternalPeopleQuery
|
||||
{
|
||||
AppearsInItemId = item.Id
|
||||
});
|
||||
|
||||
return inputItems.Select(i => new Tuple<BaseItem, int>(i, getSimilarityScore(item, i, libraryManager)))
|
||||
return inputItems.Select(i => new Tuple<BaseItem, int>(i, getSimilarityScore(item, itemPeople, allPeople, i)))
|
||||
.Where(i => i.Item2 > 2)
|
||||
.OrderByDescending(i => i.Item2)
|
||||
.Select(i => i.Item1);
|
||||
|
@ -147,9 +152,11 @@ namespace MediaBrowser.Api
|
|||
/// Gets the similiarity score.
|
||||
/// </summary>
|
||||
/// <param name="item1">The item1.</param>
|
||||
/// <param name="item1People">The item1 people.</param>
|
||||
/// <param name="allPeople">All people.</param>
|
||||
/// <param name="item2">The item2.</param>
|
||||
/// <returns>System.Int32.</returns>
|
||||
internal static int GetSimiliarityScore(BaseItem item1, BaseItem item2, ILibraryManager libraryManager)
|
||||
internal static int GetSimiliarityScore(BaseItem item1, List<PersonInfo> item1People, List<PersonInfo> allPeople, BaseItem item2)
|
||||
{
|
||||
var points = 0;
|
||||
|
||||
|
@ -170,11 +177,13 @@ namespace MediaBrowser.Api
|
|||
// Find common studios
|
||||
points += item1.Studios.Where(i => item2.Studios.Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 3);
|
||||
|
||||
var item2PeopleNames = libraryManager.GetPeople(item2).Select(i => i.Name)
|
||||
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 += libraryManager.GetPeople(item1).Where(i => item2PeopleNames.ContainsKey(i.Name)).Sum(i =>
|
||||
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))
|
||||
{
|
||||
|
|
164
MediaBrowser.Api/Social/SharingService.cs
Normal file
|
@ -0,0 +1,164 @@
|
|||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller.Dlna;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Controller.Social;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Social;
|
||||
using ServiceStack;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api.Social
|
||||
{
|
||||
[Route("/Social/Shares/{Id}", "GET", Summary = "Gets a share")]
|
||||
[Authenticated]
|
||||
public class GetSocialShareInfo : IReturn<SocialShareInfo>
|
||||
{
|
||||
[ApiMember(Name = "Id", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Social/Shares/Public/{Id}", "GET", Summary = "Gets a share")]
|
||||
public class GetPublicSocialShareInfo : IReturn<SocialShareInfo>
|
||||
{
|
||||
[ApiMember(Name = "Id", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Social/Shares/Public/{Id}/Image", "GET", Summary = "Gets a share")]
|
||||
public class GetShareImage
|
||||
{
|
||||
[ApiMember(Name = "Id", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Social/Shares", "POST", Summary = "Creates a share")]
|
||||
[Authenticated]
|
||||
public class CreateShare : IReturn<SocialShareInfo>
|
||||
{
|
||||
[ApiMember(Name = "ItemId", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||
public string ItemId { get; set; }
|
||||
|
||||
[ApiMember(Name = "UserId", Description = "The user id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||
public string UserId { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Social/Shares/{Id}", "DELETE", Summary = "Deletes a share")]
|
||||
[Authenticated]
|
||||
public class DeleteShare : IReturnVoid
|
||||
{
|
||||
[ApiMember(Name = "Id", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Social/Shares/Public/{Id}/Item", "GET", Summary = "Gets a share")]
|
||||
public class GetSharedLibraryItem
|
||||
{
|
||||
[ApiMember(Name = "Id", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
public class SharingService : BaseApiService
|
||||
{
|
||||
private readonly ISharingManager _sharingManager;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly IDlnaManager _dlnaManager;
|
||||
private readonly IDtoService _dtoService;
|
||||
|
||||
public SharingService(ISharingManager sharingManager, IDlnaManager dlnaManager, ILibraryManager libraryManager, IDtoService dtoService)
|
||||
{
|
||||
_sharingManager = sharingManager;
|
||||
_dlnaManager = dlnaManager;
|
||||
_libraryManager = libraryManager;
|
||||
_dtoService = dtoService;
|
||||
}
|
||||
|
||||
public object Get(GetSocialShareInfo request)
|
||||
{
|
||||
var info = _sharingManager.GetShareInfo(request.Id);
|
||||
|
||||
return ToOptimizedResult(info);
|
||||
}
|
||||
|
||||
public object Get(GetSharedLibraryItem request)
|
||||
{
|
||||
var info = _sharingManager.GetShareInfo(request.Id);
|
||||
|
||||
if (info.ExpirationDate <= DateTime.UtcNow)
|
||||
{
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
var item = _libraryManager.GetItemById(info.ItemId);
|
||||
|
||||
var dto = _dtoService.GetBaseItemDto(item, new DtoOptions());
|
||||
|
||||
return ToOptimizedResult(dto);
|
||||
}
|
||||
|
||||
public object Get(GetPublicSocialShareInfo request)
|
||||
{
|
||||
var info = _sharingManager.GetShareInfo(request.Id);
|
||||
|
||||
if (info.ExpirationDate <= DateTime.UtcNow)
|
||||
{
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
return ToOptimizedResult(info);
|
||||
}
|
||||
|
||||
public async Task<object> Post(CreateShare request)
|
||||
{
|
||||
var info = await _sharingManager.CreateShare(request.ItemId, request.UserId).ConfigureAwait(false);
|
||||
|
||||
return ToOptimizedResult(info);
|
||||
}
|
||||
|
||||
public void Delete(DeleteShare request)
|
||||
{
|
||||
var task = _sharingManager.DeleteShare(request.Id);
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
|
||||
public object Get(GetShareImage request)
|
||||
{
|
||||
var share = _sharingManager.GetShareInfo(request.Id);
|
||||
|
||||
if (share == null)
|
||||
{
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
if (share.ExpirationDate <= DateTime.UtcNow)
|
||||
{
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
var item = _libraryManager.GetItemById(share.ItemId);
|
||||
|
||||
var image = item.GetImageInfo(ImageType.Primary, 0);
|
||||
|
||||
if (image != null)
|
||||
{
|
||||
return ToStaticFileResult(image.Path);
|
||||
}
|
||||
|
||||
// Grab a dlna icon if nothing else is available
|
||||
using (var response = _dlnaManager.GetIcon("logo240.jpg"))
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
response.Stream.CopyTo(ms);
|
||||
|
||||
ms.Position = 0;
|
||||
var bytes = ms.ToArray();
|
||||
return ResultFactory.GetResult(bytes, "image/" + response.Format.ToString().ToLower());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -252,6 +252,11 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
return (PersonIds ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
public string[] GetItemIds()
|
||||
{
|
||||
return (Ids ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
public VideoType[] GetVideoTypes()
|
||||
{
|
||||
var val = VideoTypes;
|
||||
|
@ -329,11 +334,8 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
|
||||
var result = await GetItemsToSerialize(request, user, parentItem).ConfigureAwait(false);
|
||||
|
||||
var isFiltered = result.Item2;
|
||||
var dtoOptions = GetDtoOptions(request);
|
||||
|
||||
if (isFiltered)
|
||||
{
|
||||
return new ItemsResult
|
||||
{
|
||||
TotalRecordCount = result.Item1.TotalRecordCount,
|
||||
|
@ -341,45 +343,6 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
};
|
||||
}
|
||||
|
||||
var items = result.Item1.Items.Where(i => ApplyAdditionalFilters(request, i, user, false, _libraryManager));
|
||||
|
||||
// Apply filters
|
||||
// Run them starting with the ones that are likely to reduce the list the most
|
||||
foreach (var filter in request.GetFilters().OrderByDescending(f => (int)f))
|
||||
{
|
||||
items = ApplyFilter(items, filter, user, _userDataRepository);
|
||||
}
|
||||
|
||||
items = UserViewBuilder.FilterVirtualEpisodes(items,
|
||||
request.IsMissing,
|
||||
request.IsVirtualUnaired,
|
||||
request.IsUnaired);
|
||||
|
||||
var internalQuery = GetItemsQuery(request, user);
|
||||
|
||||
items = UserViewBuilder.CollapseBoxSetItemsIfNeeded(items, internalQuery, parentItem, user);
|
||||
|
||||
items = _libraryManager.Sort(items, user, request.GetOrderBy(), request.SortOrder ?? SortOrder.Ascending);
|
||||
|
||||
// This must be the last filter
|
||||
if (!string.IsNullOrEmpty(request.AdjacentTo))
|
||||
{
|
||||
items = UserViewBuilder.FilterForAdjacency(items, request.AdjacentTo);
|
||||
}
|
||||
|
||||
var itemsArray = items.ToList();
|
||||
|
||||
var pagedItems = ApplyPaging(request, itemsArray);
|
||||
|
||||
var returnItems = _dtoService.GetBaseItemDtos(pagedItems, dtoOptions, user).ToArray();
|
||||
|
||||
return new ItemsResult
|
||||
{
|
||||
TotalRecordCount = itemsArray.Count,
|
||||
Items = returnItems
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the items to serialize.
|
||||
/// </summary>
|
||||
|
@ -394,23 +357,22 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
parentItem;
|
||||
|
||||
// Default list type = children
|
||||
IEnumerable<BaseItem> items;
|
||||
|
||||
if (!string.IsNullOrEmpty(request.Ids))
|
||||
{
|
||||
var idList = request.Ids.Split(',').ToList();
|
||||
request.Recursive = true;
|
||||
var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
|
||||
|
||||
items = idList.Select(i => _libraryManager.GetItemById(i));
|
||||
return new Tuple<QueryResult<BaseItem>, bool>(result, true);
|
||||
}
|
||||
|
||||
else if (request.Recursive)
|
||||
if (request.Recursive)
|
||||
{
|
||||
var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
|
||||
|
||||
return new Tuple<QueryResult<BaseItem>, bool>(result, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
var result = await ((Folder)item).GetItems(GetItemsQuery(request, null)).ConfigureAwait(false);
|
||||
|
@ -427,12 +389,14 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
return new Tuple<QueryResult<BaseItem>, bool>(result, true);
|
||||
}
|
||||
|
||||
items = ((Folder)item).GetChildren(user, true);
|
||||
}
|
||||
IEnumerable<BaseItem> items = ((Folder)item).GetChildren(user, true);
|
||||
|
||||
var itemsArray = items.ToArray();
|
||||
|
||||
return new Tuple<QueryResult<BaseItem>, bool>(new QueryResult<BaseItem>
|
||||
{
|
||||
Items = items.ToArray()
|
||||
Items = itemsArray,
|
||||
TotalRecordCount = itemsArray.Length
|
||||
|
||||
}, false);
|
||||
}
|
||||
|
@ -450,7 +414,7 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
SortBy = request.GetOrderBy(),
|
||||
SortOrder = request.SortOrder ?? SortOrder.Ascending,
|
||||
|
||||
Filter = i => ApplyAdditionalFilters(request, i, user, true, _libraryManager),
|
||||
Filter = i => ApplyAdditionalFilters(request, i, user, _libraryManager),
|
||||
|
||||
Limit = request.Limit,
|
||||
StartIndex = request.StartIndex,
|
||||
|
@ -490,7 +454,12 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
Years = request.GetYears(),
|
||||
ImageTypes = request.GetImageTypes().ToArray(),
|
||||
VideoTypes = request.GetVideoTypes().ToArray(),
|
||||
AdjacentTo = request.AdjacentTo
|
||||
AdjacentTo = request.AdjacentTo,
|
||||
ItemIds = request.GetItemIds(),
|
||||
MinPlayers = request.MinPlayers,
|
||||
MaxPlayers = request.MaxPlayers,
|
||||
MinCommunityRating = request.MinCommunityRating,
|
||||
MinCriticRating = request.MinCriticRating
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.Ids))
|
||||
|
@ -619,440 +588,8 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
return items;
|
||||
}
|
||||
|
||||
private bool ApplyAdditionalFilters(GetItems request, BaseItem i, User user, bool isPreFiltered, ILibraryManager libraryManager)
|
||||
private bool ApplyAdditionalFilters(GetItems request, BaseItem i, User user, ILibraryManager libraryManager)
|
||||
{
|
||||
var video = i as Video;
|
||||
|
||||
if (!isPreFiltered)
|
||||
{
|
||||
var mediaTypes = request.GetMediaTypes();
|
||||
if (mediaTypes.Length > 0)
|
||||
{
|
||||
if (!(!string.IsNullOrEmpty(i.MediaType) && mediaTypes.Contains(i.MediaType, StringComparer.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (request.IsPlayed.HasValue)
|
||||
{
|
||||
var val = request.IsPlayed.Value;
|
||||
if (i.IsPlayed(user) != val)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Exclude item types
|
||||
var excluteItemTypes = request.GetExcludeItemTypes();
|
||||
if (excluteItemTypes.Length > 0 && excluteItemTypes.Contains(i.GetType().Name, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Include item types
|
||||
var includeItemTypes = request.GetIncludeItemTypes();
|
||||
if (includeItemTypes.Length > 0 && !includeItemTypes.Contains(i.GetType().Name, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (request.IsInBoxSet.HasValue)
|
||||
{
|
||||
var val = request.IsInBoxSet.Value;
|
||||
if (i.Parents.OfType<BoxSet>().Any() != val)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Filter by Video3DFormat
|
||||
if (request.Is3D.HasValue)
|
||||
{
|
||||
var val = request.Is3D.Value;
|
||||
|
||||
if (video == null || val != video.Video3DFormat.HasValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (request.IsHD.HasValue)
|
||||
{
|
||||
var val = request.IsHD.Value;
|
||||
|
||||
if (video == null || val != video.IsHD)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (request.IsUnidentified.HasValue)
|
||||
{
|
||||
var val = request.IsUnidentified.Value;
|
||||
if (i.IsUnidentified != val)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (request.IsLocked.HasValue)
|
||||
{
|
||||
var val = request.IsLocked.Value;
|
||||
if (i.IsLocked != val)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (request.HasOverview.HasValue)
|
||||
{
|
||||
var filterValue = request.HasOverview.Value;
|
||||
|
||||
var hasValue = !string.IsNullOrEmpty(i.Overview);
|
||||
|
||||
if (hasValue != filterValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (request.HasImdbId.HasValue)
|
||||
{
|
||||
var filterValue = request.HasImdbId.Value;
|
||||
|
||||
var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Imdb));
|
||||
|
||||
if (hasValue != filterValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (request.HasTmdbId.HasValue)
|
||||
{
|
||||
var filterValue = request.HasTmdbId.Value;
|
||||
|
||||
var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tmdb));
|
||||
|
||||
if (hasValue != filterValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (request.HasTvdbId.HasValue)
|
||||
{
|
||||
var filterValue = request.HasTvdbId.Value;
|
||||
|
||||
var hasValue = !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tvdb));
|
||||
|
||||
if (hasValue != filterValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (request.IsYearMismatched.HasValue)
|
||||
{
|
||||
var filterValue = request.IsYearMismatched.Value;
|
||||
|
||||
if (UserViewBuilder.IsYearMismatched(i, libraryManager) != filterValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (request.HasOfficialRating.HasValue)
|
||||
{
|
||||
var filterValue = request.HasOfficialRating.Value;
|
||||
|
||||
var hasValue = !string.IsNullOrEmpty(i.OfficialRating);
|
||||
|
||||
if (hasValue != filterValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (request.IsPlaceHolder.HasValue)
|
||||
{
|
||||
var filterValue = request.IsPlaceHolder.Value;
|
||||
|
||||
var isPlaceHolder = false;
|
||||
|
||||
var hasPlaceHolder = i as ISupportsPlaceHolders;
|
||||
|
||||
if (hasPlaceHolder != null)
|
||||
{
|
||||
isPlaceHolder = hasPlaceHolder.IsPlaceHolder;
|
||||
}
|
||||
|
||||
if (isPlaceHolder != filterValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (request.HasSpecialFeature.HasValue)
|
||||
{
|
||||
var filterValue = request.HasSpecialFeature.Value;
|
||||
|
||||
var movie = i as IHasSpecialFeatures;
|
||||
|
||||
if (movie != null)
|
||||
{
|
||||
var ok = filterValue
|
||||
? movie.SpecialFeatureIds.Count > 0
|
||||
: movie.SpecialFeatureIds.Count == 0;
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (request.HasSubtitles.HasValue)
|
||||
{
|
||||
var val = request.HasSubtitles.Value;
|
||||
|
||||
if (video == null || val != video.HasSubtitles)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (request.HasParentalRating.HasValue)
|
||||
{
|
||||
var val = request.HasParentalRating.Value;
|
||||
|
||||
var rating = i.CustomRating;
|
||||
|
||||
if (string.IsNullOrEmpty(rating))
|
||||
{
|
||||
rating = i.OfficialRating;
|
||||
}
|
||||
|
||||
if (val)
|
||||
{
|
||||
if (string.IsNullOrEmpty(rating))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(rating))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (request.HasTrailer.HasValue)
|
||||
{
|
||||
var val = request.HasTrailer.Value;
|
||||
var trailerCount = 0;
|
||||
|
||||
var hasTrailers = i as IHasTrailers;
|
||||
if (hasTrailers != null)
|
||||
{
|
||||
trailerCount = hasTrailers.GetTrailerIds().Count;
|
||||
}
|
||||
|
||||
var ok = val ? trailerCount > 0 : trailerCount == 0;
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (request.HasThemeSong.HasValue)
|
||||
{
|
||||
var filterValue = request.HasThemeSong.Value;
|
||||
|
||||
var themeCount = 0;
|
||||
var iHasThemeMedia = i as IHasThemeMedia;
|
||||
|
||||
if (iHasThemeMedia != null)
|
||||
{
|
||||
themeCount = iHasThemeMedia.ThemeSongIds.Count;
|
||||
}
|
||||
var ok = filterValue ? themeCount > 0 : themeCount == 0;
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (request.HasThemeVideo.HasValue)
|
||||
{
|
||||
var filterValue = request.HasThemeVideo.Value;
|
||||
|
||||
var themeCount = 0;
|
||||
var iHasThemeMedia = i as IHasThemeMedia;
|
||||
|
||||
if (iHasThemeMedia != null)
|
||||
{
|
||||
themeCount = iHasThemeMedia.ThemeVideoIds.Count;
|
||||
}
|
||||
var ok = filterValue ? themeCount > 0 : themeCount == 0;
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply tag filter
|
||||
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))))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply official rating filter
|
||||
var officialRatings = request.GetOfficialRatings();
|
||||
if (officialRatings.Length > 0 && !officialRatings.Contains(i.OfficialRating ?? string.Empty))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Apply genre filter
|
||||
var genres = request.GetGenres();
|
||||
if (genres.Length > 0 && !(genres.Any(v => i.Genres.Contains(v, StringComparer.OrdinalIgnoreCase))))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Filter by VideoType
|
||||
var videoTypes = request.GetVideoTypes();
|
||||
if (videoTypes.Length > 0 && (video == null || !videoTypes.Contains(video.VideoType)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var imageTypes = request.GetImageTypes().ToList();
|
||||
if (imageTypes.Count > 0)
|
||||
{
|
||||
if (!(imageTypes.Any(i.HasImage)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply studio filter
|
||||
var studios = request.GetStudios();
|
||||
if (studios.Length > 0 && !studios.Any(v => i.Studios.Contains(v, StringComparer.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Apply studio filter
|
||||
var studioIds = request.GetStudioIds();
|
||||
if (studioIds.Length > 0 && !studioIds.Any(id =>
|
||||
{
|
||||
var studioItem = libraryManager.GetItemById(id);
|
||||
return studioItem != null && i.Studios.Contains(studioItem.Name, StringComparer.OrdinalIgnoreCase);
|
||||
}))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Apply year filter
|
||||
var years = request.GetYears();
|
||||
if (years.Length > 0 && !(i.ProductionYear.HasValue && years.Contains(i.ProductionYear.Value)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Apply person filter
|
||||
var personIds = request.GetPersonIds();
|
||||
if (personIds.Length > 0)
|
||||
{
|
||||
var names = personIds
|
||||
.Select(libraryManager.GetItemById)
|
||||
.Select(p => p == null ? "-1" : p.Name)
|
||||
.ToList();
|
||||
|
||||
if (!(names.Any(v => libraryManager.GetPeople(i).Select(p => p.Name).Contains(v, StringComparer.OrdinalIgnoreCase))))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply person filter
|
||||
if (!string.IsNullOrEmpty(request.Person))
|
||||
{
|
||||
var personTypes = request.GetPersonTypes();
|
||||
|
||||
if (personTypes.Length == 0)
|
||||
{
|
||||
if (!(libraryManager.GetPeople(i).Any(p => string.Equals(p.Name, request.Person, StringComparison.OrdinalIgnoreCase))))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var types = personTypes;
|
||||
|
||||
var ok = new[] { i }.Any(item =>
|
||||
libraryManager.GetPeople(item).Any(p =>
|
||||
p.Name.Equals(request.Person, StringComparison.OrdinalIgnoreCase) && (types.Contains(p.Type, StringComparer.OrdinalIgnoreCase) || types.Contains(p.Role, StringComparer.OrdinalIgnoreCase))));
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (request.MinCommunityRating.HasValue)
|
||||
{
|
||||
var val = request.MinCommunityRating.Value;
|
||||
|
||||
if (!(i.CommunityRating.HasValue && i.CommunityRating >= val))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (request.MinCriticRating.HasValue)
|
||||
{
|
||||
var val = request.MinCriticRating.Value;
|
||||
|
||||
var hasCriticRating = i as IHasCriticRating;
|
||||
|
||||
if (hasCriticRating != null)
|
||||
{
|
||||
if (!(hasCriticRating.CriticRating.HasValue && hasCriticRating.CriticRating >= val))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Artists
|
||||
if (!string.IsNullOrEmpty(request.ArtistIds))
|
||||
{
|
||||
|
@ -1239,52 +776,6 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
}
|
||||
}
|
||||
|
||||
if (request.MinPlayers.HasValue)
|
||||
{
|
||||
var filterValue = request.MinPlayers.Value;
|
||||
|
||||
var game = i as Game;
|
||||
|
||||
if (game != null)
|
||||
{
|
||||
var players = game.PlayersSupported ?? 1;
|
||||
|
||||
var ok = players >= filterValue;
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (request.MaxPlayers.HasValue)
|
||||
{
|
||||
var filterValue = request.MaxPlayers.Value;
|
||||
|
||||
var game = i as Game;
|
||||
|
||||
if (game != null)
|
||||
{
|
||||
var players = game.PlayersSupported ?? 1;
|
||||
|
||||
var ok = players <= filterValue;
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (request.ParentIndexNumber.HasValue)
|
||||
{
|
||||
var filterValue = request.ParentIndexNumber.Value;
|
||||
|
@ -1347,29 +838,6 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the paging.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <returns>IEnumerable{BaseItem}.</returns>
|
||||
private IEnumerable<BaseItem> ApplyPaging(GetItems request, IEnumerable<BaseItem> items)
|
||||
{
|
||||
// Start at
|
||||
if (request.StartIndex.HasValue)
|
||||
{
|
||||
items = items.Skip(request.StartIndex.Value);
|
||||
}
|
||||
|
||||
// Return limit
|
||||
if (request.Limit.HasValue)
|
||||
{
|
||||
items = items.Take(request.Limit.Value);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -5,7 +5,6 @@ using MediaBrowser.Controller.Net;
|
|||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using ServiceStack;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
|
@ -151,18 +150,16 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
/// <param name="itemsList">The items list.</param>
|
||||
/// <param name="personTypes">The person types.</param>
|
||||
/// <returns>IEnumerable{PersonInfo}.</returns>
|
||||
private IEnumerable<PersonInfo> GetAllPeople(IEnumerable<BaseItem> itemsList, string[] personTypes)
|
||||
private IEnumerable<PersonInfo> GetAllPeople(IEnumerable<BaseItem> itemsList, IEnumerable<string> personTypes)
|
||||
{
|
||||
var people = itemsList.SelectMany(i => LibraryManager.GetPeople(i).OrderBy(p => p.SortOrder ?? int.MaxValue).ThenBy(p => p.Type));
|
||||
var allIds = itemsList.Select(i => i.Id).ToList();
|
||||
|
||||
if (personTypes.Length > 0)
|
||||
var allPeople = LibraryManager.GetPeople(new InternalPeopleQuery
|
||||
{
|
||||
people = people.Where(p =>
|
||||
personTypes.Contains(p.Type ?? string.Empty, StringComparer.OrdinalIgnoreCase) ||
|
||||
personTypes.Contains(p.Role ?? string.Empty, StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
PersonTypes = personTypes.ToList()
|
||||
});
|
||||
|
||||
return people;
|
||||
return allPeople.Where(i => allIds.Contains(i.ItemId)).OrderBy(p => p.SortOrder ?? int.MaxValue).ThenBy(p => p.Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,6 +85,13 @@ namespace MediaBrowser.Common.Implementations.Logging
|
|||
{
|
||||
rule.EnableLoggingForLevel(level);
|
||||
}
|
||||
foreach (var lev in rule.Levels.ToArray())
|
||||
{
|
||||
if (lev < level)
|
||||
{
|
||||
rule.DisableLoggingForLevel(lev);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -195,7 +195,7 @@ namespace MediaBrowser.Common.Implementations.Updates
|
|||
cacheLength = TimeSpan.FromMinutes(3);
|
||||
break;
|
||||
default:
|
||||
cacheLength = TimeSpan.FromHours(3);
|
||||
cacheLength = TimeSpan.FromHours(24);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -235,6 +235,15 @@ namespace MediaBrowser.Controller.Entities
|
|||
}
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public virtual bool EnableAlphaNumericSorting
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is just a helper for convenience
|
||||
/// </summary>
|
||||
|
@ -439,6 +448,11 @@ namespace MediaBrowser.Controller.Entities
|
|||
{
|
||||
if (Name == null) return null; //some items may not have name filled in properly
|
||||
|
||||
if (!EnableAlphaNumericSorting)
|
||||
{
|
||||
return Name.TrimStart();
|
||||
}
|
||||
|
||||
var sortable = Name.Trim().ToLower();
|
||||
sortable = ConfigurationManager.Configuration.SortRemoveCharacters.Aggregate(sortable, (current, search) => current.Replace(search.ToLower(), string.Empty));
|
||||
|
||||
|
@ -464,12 +478,37 @@ namespace MediaBrowser.Controller.Entities
|
|||
return sortable;
|
||||
}
|
||||
|
||||
public Guid ParentId { get; set; }
|
||||
|
||||
private Folder _parent;
|
||||
/// <summary>
|
||||
/// Gets or sets the parent.
|
||||
/// </summary>
|
||||
/// <value>The parent.</value>
|
||||
[IgnoreDataMember]
|
||||
public Folder Parent { get; set; }
|
||||
public Folder Parent
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_parent != null)
|
||||
{
|
||||
return _parent;
|
||||
}
|
||||
|
||||
if (ParentId != Guid.Empty)
|
||||
{
|
||||
return LibraryManager.GetItemById(ParentId) as Folder;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
set { _parent = value; }
|
||||
}
|
||||
|
||||
public void SetParent(Folder parent)
|
||||
{
|
||||
Parent = parent;
|
||||
ParentId = parent == null ? Guid.Empty : parent.Id;
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public IEnumerable<Folder> Parents
|
||||
|
|
|
@ -134,7 +134,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// <exception cref="System.InvalidOperationException">Unable to add + item.Name</exception>
|
||||
public async Task AddChild(BaseItem item, CancellationToken cancellationToken)
|
||||
{
|
||||
item.Parent = this;
|
||||
item.SetParent(this);
|
||||
|
||||
if (item.Id == Guid.Empty)
|
||||
{
|
||||
|
@ -230,7 +230,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
{
|
||||
RemoveChildrenInternal(new[] { item });
|
||||
|
||||
item.Parent = null;
|
||||
item.SetParent(null);
|
||||
|
||||
return ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken);
|
||||
}
|
||||
|
@ -783,11 +783,11 @@ namespace MediaBrowser.Controller.Entities
|
|||
return LibraryManager.GetOrAddByReferenceItem(item);
|
||||
}
|
||||
|
||||
item.Parent = this;
|
||||
item.SetParent(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
child.Parent = this;
|
||||
child.SetParent(this);
|
||||
LibraryManager.RegisterItem(child);
|
||||
item = child;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// <summary>
|
||||
/// Marker interface
|
||||
/// </summary>
|
||||
public interface IItemByName
|
||||
public interface IItemByName : IHasMetadata
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the tagged items.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using MediaBrowser.Model.Entities;
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
|
@ -42,6 +43,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
public string Person { get; set; }
|
||||
public string[] PersonIds { get; set; }
|
||||
public string[] ItemIds { get; set; }
|
||||
public string AdjacentTo { get; set; }
|
||||
public string[] PersonTypes { get; set; }
|
||||
|
||||
|
@ -83,8 +85,15 @@ namespace MediaBrowser.Controller.Entities
|
|||
public bool? IsSports { get; set; }
|
||||
public bool? IsKids { get; set; }
|
||||
|
||||
public int? MinPlayers { get; set; }
|
||||
public int? MaxPlayers { get; set; }
|
||||
public double? MinCriticRating { get; set; }
|
||||
public double? MinCommunityRating { get; set; }
|
||||
|
||||
public string[] ChannelIds { get; set; }
|
||||
|
||||
internal List<Guid> ItemIdsFromPersonFilters { get; set; }
|
||||
|
||||
public InternalItemsQuery()
|
||||
{
|
||||
Tags = new string[] { };
|
||||
|
@ -102,6 +111,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
PersonTypes = new string[] { };
|
||||
PersonIds = new string[] { };
|
||||
ChannelIds = new string[] { };
|
||||
ItemIds = new string[] { };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
21
MediaBrowser.Controller/Entities/InternalPeopleQuery.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
public class InternalPeopleQuery
|
||||
{
|
||||
public Guid ItemId { get; set; }
|
||||
public List<string> PersonTypes { get; set; }
|
||||
public List<string> ExcludePersonTypes { get; set; }
|
||||
public int? MaxListOrder { get; set; }
|
||||
public Guid AppearsInItemId { get; set; }
|
||||
public string NameContains { get; set; }
|
||||
|
||||
public InternalPeopleQuery()
|
||||
{
|
||||
PersonTypes = new List<string>();
|
||||
ExcludePersonTypes = new List<string>();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -55,6 +55,15 @@ namespace MediaBrowser.Controller.Entities
|
|||
return true;
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public override bool EnableAlphaNumericSorting
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is owned item.
|
||||
/// </summary>
|
||||
|
@ -70,7 +79,12 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
|
||||
{
|
||||
return inputItems.Where(GetItemFilter());
|
||||
var itemsWithPerson = LibraryManager.GetItemIds(new InternalItemsQuery
|
||||
{
|
||||
Person = Name
|
||||
});
|
||||
|
||||
return inputItems.Where(i => itemsWithPerson.Contains(i.Id));
|
||||
}
|
||||
|
||||
|
||||
|
@ -94,6 +108,8 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// </summary>
|
||||
public class PersonInfo
|
||||
{
|
||||
public Guid ItemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
|
|
|
@ -1116,6 +1116,11 @@ namespace MediaBrowser.Controller.Entities
|
|||
return false;
|
||||
}
|
||||
|
||||
if (request.ItemIds.Length > 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (request.Studios.Length > 0)
|
||||
{
|
||||
return false;
|
||||
|
@ -1146,6 +1151,26 @@ namespace MediaBrowser.Controller.Entities
|
|||
return false;
|
||||
}
|
||||
|
||||
if (request.MinPlayers.HasValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (request.MaxPlayers.HasValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (request.MinCommunityRating.HasValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (request.MinCriticRating.HasValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1304,6 +1329,41 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
public static bool Filter(BaseItem item, User user, InternalItemsQuery query, IUserDataManager userDataManager, ILibraryManager libraryManager)
|
||||
{
|
||||
if (query.ItemIdsFromPersonFilters == null)
|
||||
{
|
||||
if (query.PersonIds.Length > 0)
|
||||
{
|
||||
var names = query.PersonIds
|
||||
.Select(libraryManager.GetItemById)
|
||||
.Select(i => i == null ? null : i.Name)
|
||||
.Where(i => !string.IsNullOrWhiteSpace(i))
|
||||
.ToList();
|
||||
|
||||
var itemIdList = new List<Guid>();
|
||||
foreach (var name in names)
|
||||
{
|
||||
itemIdList.AddRange(libraryManager.GetItemIds(new InternalItemsQuery
|
||||
{
|
||||
Person = name
|
||||
}));
|
||||
}
|
||||
query.ItemIdsFromPersonFilters = itemIdList;
|
||||
}
|
||||
|
||||
// Apply person filter
|
||||
else if (!string.IsNullOrWhiteSpace(query.Person))
|
||||
{
|
||||
var itemIdList = new List<Guid>();
|
||||
|
||||
itemIdList.AddRange(libraryManager.GetItemIds(new InternalItemsQuery
|
||||
{
|
||||
Person = query.Person,
|
||||
PersonTypes = query.PersonTypes
|
||||
}));
|
||||
query.ItemIdsFromPersonFilters = itemIdList;
|
||||
}
|
||||
}
|
||||
|
||||
if (query.MediaTypes.Length > 0 && !query.MediaTypes.Contains(item.MediaType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
|
@ -1691,47 +1751,22 @@ namespace MediaBrowser.Controller.Entities
|
|||
return false;
|
||||
}
|
||||
|
||||
// Apply person filter
|
||||
if (query.PersonIds.Length > 0)
|
||||
if (query.ItemIds.Length > 0)
|
||||
{
|
||||
var names = query.PersonIds
|
||||
.Select(libraryManager.GetItemById)
|
||||
.Select(i => i == null ? "-1" : i.Name)
|
||||
.ToList();
|
||||
|
||||
if (!(names.Any(
|
||||
v => libraryManager.GetPeople(item).Select(i => i.Name).Contains(v, StringComparer.OrdinalIgnoreCase))))
|
||||
if (!query.ItemIds.Contains(item.Id.ToString("N"), StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply person filter
|
||||
if (!string.IsNullOrWhiteSpace(query.Person))
|
||||
if (query.ItemIdsFromPersonFilters != null)
|
||||
{
|
||||
var personTypes = query.PersonTypes;
|
||||
|
||||
if (personTypes.Length == 0)
|
||||
{
|
||||
if (!(libraryManager.GetPeople(item).Any(p => string.Equals(p.Name, query.Person, StringComparison.OrdinalIgnoreCase))))
|
||||
if (!query.ItemIdsFromPersonFilters.Contains(item.Id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var types = personTypes;
|
||||
|
||||
var ok = new[] { item }.Any(i =>
|
||||
libraryManager.GetPeople(i).Any(p =>
|
||||
string.Equals(p.Name, query.Person, StringComparison.OrdinalIgnoreCase) && (types.Contains(p.Type ?? string.Empty, StringComparer.OrdinalIgnoreCase) || types.Contains(p.Role ?? string.Empty, StringComparer.OrdinalIgnoreCase))));
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply tag filter
|
||||
var tags = query.Tags;
|
||||
|
@ -1748,6 +1783,81 @@ namespace MediaBrowser.Controller.Entities
|
|||
}
|
||||
}
|
||||
|
||||
if (query.MinPlayers.HasValue)
|
||||
{
|
||||
var filterValue = query.MinPlayers.Value;
|
||||
|
||||
var game = item as Game;
|
||||
|
||||
if (game != null)
|
||||
{
|
||||
var players = game.PlayersSupported ?? 1;
|
||||
|
||||
var ok = players >= filterValue;
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (query.MaxPlayers.HasValue)
|
||||
{
|
||||
var filterValue = query.MaxPlayers.Value;
|
||||
|
||||
var game = item as Game;
|
||||
|
||||
if (game != null)
|
||||
{
|
||||
var players = game.PlayersSupported ?? 1;
|
||||
|
||||
var ok = players <= filterValue;
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (query.MinCommunityRating.HasValue)
|
||||
{
|
||||
var val = query.MinCommunityRating.Value;
|
||||
|
||||
if (!(item.CommunityRating.HasValue && item.CommunityRating >= val))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (query.MinCriticRating.HasValue)
|
||||
{
|
||||
var val = query.MinCriticRating.Value;
|
||||
|
||||
var hasCriticRating = item as IHasCriticRating;
|
||||
|
||||
if (hasCriticRating != null)
|
||||
{
|
||||
if (!(hasCriticRating.CriticRating.HasValue && hasCriticRating.CriticRating >= val))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -420,6 +420,20 @@ namespace MediaBrowser.Controller.Library
|
|||
/// <returns>List<PersonInfo>.</returns>
|
||||
List<PersonInfo> GetPeople(BaseItem item);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the people.
|
||||
/// </summary>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <returns>List<PersonInfo>.</returns>
|
||||
List<PersonInfo> GetPeople(InternalPeopleQuery query);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the people items.
|
||||
/// </summary>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <returns>List<Person>.</returns>
|
||||
List<Person> GetPeopleItems(InternalPeopleQuery query);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all people names.
|
||||
/// </summary>
|
||||
|
@ -433,5 +447,19 @@ namespace MediaBrowser.Controller.Library
|
|||
/// <param name="people">The people.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task UpdatePeople(BaseItem item, List<PersonInfo> people);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item ids.
|
||||
/// </summary>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <returns>List<Guid>.</returns>
|
||||
List<Guid> GetItemIds(InternalItemsQuery query);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the people names.
|
||||
/// </summary>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <returns>List<System.String>.</returns>
|
||||
List<string> GetPeopleNames(InternalPeopleQuery query);
|
||||
}
|
||||
}
|
|
@ -166,6 +166,7 @@
|
|||
<Compile Include="Entities\ImageSourceInfo.cs" />
|
||||
<Compile Include="Entities\IMetadataContainer.cs" />
|
||||
<Compile Include="Entities\InternalItemsQuery.cs" />
|
||||
<Compile Include="Entities\InternalPeopleQuery.cs" />
|
||||
<Compile Include="Entities\ISupportsBoxSetGrouping.cs" />
|
||||
<Compile Include="Entities\ISupportsPlaceHolders.cs" />
|
||||
<Compile Include="Entities\ItemImageInfo.cs" />
|
||||
|
@ -329,6 +330,7 @@
|
|||
<Compile Include="Security\IAuthenticationRepository.cs" />
|
||||
<Compile Include="Security\IEncryptionManager.cs" />
|
||||
<Compile Include="Session\AuthenticationRequest.cs" />
|
||||
<Compile Include="Social\ISharingManager.cs" />
|
||||
<Compile Include="Subtitles\ISubtitleManager.cs" />
|
||||
<Compile Include="Subtitles\ISubtitleProvider.cs" />
|
||||
<Compile Include="Providers\ItemIdentifier.cs" />
|
||||
|
|
|
@ -151,9 +151,9 @@ namespace MediaBrowser.Controller.Persistence
|
|||
/// <summary>
|
||||
/// Gets the people.
|
||||
/// </summary>
|
||||
/// <param name="itemId">The item identifier.</param>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <returns>List<PersonInfo>.</returns>
|
||||
List<PersonInfo> GetPeople(Guid itemId);
|
||||
List<PersonInfo> GetPeople(InternalPeopleQuery query);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the people.
|
||||
|
@ -162,6 +162,13 @@ namespace MediaBrowser.Controller.Persistence
|
|||
/// <param name="people">The people.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task UpdatePeople(Guid itemId, List<PersonInfo> people);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the people names.
|
||||
/// </summary>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <returns>List<System.String>.</returns>
|
||||
List<string> GetPeopleNames(InternalPeopleQuery query);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
28
MediaBrowser.Controller/Social/ISharingManager.cs
Normal file
|
@ -0,0 +1,28 @@
|
|||
using MediaBrowser.Model.Social;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Controller.Social
|
||||
{
|
||||
public interface ISharingManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates the share.
|
||||
/// </summary>
|
||||
/// <param name="itemId">The item identifier.</param>
|
||||
/// <param name="userId">The user identifier.</param>
|
||||
/// <returns>Task<SocialShareInfo>.</returns>
|
||||
Task<SocialShareInfo> CreateShare(string itemId, string userId);
|
||||
/// <summary>
|
||||
/// Gets the share information.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier.</param>
|
||||
/// <returns>SocialShareInfo.</returns>
|
||||
SocialShareInfo GetShareInfo(string id);
|
||||
/// <summary>
|
||||
/// Deletes the share.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task DeleteShare(string id);
|
||||
}
|
||||
}
|
|
@ -49,4 +49,18 @@ namespace MediaBrowser.Controller.Sync
|
|||
/// <returns>Task<QueryResult<FileMetadata>>.</returns>
|
||||
Task<QueryResult<FileMetadata>> GetFiles(FileQuery query, SyncTarget target, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public interface ISupportsDirectCopy
|
||||
{
|
||||
/// <summary>
|
||||
/// Sends the file.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="pathParts">The path parts.</param>
|
||||
/// <param name="target">The target.</param>
|
||||
/// <param name="progress">The progress.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task<SyncedFileInfo>.</returns>
|
||||
Task<SyncedFileInfo> SendFile(string path, string[] pathParts, SyncTarget target, IProgress<double> progress, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -410,18 +410,11 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
|||
{
|
||||
if (stubType.Value == StubType.People)
|
||||
{
|
||||
var items = _libraryManager.GetPeople(item).Select(i =>
|
||||
var items = _libraryManager.GetPeopleItems(new InternalPeopleQuery
|
||||
{
|
||||
try
|
||||
{
|
||||
return _libraryManager.GetPerson(i.Name);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ItemId = item.Id
|
||||
|
||||
}).Where(i => i != null).ToArray();
|
||||
}).ToArray();
|
||||
|
||||
var result = new QueryResult<ServerItem>
|
||||
{
|
||||
|
@ -443,7 +436,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
|||
var person = item as Person;
|
||||
if (person != null)
|
||||
{
|
||||
return await GetItemsFromPerson(person, user, startIndex, limit).ConfigureAwait(false);
|
||||
return GetItemsFromPerson(person, user, startIndex, limit);
|
||||
}
|
||||
|
||||
return ApplyPaging(new QueryResult<ServerItem>(), startIndex, limit);
|
||||
|
@ -486,38 +479,19 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
|||
};
|
||||
}
|
||||
|
||||
private async Task<QueryResult<ServerItem>> GetItemsFromPerson(Person person, User user, int? startIndex, int? limit)
|
||||
private QueryResult<ServerItem> GetItemsFromPerson(Person person, User user, int? startIndex, int? limit)
|
||||
{
|
||||
var items = user.RootFolder.GetRecursiveChildren(user, i => i is Movie || i is Series && PeopleHelper.ContainsPerson(_libraryManager.GetPeople(i), person.Name))
|
||||
var itemsWithPerson = _libraryManager.GetItems(new InternalItemsQuery
|
||||
{
|
||||
Person = person.Name
|
||||
|
||||
}).Items;
|
||||
|
||||
var items = itemsWithPerson
|
||||
.Where(i => i is Movie || i is Series || i is IChannelItem)
|
||||
.Where(i => i.IsVisibleStandalone(user))
|
||||
.ToList();
|
||||
|
||||
var trailerResult = await _channelManager.GetAllMediaInternal(new AllChannelMediaQuery
|
||||
{
|
||||
ContentTypes = new[] { ChannelMediaContentType.MovieExtra },
|
||||
ExtraTypes = new[] { ExtraType.Trailer },
|
||||
UserId = user.Id.ToString("N")
|
||||
|
||||
}, CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
var currentIds = items.Select(i => i.GetProviderId(MetadataProviders.Imdb))
|
||||
.ToList();
|
||||
|
||||
var trailersToAdd = trailerResult.Items
|
||||
.Where(i => PeopleHelper.ContainsPerson(_libraryManager.GetPeople(i), person.Name))
|
||||
.Where(i =>
|
||||
{
|
||||
// Try to filter out dupes using imdb id
|
||||
var imdb = i.GetProviderId(MetadataProviders.Imdb);
|
||||
if (!string.IsNullOrWhiteSpace(imdb) &&
|
||||
currentIds.Contains(imdb, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
items.AddRange(trailersToAdd);
|
||||
|
||||
items = _libraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending)
|
||||
.Skip(startIndex ?? 0)
|
||||
.Take(limit ?? int.MaxValue)
|
||||
|
@ -569,7 +543,11 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
|||
|
||||
private bool EnablePeopleDisplay(BaseItem item)
|
||||
{
|
||||
if (_libraryManager.GetPeople(item).Count > 0)
|
||||
if (_libraryManager.GetPeopleNames(new InternalPeopleQuery
|
||||
{
|
||||
ItemId = item.Id
|
||||
|
||||
}).Count > 0)
|
||||
{
|
||||
return item is Movie;
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 840 B |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 8.0 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 699 B |
|
@ -834,7 +834,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||
{
|
||||
var heightParam = request.Height.Value.ToString(UsCulture);
|
||||
|
||||
filters.Add(string.Format("scale=trunc(oh*a*2)/2:{0}", heightParam));
|
||||
filters.Add(string.Format("scale=trunc(oh*a/2)*2:{0}", heightParam));
|
||||
}
|
||||
|
||||
// If a max width was requested
|
||||
|
@ -850,7 +850,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||
{
|
||||
var maxHeightParam = request.MaxHeight.Value.ToString(UsCulture);
|
||||
|
||||
filters.Add(string.Format("scale=trunc(oh*a*2)/2:min(ih\\,{0})", maxHeightParam));
|
||||
filters.Add(string.Format("scale=trunc(oh*a/2)*2:min(ih\\,{0})", maxHeightParam));
|
||||
}
|
||||
|
||||
var output = string.Empty;
|
||||
|
|
|
@ -439,7 +439,7 @@ namespace MediaBrowser.MediaEncoding.Probing
|
|||
|
||||
if (!string.IsNullOrWhiteSpace(artists))
|
||||
{
|
||||
audio.Artists = artists.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
audio.Artists = SplitArtists(artists, new[] { '/' }, false)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
}
|
||||
|
@ -452,7 +452,7 @@ namespace MediaBrowser.MediaEncoding.Probing
|
|||
}
|
||||
else
|
||||
{
|
||||
audio.Artists = SplitArtists(artist)
|
||||
audio.Artists = SplitArtists(artist, _nameDelimiters, true)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
}
|
||||
|
@ -474,7 +474,7 @@ namespace MediaBrowser.MediaEncoding.Probing
|
|||
}
|
||||
else
|
||||
{
|
||||
audio.AlbumArtists = SplitArtists(albumArtist)
|
||||
audio.AlbumArtists = SplitArtists(albumArtist, _nameDelimiters, true)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
|
@ -552,10 +552,13 @@ namespace MediaBrowser.MediaEncoding.Probing
|
|||
|
||||
private const string ArtistReplaceValue = " | ";
|
||||
|
||||
private IEnumerable<string> SplitArtists(string val)
|
||||
private IEnumerable<string> SplitArtists(string val, char[] delimiters, bool splitFeaturing)
|
||||
{
|
||||
if (splitFeaturing)
|
||||
{
|
||||
val = val.Replace(" featuring ", ArtistReplaceValue, StringComparison.OrdinalIgnoreCase)
|
||||
.Replace(" feat. ", ArtistReplaceValue, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
var artistsFound = new List<string>();
|
||||
|
||||
|
@ -570,11 +573,7 @@ namespace MediaBrowser.MediaEncoding.Probing
|
|||
}
|
||||
}
|
||||
|
||||
// Only use the comma as a delimeter if there are no slashes or pipes.
|
||||
// We want to be careful not to split names that have commas in them
|
||||
var delimeter = _nameDelimiters;
|
||||
|
||||
var artists = val.Split(delimeter, StringSplitOptions.RemoveEmptyEntries)
|
||||
var artists = val.Split(delimiters, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Where(i => !string.IsNullOrWhiteSpace(i))
|
||||
.Select(i => i.Trim());
|
||||
|
||||
|
|
|
@ -1076,6 +1076,9 @@
|
|||
<Compile Include="..\MediaBrowser.Model\Session\UserDataChangeInfo.cs">
|
||||
<Link>Session\UserDataChangeInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Social\SocialShareInfo.cs">
|
||||
<Link>Social\SocialShareInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Sync\CompleteSyncJobInfo.cs">
|
||||
<Link>Sync\CompleteSyncJobInfo.cs</Link>
|
||||
</Compile>
|
||||
|
|
|
@ -1032,6 +1032,9 @@
|
|||
<Compile Include="..\MediaBrowser.Model\Session\UserDataChangeInfo.cs">
|
||||
<Link>Session\UserDataChangeInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\mediabrowser.model\social\SocialShareInfo.cs">
|
||||
<Link>Social\SocialShareInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\MediaBrowser.Model\Sync\CompleteSyncJobInfo.cs">
|
||||
<Link>Sync\CompleteSyncJobInfo.cs</Link>
|
||||
</Compile>
|
||||
|
|
|
@ -211,6 +211,8 @@ namespace MediaBrowser.Model.Configuration
|
|||
|
||||
public AutoOnOff EnableLibraryMonitor { get; set; }
|
||||
|
||||
public int SharingExpirationDays { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
|
||||
/// </summary>
|
||||
|
@ -231,6 +233,7 @@ namespace MediaBrowser.Model.Configuration
|
|||
|
||||
EnableUPnP = true;
|
||||
|
||||
SharingExpirationDays = 30;
|
||||
MinResumePct = 5;
|
||||
MaxResumePct = 90;
|
||||
|
||||
|
|
|
@ -148,6 +148,7 @@ namespace MediaBrowser.Model.Dlna
|
|||
{
|
||||
if (!conditionProcessor.IsAudioConditionSatisfied(c, audioChannels, audioBitrate))
|
||||
{
|
||||
LogConditionFailure(options.Profile, "AudioCodecProfile", c, item);
|
||||
all = false;
|
||||
break;
|
||||
}
|
||||
|
@ -277,11 +278,18 @@ namespace MediaBrowser.Model.Dlna
|
|||
|
||||
// The profile describes what the device supports
|
||||
// If device requirements are satisfied then allow both direct stream and direct play
|
||||
if (item.SupportsDirectPlay && IsAudioEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options)))
|
||||
if (item.SupportsDirectPlay &&
|
||||
IsAudioEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options)))
|
||||
{
|
||||
playMethods.Add(PlayMethod.DirectPlay);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Debug("Profile: {0}, No direct play profiles found for Path: {1}",
|
||||
options.Profile.Name ?? "Unknown Profile",
|
||||
item.Path ?? "Unknown path");
|
||||
}
|
||||
|
||||
return playMethods;
|
||||
}
|
||||
|
@ -354,8 +362,8 @@ namespace MediaBrowser.Model.Dlna
|
|||
MediaStream videoStream = item.VideoStream;
|
||||
|
||||
// TODO: This doesn't accout for situation of device being able to handle media bitrate, but wifi connection not fast enough
|
||||
bool isEligibleForDirectPlay = IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options), subtitleStream, options);
|
||||
bool isEligibleForDirectStream = IsEligibleForDirectPlay(item, options.GetMaxBitrate(), subtitleStream, options);
|
||||
bool isEligibleForDirectPlay = IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options), subtitleStream, options, PlayMethod.DirectPlay);
|
||||
bool isEligibleForDirectStream = IsEligibleForDirectPlay(item, options.GetMaxBitrate(), subtitleStream, options, PlayMethod.DirectStream);
|
||||
|
||||
_logger.Debug("Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}",
|
||||
options.Profile.Name ?? "Unknown Profile",
|
||||
|
@ -375,7 +383,7 @@ namespace MediaBrowser.Model.Dlna
|
|||
|
||||
if (subtitleStream != null)
|
||||
{
|
||||
SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile.SubtitleProfiles, options.Context);
|
||||
SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile.SubtitleProfiles, options.Context, directPlay.Value);
|
||||
|
||||
playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method;
|
||||
playlistItem.SubtitleFormat = subtitleProfile.Format;
|
||||
|
@ -405,7 +413,7 @@ namespace MediaBrowser.Model.Dlna
|
|||
|
||||
if (subtitleStream != null)
|
||||
{
|
||||
SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile.SubtitleProfiles, options.Context);
|
||||
SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile.SubtitleProfiles, options.Context, PlayMethod.Transcode);
|
||||
|
||||
playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method;
|
||||
playlistItem.SubtitleFormat = subtitleProfile.Format;
|
||||
|
@ -698,14 +706,16 @@ namespace MediaBrowser.Model.Dlna
|
|||
private bool IsEligibleForDirectPlay(MediaSourceInfo item,
|
||||
int? maxBitrate,
|
||||
MediaStream subtitleStream,
|
||||
VideoOptions options)
|
||||
VideoOptions options,
|
||||
PlayMethod playMethod)
|
||||
{
|
||||
if (subtitleStream != null)
|
||||
{
|
||||
SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile.SubtitleProfiles, options.Context);
|
||||
SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile.SubtitleProfiles, options.Context, playMethod);
|
||||
|
||||
if (subtitleProfile.Method != SubtitleDeliveryMethod.External && subtitleProfile.Method != SubtitleDeliveryMethod.Embed)
|
||||
{
|
||||
_logger.Debug("Not eligible for {0} due to unsupported subtitles", playMethod);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -713,8 +723,25 @@ namespace MediaBrowser.Model.Dlna
|
|||
return IsAudioEligibleForDirectPlay(item, maxBitrate);
|
||||
}
|
||||
|
||||
public static SubtitleProfile GetSubtitleProfile(MediaStream subtitleStream, SubtitleProfile[] subtitleProfiles, EncodingContext context)
|
||||
public static SubtitleProfile GetSubtitleProfile(MediaStream subtitleStream, SubtitleProfile[] subtitleProfiles, EncodingContext context, PlayMethod playMethod)
|
||||
{
|
||||
if (playMethod != PlayMethod.Transcode)
|
||||
{
|
||||
// Look for supported embedded subs
|
||||
foreach (SubtitleProfile profile in subtitleProfiles)
|
||||
{
|
||||
if (!profile.SupportsLanguage(subtitleStream.Language))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (profile.Method == SubtitleDeliveryMethod.Embed && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format))
|
||||
{
|
||||
return profile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look for an external profile that matches the stream type (text/graphical)
|
||||
foreach (SubtitleProfile profile in subtitleProfiles)
|
||||
{
|
||||
|
@ -745,10 +772,9 @@ namespace MediaBrowser.Model.Dlna
|
|||
}
|
||||
}
|
||||
|
||||
// Look for supported embedded subs that we can just mux into the output
|
||||
foreach (SubtitleProfile profile in subtitleProfiles)
|
||||
{
|
||||
bool requiresConversion = !StringHelper.EqualsIgnoreCase(subtitleStream.Codec, profile.Format);
|
||||
|
||||
if (!profile.SupportsLanguage(subtitleStream.Language))
|
||||
{
|
||||
continue;
|
||||
|
@ -756,11 +782,6 @@ namespace MediaBrowser.Model.Dlna
|
|||
|
||||
if (profile.Method == SubtitleDeliveryMethod.Embed && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format))
|
||||
{
|
||||
if (!requiresConversion)
|
||||
{
|
||||
return profile;
|
||||
}
|
||||
|
||||
return profile;
|
||||
}
|
||||
}
|
||||
|
@ -774,8 +795,13 @@ namespace MediaBrowser.Model.Dlna
|
|||
|
||||
private bool IsAudioEligibleForDirectPlay(MediaSourceInfo item, int? maxBitrate)
|
||||
{
|
||||
// Honor the max bitrate setting
|
||||
return !maxBitrate.HasValue || (item.Bitrate.HasValue && item.Bitrate.Value <= maxBitrate.Value);
|
||||
if (!maxBitrate.HasValue || (item.Bitrate.HasValue && item.Bitrate.Value <= maxBitrate.Value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
_logger.Debug("Bitrate exceeds DirectPlay limit");
|
||||
return false;
|
||||
}
|
||||
|
||||
private void ValidateInput(VideoOptions options)
|
||||
|
|
|
@ -329,7 +329,7 @@ namespace MediaBrowser.Model.Dlna
|
|||
|
||||
private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles)
|
||||
{
|
||||
SubtitleProfile subtitleProfile = StreamBuilder.GetSubtitleProfile(stream, subtitleProfiles, Context);
|
||||
SubtitleProfile subtitleProfile = StreamBuilder.GetSubtitleProfile(stream, subtitleProfiles, Context, PlayMethod);
|
||||
SubtitleStreamInfo info = new SubtitleStreamInfo
|
||||
{
|
||||
IsForced = stream.IsForced,
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Extensions;
|
||||
using System.Diagnostics;
|
||||
|
||||
|
|
|
@ -379,6 +379,7 @@
|
|||
<Compile Include="Session\TranscodingInfo.cs" />
|
||||
<Compile Include="Session\UserDataChangeInfo.cs" />
|
||||
<Compile Include="Devices\ContentUploadHistory.cs" />
|
||||
<Compile Include="Social\SocialShareInfo.cs" />
|
||||
<Compile Include="Sync\CompleteSyncJobInfo.cs" />
|
||||
<Compile Include="Sync\DeviceFileInfo.cs" />
|
||||
<Compile Include="Sync\ItemFIleInfo.cs" />
|
||||
|
|
16
MediaBrowser.Model/Social/SocialShareInfo.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
|
||||
namespace MediaBrowser.Model.Social
|
||||
{
|
||||
public class SocialShareInfo
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Url { get; set; }
|
||||
public string ItemId { get; set; }
|
||||
public string UserId { get; set; }
|
||||
public DateTime ExpirationDate { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string ImageUrl { get; set; }
|
||||
public string Overview { get; set; }
|
||||
}
|
||||
}
|
|
@ -330,12 +330,11 @@ namespace MediaBrowser.Providers.Manager
|
|||
|
||||
protected async Task SaveItem(MetadataResult<TItemType> result, ItemUpdateType reason, CancellationToken cancellationToken)
|
||||
{
|
||||
await result.Item.UpdateToRepository(reason, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (result.Item.SupportsPeople)
|
||||
{
|
||||
await LibraryManager.UpdatePeople(result.Item as BaseItem, result.People);
|
||||
}
|
||||
await result.Item.UpdateToRepository(reason, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public bool CanRefresh(IHasMetadata item)
|
||||
|
|
|
@ -485,12 +485,11 @@ namespace MediaBrowser.Providers.Manager
|
|||
// Give it a dummy path just so that it looks like a file system item
|
||||
var dummy = new T()
|
||||
{
|
||||
Path = Path.Combine(_appPaths.InternalMetadataPath, "dummy"),
|
||||
|
||||
// Dummy this up to fool the local trailer check
|
||||
Parent = new Folder()
|
||||
Path = Path.Combine(_appPaths.InternalMetadataPath, "dummy")
|
||||
};
|
||||
|
||||
dummy.SetParent(new Folder());
|
||||
|
||||
var options = GetMetadataOptions(dummy);
|
||||
|
||||
var summary = new MetadataPluginSummary
|
||||
|
@ -727,12 +726,11 @@ namespace MediaBrowser.Providers.Manager
|
|||
// Give it a dummy path just so that it looks like a file system item
|
||||
var dummy = new TItemType
|
||||
{
|
||||
Path = Path.Combine(_appPaths.InternalMetadataPath, "dummy"),
|
||||
|
||||
// Dummy this up to fool the local trailer check
|
||||
Parent = new Folder()
|
||||
Path = Path.Combine(_appPaths.InternalMetadataPath, "dummy")
|
||||
};
|
||||
|
||||
dummy.SetParent(new Folder());
|
||||
|
||||
var options = GetMetadataOptions(dummy);
|
||||
|
||||
var providers = GetMetadataProvidersInternal<TItemType>(dummy, options, searchInfo.IncludeDisabledProviders)
|
||||
|
|
|
@ -138,7 +138,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||
if (item.IsShortcut)
|
||||
{
|
||||
FetchShortcutInfo(item);
|
||||
return Task.FromResult(ItemUpdateType.MetadataEdit);
|
||||
return Task.FromResult(ItemUpdateType.MetadataImport);
|
||||
}
|
||||
|
||||
var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem, _config, _subtitleManager, _chapterManager, _libraryManager);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using MediaBrowser.Common.Net;
|
||||
using System.Globalization;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Channels;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
|
@ -191,7 +192,20 @@ namespace MediaBrowser.Providers.Movies
|
|||
var tmdbId = item.GetProviderId(MetadataProviders.Tmdb);
|
||||
var language = item.GetPreferredMetadataLanguage();
|
||||
|
||||
if (string.IsNullOrEmpty(tmdbId))
|
||||
if (string.IsNullOrWhiteSpace(tmdbId))
|
||||
{
|
||||
var imdbId = item.GetProviderId(MetadataProviders.Imdb);
|
||||
if (!string.IsNullOrWhiteSpace(imdbId))
|
||||
{
|
||||
var movieInfo = await MovieDbProvider.Current.FetchMainResult(imdbId, false, language, cancellationToken).ConfigureAwait(false);
|
||||
if (movieInfo != null)
|
||||
{
|
||||
tmdbId = movieInfo.id.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(tmdbId))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Channels;
|
||||
using MediaBrowser.Model.Entities;
|
||||
|
@ -34,6 +35,13 @@ namespace MediaBrowser.Providers.Movies
|
|||
return true;
|
||||
}
|
||||
|
||||
// Supports images for tv movies
|
||||
var tvProgram = item as LiveTvProgram;
|
||||
if (tvProgram != null && tvProgram.IsMovie)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return item is Movie || item is MusicVideo;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -172,7 +172,7 @@ namespace MediaBrowser.Providers.Omdb
|
|||
result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
|
||||
result.HasMetadata = true;
|
||||
|
||||
await new OmdbProvider(_jsonSerializer, _httpClient).Fetch(result.Item, imdbId, cancellationToken).ConfigureAwait(false);
|
||||
await new OmdbProvider(_jsonSerializer, _httpClient).Fetch(result.Item, imdbId, info.MetadataLanguage, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -211,7 +211,7 @@ namespace MediaBrowser.Providers.Omdb
|
|||
result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
|
||||
result.HasMetadata = true;
|
||||
|
||||
await new OmdbProvider(_jsonSerializer, _httpClient).Fetch(result.Item, imdbId, cancellationToken).ConfigureAwait(false);
|
||||
await new OmdbProvider(_jsonSerializer, _httpClient).Fetch(result.Item, imdbId, info.MetadataLanguage, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace MediaBrowser.Providers.Omdb
|
|||
Current = this;
|
||||
}
|
||||
|
||||
public async Task Fetch(BaseItem item, string imdbId, CancellationToken cancellationToken)
|
||||
public async Task Fetch(BaseItem item, string imdbId, string language, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(imdbId))
|
||||
{
|
||||
|
@ -51,7 +51,11 @@ namespace MediaBrowser.Providers.Omdb
|
|||
{
|
||||
var result = _jsonSerializer.DeserializeFromStream<RootObject>(stream);
|
||||
|
||||
// Only take the name if the user's language is set to english, since Omdb has no localization
|
||||
if (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
item.Name = result.Title;
|
||||
}
|
||||
|
||||
int year;
|
||||
|
||||
|
|
|
@ -59,9 +59,13 @@ namespace MediaBrowser.Providers.People
|
|||
// Avoid implicitly captured closure
|
||||
var itemName = item.Name;
|
||||
|
||||
var seriesWithPerson = _libraryManager.RootFolder
|
||||
.GetRecursiveChildren(i => i is Series && !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tvdb)) && _libraryManager.GetPeople(i).Any(p => string.Equals(p.Name, itemName, StringComparison.OrdinalIgnoreCase)))
|
||||
.Cast<Series>()
|
||||
var seriesWithPerson = _libraryManager.GetItems(new InternalItemsQuery
|
||||
{
|
||||
IncludeItemTypes = new[] { typeof(Series).Name },
|
||||
Person = itemName
|
||||
|
||||
}).Items.Cast<Series>()
|
||||
.Where(i => !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tvdb)))
|
||||
.ToList();
|
||||
|
||||
var infos = seriesWithPerson.Select(i => GetImageFromSeriesData(i, item.Name, cancellationToken))
|
||||
|
|
|
@ -111,10 +111,11 @@ namespace MediaBrowser.Providers.TV
|
|||
{
|
||||
Name = seasonName,
|
||||
IndexNumber = seasonNumber,
|
||||
Parent = series,
|
||||
Id = (series.Id + (seasonNumber ?? -1).ToString(_usCulture) + seasonName).GetMBId(typeof(Season))
|
||||
};
|
||||
|
||||
season.SetParent(series);
|
||||
|
||||
await series.AddChild(season, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
await season.RefreshMetadata(new MetadataRefreshOptions(), cancellationToken).ConfigureAwait(false);
|
||||
|
|
|
@ -406,10 +406,11 @@ namespace MediaBrowser.Providers.TV
|
|||
Name = name,
|
||||
IndexNumber = episodeNumber,
|
||||
ParentIndexNumber = seasonNumber,
|
||||
Parent = season,
|
||||
Id = (series.Id + seasonNumber.ToString(_usCulture) + name).GetMBId(typeof(Episode))
|
||||
};
|
||||
|
||||
episode.SetParent(season);
|
||||
|
||||
await season.AddChild(episode, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
await episode.RefreshMetadata(new MetadataRefreshOptions
|
||||
|
|
|
@ -7,6 +7,7 @@ using MediaBrowser.Controller.Providers;
|
|||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Providers.Manager;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -30,8 +31,15 @@ namespace MediaBrowser.Providers.TV
|
|||
{
|
||||
var provider = new DummySeasonProvider(ServerConfigurationManager, Logger, _localization, LibraryManager);
|
||||
|
||||
try
|
||||
{
|
||||
await provider.Run(item, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorException("Error in DummySeasonProvider", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool IsFullLocalMetadata(Series item)
|
||||
|
|
|
@ -75,7 +75,6 @@ namespace MediaBrowser.Server.Implementations.Collections
|
|||
var collection = new BoxSet
|
||||
{
|
||||
Name = name,
|
||||
Parent = parentFolder,
|
||||
Path = path,
|
||||
IsLocked = options.IsLocked,
|
||||
ProviderIds = options.ProviderIds,
|
||||
|
|
|
@ -88,7 +88,7 @@ namespace MediaBrowser.Server.Implementations.Dto
|
|||
public IEnumerable<BaseItemDto> GetBaseItemDtos(IEnumerable<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null)
|
||||
{
|
||||
var syncJobItems = GetSyncedItemProgress(options);
|
||||
var syncDictionary = syncJobItems.ToDictionary(i => i.ItemId);
|
||||
var syncDictionary = GetSyncedItemProgressDictionary(syncJobItems);
|
||||
|
||||
var list = new List<BaseItemDto>();
|
||||
|
||||
|
@ -120,11 +120,23 @@ namespace MediaBrowser.Server.Implementations.Dto
|
|||
return list;
|
||||
}
|
||||
|
||||
private Dictionary<string, SyncedItemProgress> GetSyncedItemProgressDictionary(IEnumerable<SyncedItemProgress> items)
|
||||
{
|
||||
var dict = new Dictionary<string, SyncedItemProgress>();
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
dict[item.ItemId] = item;
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
public BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null)
|
||||
{
|
||||
var syncProgress = GetSyncedItemProgress(options);
|
||||
|
||||
var dto = GetBaseItemDtoInternal(item, options, syncProgress.ToDictionary(i => i.ItemId), user, owner);
|
||||
var dto = GetBaseItemDtoInternal(item, options, GetSyncedItemProgressDictionary(syncProgress), user, owner);
|
||||
|
||||
var byName = item as IItemByName;
|
||||
|
||||
|
@ -132,22 +144,43 @@ namespace MediaBrowser.Server.Implementations.Dto
|
|||
{
|
||||
if (options.Fields.Contains(ItemFields.ItemCounts))
|
||||
{
|
||||
SetItemByNameInfo(item, dto, GetTaggedItems(byName, user), user);
|
||||
}
|
||||
|
||||
FillSyncInfo(dto, item, options, user, syncProgress);
|
||||
return dto;
|
||||
}
|
||||
|
||||
FillSyncInfo(dto, item, options, user, syncProgress);
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
private List<BaseItem> GetTaggedItems(IItemByName byName, User user)
|
||||
{
|
||||
var person = byName as Person;
|
||||
|
||||
if (person != null)
|
||||
{
|
||||
var items = _libraryManager.GetItems(new InternalItemsQuery
|
||||
{
|
||||
Person = byName.Name
|
||||
|
||||
}).Items;
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
return items.Where(i => i.IsVisibleStandalone(user)).ToList();
|
||||
}
|
||||
|
||||
return items.ToList();
|
||||
}
|
||||
|
||||
var itemFilter = byName.GetItemFilter();
|
||||
|
||||
var libraryItems = user != null ?
|
||||
user.RootFolder.GetRecursiveChildren(user, itemFilter) :
|
||||
_libraryManager.RootFolder.GetRecursiveChildren(itemFilter);
|
||||
|
||||
SetItemByNameInfo(item, dto, libraryItems.ToList(), user);
|
||||
}
|
||||
|
||||
FillSyncInfo(dto, item, options, user, syncProgress);
|
||||
return dto;
|
||||
}
|
||||
|
||||
FillSyncInfo(dto, item, options, user, syncProgress);
|
||||
|
||||
return dto;
|
||||
return user != null ?
|
||||
user.RootFolder.GetRecursiveChildren(user, itemFilter).ToList() :
|
||||
_libraryManager.RootFolder.GetRecursiveChildren(itemFilter).ToList();
|
||||
}
|
||||
|
||||
private SyncedItemProgress[] GetSyncedItemProgress(DtoOptions options)
|
||||
|
@ -361,7 +394,7 @@ namespace MediaBrowser.Server.Implementations.Dto
|
|||
{
|
||||
var syncProgress = GetSyncedItemProgress(options);
|
||||
|
||||
var dto = GetBaseItemDtoInternal(item, options, syncProgress.ToDictionary(i => i.ItemId), user);
|
||||
var dto = GetBaseItemDtoInternal(item, options, GetSyncedItemProgressDictionary(syncProgress), user);
|
||||
|
||||
if (options.Fields.Contains(ItemFields.ItemCounts))
|
||||
{
|
||||
|
@ -684,7 +717,7 @@ namespace MediaBrowser.Server.Implementations.Dto
|
|||
}
|
||||
|
||||
}).Where(i => i != null)
|
||||
.DistinctBy(i => i.Name)
|
||||
.DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
|
||||
.ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
for (var i = 0; i < people.Count; i++)
|
||||
|
@ -736,6 +769,7 @@ namespace MediaBrowser.Server.Implementations.Dto
|
|||
}
|
||||
})
|
||||
.Where(i => i != null)
|
||||
.DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
|
||||
.ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
for (var i = 0; i < studios.Count; i++)
|
||||
|
|
|
@ -256,7 +256,15 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
|
|||
NotificationType = type
|
||||
};
|
||||
|
||||
if (e.Item != null)
|
||||
{
|
||||
notification.Variables["ItemName"] = GetItemName(e.Item);
|
||||
}
|
||||
else
|
||||
{
|
||||
notification.Variables["ItemName"] = item.Name;
|
||||
}
|
||||
|
||||
notification.Variables["UserName"] = user == null ? "Unknown user" : user.Name;
|
||||
notification.Variables["AppName"] = e.ClientName;
|
||||
notification.Variables["DeviceName"] = e.DeviceName;
|
||||
|
|
|
@ -104,7 +104,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
container.Adapter = _containerAdapter;
|
||||
|
||||
Plugins.Add(new SwaggerFeature());
|
||||
Plugins.Add(new CorsFeature(allowedHeaders: "Content-Type, Authorization, Range, X-MediaBrowser-Token"));
|
||||
Plugins.Add(new CorsFeature(allowedHeaders: "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization"));
|
||||
|
||||
//Plugins.Add(new AuthFeature(() => new AuthUserSession(), new IAuthProvider[] {
|
||||
// new SessionAuthProvider(_containerAdapter.Resolve<ISessionContext>()),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using MediaBrowser.Model.Logging;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace MediaBrowser.Server.Implementations.HttpServer
|
||||
|
@ -23,7 +23,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
|
||||
//log.AppendLine("Headers: " + string.Join(",", response.Headers.AllKeys.Select(k => k + "=" + response.Headers[k])));
|
||||
|
||||
var responseTime = string.Format(". Response time: {0} ms.", duration.TotalMilliseconds);
|
||||
var responseTime = string.Format(". Response time: {0} ms.", duration.TotalMilliseconds.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
var msg = "HTTP Response " + statusCode + " to " + endPoint + responseTime;
|
||||
|
||||
|
|
|
@ -170,7 +170,12 @@ namespace MediaBrowser.Server.Implementations.HttpServer.Security
|
|||
/// <returns>Dictionary{System.StringSystem.String}.</returns>
|
||||
private Dictionary<string, string> GetAuthorizationDictionary(IServiceRequest httpReq)
|
||||
{
|
||||
var auth = httpReq.Headers["Authorization"];
|
||||
var auth = httpReq.Headers["X-Emby-Authorization"];
|
||||
|
||||
if (string.IsNullOrWhiteSpace(auth))
|
||||
{
|
||||
auth = httpReq.Headers["Authorization"];
|
||||
}
|
||||
|
||||
return GetAuthorization(auth);
|
||||
}
|
||||
|
|
|
@ -71,6 +71,12 @@ namespace MediaBrowser.Server.Implementations.Intros
|
|||
|
||||
var candidates = new List<ItemWithTrailer>();
|
||||
|
||||
var itemPeople = _libraryManager.GetPeople(item);
|
||||
var allPeople = _libraryManager.GetPeople(new InternalPeopleQuery
|
||||
{
|
||||
AppearsInItemId = item.Id
|
||||
});
|
||||
|
||||
if (config.EnableIntrosFromMoviesInLibrary)
|
||||
{
|
||||
var itemsWithTrailers = user.RootFolder
|
||||
|
@ -94,6 +100,8 @@ namespace MediaBrowser.Server.Implementations.Intros
|
|||
Type = ItemWithTrailerType.ItemWithTrailer,
|
||||
User = user,
|
||||
WatchingItem = item,
|
||||
WatchingItemPeople = itemPeople,
|
||||
AllPeople = allPeople,
|
||||
Random = random,
|
||||
LibraryManager = _libraryManager
|
||||
}));
|
||||
|
@ -135,6 +143,8 @@ namespace MediaBrowser.Server.Implementations.Intros
|
|||
Type = ItemWithTrailerType.ChannelTrailer,
|
||||
User = user,
|
||||
WatchingItem = item,
|
||||
WatchingItemPeople = itemPeople,
|
||||
AllPeople = allPeople,
|
||||
Random = random,
|
||||
LibraryManager = _libraryManager
|
||||
}));
|
||||
|
@ -241,7 +251,7 @@ namespace MediaBrowser.Server.Implementations.Intros
|
|||
return true;
|
||||
}
|
||||
|
||||
internal static int GetSimiliarityScore(BaseItem item1, BaseItem item2, Random random, ILibraryManager libraryManager)
|
||||
internal static int GetSimiliarityScore(BaseItem item1, List<PersonInfo> item1People, List<PersonInfo> allPeople, BaseItem item2, Random random, ILibraryManager libraryManager)
|
||||
{
|
||||
var points = 0;
|
||||
|
||||
|
@ -262,11 +272,13 @@ namespace MediaBrowser.Server.Implementations.Intros
|
|||
// Find common studios
|
||||
points += item1.Studios.Where(i => item2.Studios.Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 5);
|
||||
|
||||
var item2PeopleNames = libraryManager.GetPeople(item2).Select(i => i.Name)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
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 += libraryManager.GetPeople(item1).Where(i => item2PeopleNames.ContainsKey(i.Name)).Sum(i =>
|
||||
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))
|
||||
{
|
||||
|
@ -341,6 +353,8 @@ namespace MediaBrowser.Server.Implementations.Intros
|
|||
internal ItemWithTrailerType Type;
|
||||
internal User User;
|
||||
internal BaseItem WatchingItem;
|
||||
internal List<PersonInfo> WatchingItemPeople;
|
||||
internal List<PersonInfo> AllPeople;
|
||||
internal Random Random;
|
||||
internal ILibraryManager LibraryManager;
|
||||
|
||||
|
@ -364,7 +378,7 @@ namespace MediaBrowser.Server.Implementations.Intros
|
|||
{
|
||||
if (!_score.HasValue)
|
||||
{
|
||||
_score = GetSimiliarityScore(WatchingItem, Item, Random, LibraryManager);
|
||||
_score = GetSimiliarityScore(WatchingItem, WatchingItemPeople, AllPeople, Item, Random, LibraryManager);
|
||||
}
|
||||
return _score.Value;
|
||||
}
|
||||
|
|
|
@ -345,7 +345,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
|
||||
try
|
||||
{
|
||||
await UpdateItem(season, ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false);
|
||||
await UpdateItem(season, ItemUpdateType.MetadataDownload, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -1108,6 +1108,8 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
progress.Report(innerPercent);
|
||||
});
|
||||
|
||||
_logger.Debug("Running post-scan task {0}", task.GetType().Name);
|
||||
|
||||
try
|
||||
{
|
||||
await task.Run(innerProgress, cancellationToken).ConfigureAwait(false);
|
||||
|
@ -1223,6 +1225,11 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
};
|
||||
}
|
||||
|
||||
public List<Guid> GetItemIds(InternalItemsQuery query)
|
||||
{
|
||||
return ItemRepository.GetItemIdsList(query);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the intros.
|
||||
/// </summary>
|
||||
|
@ -2057,30 +2064,58 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
}
|
||||
}
|
||||
|
||||
public List<PersonInfo> GetPeople(InternalPeopleQuery query)
|
||||
{
|
||||
return ItemRepository.GetPeople(query);
|
||||
}
|
||||
|
||||
public List<PersonInfo> GetPeople(BaseItem item)
|
||||
{
|
||||
return item.People ?? ItemRepository.GetPeople(item.Id);
|
||||
var people = GetPeople(new InternalPeopleQuery
|
||||
{
|
||||
ItemId = item.Id
|
||||
});
|
||||
|
||||
if (people.Count > 0)
|
||||
{
|
||||
return people;
|
||||
}
|
||||
|
||||
return item.People ?? new List<PersonInfo>();
|
||||
}
|
||||
|
||||
public List<Person> GetPeopleItems(InternalPeopleQuery query)
|
||||
{
|
||||
return ItemRepository.GetPeopleNames(query).Select(i =>
|
||||
{
|
||||
try
|
||||
{
|
||||
return GetPerson(i);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("Error getting person", ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
}).Where(i => i != null).ToList();
|
||||
}
|
||||
|
||||
public List<string> GetPeopleNames(InternalPeopleQuery query)
|
||||
{
|
||||
return ItemRepository.GetPeopleNames(query);
|
||||
}
|
||||
|
||||
public List<PersonInfo> GetAllPeople()
|
||||
{
|
||||
return RootFolder.GetRecursiveChildren()
|
||||
.SelectMany(GetPeople)
|
||||
.Where(i => !string.IsNullOrWhiteSpace(i.Name))
|
||||
return GetPeople(new InternalPeopleQuery())
|
||||
.DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public async Task UpdatePeople(BaseItem item, List<PersonInfo> people)
|
||||
public Task UpdatePeople(BaseItem item, List<PersonInfo> people)
|
||||
{
|
||||
await ItemRepository.UpdatePeople(item.Id, people).ConfigureAwait(false);
|
||||
|
||||
if (item.People != null)
|
||||
{
|
||||
item.People = null;
|
||||
await item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
return ItemRepository.UpdatePeople(item.Id, people);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
// If the resolver didn't specify this
|
||||
if (parent != null)
|
||||
{
|
||||
item.Parent = parent;
|
||||
item.SetParent(parent);
|
||||
}
|
||||
|
||||
item.Id = libraryManager.GetNewItemId(item.Path, item.GetType());
|
||||
|
@ -68,7 +68,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
// If the resolver didn't specify this
|
||||
if (args.Parent != null)
|
||||
{
|
||||
item.Parent = args.Parent;
|
||||
item.SetParent(args.Parent);
|
||||
}
|
||||
|
||||
item.Id = libraryManager.GetNewItemId(item.Path, item.GetType());
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Naming.Common;
|
||||
using MediaBrowser.Naming.Video;
|
||||
using MediaBrowser.Server.Implementations.Logging;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Server.Implementations.Logging;
|
||||
|
||||
namespace MediaBrowser.Server.Implementations.Library.Resolvers
|
||||
{
|
||||
|
|
|
@ -256,10 +256,15 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
|
||||
if (query.IncludePeople)
|
||||
{
|
||||
var itemIds = items.Select(i => i.Id).ToList();
|
||||
|
||||
// Find persons
|
||||
var persons = items.SelectMany(i => _libraryManager.GetPeople(i))
|
||||
var persons = _libraryManager.GetPeople(new InternalPeopleQuery
|
||||
{
|
||||
NameContains = searchTerm
|
||||
})
|
||||
.Where(i => itemIds.Contains(i.ItemId))
|
||||
.Select(i => i.Name)
|
||||
.Where(i => !string.IsNullOrWhiteSpace(i))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
|
|
|
@ -811,5 +811,10 @@
|
|||
"ErrorMessagePasswordNotMatchConfirm": "The password and password confirmation must match.",
|
||||
"ErrorMessageUsernameInUse": "The username is already in use. Please choose a new name and try again.",
|
||||
"ErrorMessageEmailInUse": "The email address is already in use. Please enter a new email address and try again, or use the forgot password feature.",
|
||||
"MessageThankYouForConnectSignUp": "Thank you for signing up for Emby Connect. An email will be sent to your address with instructions on how to confirm your new account. Please confirm the account and then return here to sign in."
|
||||
"MessageThankYouForConnectSignUp": "Thank you for signing up for Emby Connect. An email will be sent to your address with instructions on how to confirm your new account. Please confirm the account and then return here to sign in.",
|
||||
"HeaderShare": "Share",
|
||||
"ButtonShareHelp": "Share a web page containing media information with social media. Media files are never shared publicly.",
|
||||
"ButtonShare": "Share",
|
||||
"HeaderConfirm": "Confirm",
|
||||
"ButtonAdvancedRefresh": "Advanced Refresh"
|
||||
}
|
||||
|
|