#514 - Support HLS seeking
This commit is contained in:
parent
970504abdf
commit
3bef6ead9c
|
@ -159,7 +159,7 @@ namespace MediaBrowser.Api
|
|||
return libraryManager.GetPerson(DeSlugPersonName(name, libraryManager));
|
||||
}
|
||||
|
||||
protected IList<BaseItem> GetAllLibraryItems(Guid? userId, IUserManager userManager, ILibraryManager libraryManager, string parentId = null)
|
||||
protected IEnumerable<BaseItem> GetAllLibraryItems(Guid? userId, IUserManager userManager, ILibraryManager libraryManager, string parentId = null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(parentId))
|
||||
{
|
||||
|
@ -169,7 +169,7 @@ namespace MediaBrowser.Api
|
|||
{
|
||||
var user = userManager.GetUserById(userId.Value);
|
||||
|
||||
return folder.GetRecursiveChildren(user).ToList();
|
||||
return folder.GetRecursiveChildren(user);
|
||||
}
|
||||
|
||||
return folder.GetRecursiveChildren();
|
||||
|
@ -178,7 +178,7 @@ namespace MediaBrowser.Api
|
|||
{
|
||||
var user = userManager.GetUserById(userId.Value);
|
||||
|
||||
return userManager.GetUserById(userId.Value).RootFolder.GetRecursiveChildren(user, null);
|
||||
return userManager.GetUserById(userId.Value).RootFolder.GetRecursiveChildren(user);
|
||||
}
|
||||
|
||||
return libraryManager.RootFolder.GetRecursiveChildren();
|
||||
|
@ -239,7 +239,8 @@ namespace MediaBrowser.Api
|
|||
return name;
|
||||
}
|
||||
|
||||
return libraryManager.RootFolder.GetRecursiveChildren(i => i is Game)
|
||||
return libraryManager.RootFolder.GetRecursiveChildren()
|
||||
.OfType<Game>()
|
||||
.SelectMany(i => i.Genres)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.FirstOrDefault(i =>
|
||||
|
|
|
@ -381,7 +381,7 @@ namespace MediaBrowser.Api.DefaultTheme
|
|||
var currentUser1 = user;
|
||||
|
||||
var ownedEpisodes = series
|
||||
.SelectMany(i => i.GetRecursiveChildren(currentUser1, j => j.LocationType != LocationType.Virtual))
|
||||
.SelectMany(i => i.GetRecursiveChildren(currentUser1).Where(j => j.LocationType != LocationType.Virtual))
|
||||
.OfType<Episode>()
|
||||
.ToList();
|
||||
|
||||
|
|
|
@ -59,10 +59,11 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
/// Processes the request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="isLive">if set to <c>true</c> [is live].</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
protected object ProcessRequest(StreamRequest request)
|
||||
protected object ProcessRequest(StreamRequest request, bool isLive)
|
||||
{
|
||||
return ProcessRequestAsync(request).Result;
|
||||
return ProcessRequestAsync(request, isLive).Result;
|
||||
}
|
||||
|
||||
private static readonly SemaphoreSlim FfmpegStartLock = new SemaphoreSlim(1, 1);
|
||||
|
@ -70,13 +71,12 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
/// Processes the request async.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="isLive">if set to <c>true</c> [is live].</param>
|
||||
/// <returns>Task{System.Object}.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// A video bitrate is required
|
||||
/// <exception cref="ArgumentException">A video bitrate is required
|
||||
/// or
|
||||
/// An audio bitrate is required
|
||||
/// </exception>
|
||||
private async Task<object> ProcessRequestAsync(StreamRequest request)
|
||||
/// An audio bitrate is required</exception>
|
||||
private async Task<object> ProcessRequestAsync(StreamRequest request, bool isLive)
|
||||
{
|
||||
var cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
|
@ -110,7 +110,8 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
throw;
|
||||
}
|
||||
|
||||
await WaitForMinimumSegmentCount(playlist, GetSegmentWait(), cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
var waitCount = isLive ? 1 : GetSegmentWait();
|
||||
await WaitForMinimumSegmentCount(playlist, waitCount, cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
@ -119,6 +120,22 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
}
|
||||
}
|
||||
|
||||
if (isLive)
|
||||
{
|
||||
//var file = request.PlaylistId + Path.GetExtension(Request.PathInfo);
|
||||
|
||||
//file = Path.Combine(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, file);
|
||||
|
||||
try
|
||||
{
|
||||
return ResultFactory.GetStaticFileResult(Request, playlist, FileShare.ReadWrite);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ApiEntryPoint.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls);
|
||||
}
|
||||
}
|
||||
|
||||
var audioBitrate = state.OutputAudioBitrate ?? 0;
|
||||
var videoBitrate = state.OutputVideoBitrate ?? 0;
|
||||
|
||||
|
@ -188,16 +205,18 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
|
||||
protected async Task WaitForMinimumSegmentCount(string playlist, int segmentCount, CancellationToken cancellationToken)
|
||||
{
|
||||
var count = 0;
|
||||
Logger.Debug("Waiting for {0} segments in {1}", segmentCount, playlist);
|
||||
|
||||
// Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written
|
||||
using (var fileStream = FileSystem.GetFileStream(playlist, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
|
||||
while (true)
|
||||
{
|
||||
using (var reader = new StreamReader(fileStream))
|
||||
// Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written
|
||||
using (var fileStream = FileSystem.GetFileStream(playlist, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
|
||||
{
|
||||
while (true)
|
||||
using (var reader = new StreamReader(fileStream))
|
||||
{
|
||||
if (!reader.EndOfStream)
|
||||
var count = 0;
|
||||
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
var line = await reader.ReadLineAsync().ConfigureAwait(false);
|
||||
|
||||
|
@ -206,11 +225,12 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
count++;
|
||||
if (count >= segmentCount)
|
||||
{
|
||||
Logger.Debug("Finished waiting for {0} segments in {1}", segmentCount, playlist);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
await Task.Delay(25, cancellationToken).ConfigureAwait(false);
|
||||
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -229,7 +249,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
|
||||
var itsOffsetMs = hlsVideoRequest == null
|
||||
? 0
|
||||
: ((GetHlsVideoStream)state.VideoRequest).TimeStampOffsetMs;
|
||||
: hlsVideoRequest.TimeStampOffsetMs;
|
||||
|
||||
var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds.ToString(UsCulture));
|
||||
|
||||
|
@ -240,7 +260,15 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
// If isEncoding is true we're actually starting ffmpeg
|
||||
var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0";
|
||||
|
||||
var args = string.Format("{0} {1} -i {2} -map_metadata -1 -threads {3} {4} {5} -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9} -y \"{10}\"",
|
||||
var baseUrlParam = string.Empty;
|
||||
|
||||
if (state.Request is GetLiveHlsStream)
|
||||
{
|
||||
baseUrlParam = string.Format(" -hls_base_url \"{0}/\"",
|
||||
"hls/" + Path.GetFileNameWithoutExtension(outputPath));
|
||||
}
|
||||
|
||||
var args = string.Format("{0} {1} -i {2} -map_metadata -1 -threads {3} {4} {5} -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9}{10} -y \"{11}\"",
|
||||
itsOffset,
|
||||
inputModifier,
|
||||
GetInputArgument(state),
|
||||
|
@ -251,6 +279,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
state.SegmentLength.ToString(UsCulture),
|
||||
startNumberParam,
|
||||
state.HlsListSize.ToString(UsCulture),
|
||||
baseUrlParam,
|
||||
outputPath
|
||||
).Trim();
|
||||
|
||||
|
|
|
@ -22,11 +22,6 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
[Api(Description = "Gets a video stream using HTTP live streaming.")]
|
||||
public class GetMasterHlsVideoStream : VideoStreamRequest
|
||||
{
|
||||
[ApiMember(Name = "BaselineStreamAudioBitRate", Description = "Optional. Specify the audio bitrate for the baseline stream.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public int? BaselineStreamAudioBitRate { get; set; }
|
||||
|
||||
[ApiMember(Name = "AppendBaselineStream", Description = "Optional. Whether or not to include a baseline audio-only stream in the master playlist.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||
public bool AppendBaselineStream { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Videos/{Id}/main.m3u8", "GET")]
|
||||
|
@ -35,12 +30,6 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
{
|
||||
}
|
||||
|
||||
[Route("/Videos/{Id}/baseline.m3u8", "GET")]
|
||||
[Api(Description = "Gets a video stream using HTTP live streaming.")]
|
||||
public class GetBaselineHlsVideoStream : VideoStreamRequest
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetHlsVideoSegment
|
||||
/// </summary>
|
||||
|
@ -73,16 +62,11 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
|
||||
public object Get(GetDynamicHlsVideoSegment request)
|
||||
{
|
||||
if (string.Equals("baseline", request.PlaylistId, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return GetDynamicSegment(request, false).Result;
|
||||
}
|
||||
|
||||
return GetDynamicSegment(request, true).Result;
|
||||
return GetDynamicSegment(request).Result;
|
||||
}
|
||||
|
||||
private static readonly SemaphoreSlim FfmpegStartLock = new SemaphoreSlim(1, 1);
|
||||
private async Task<object> GetDynamicSegment(GetDynamicHlsVideoSegment request, bool isMain)
|
||||
private async Task<object> GetDynamicSegment(GetDynamicHlsVideoSegment request)
|
||||
{
|
||||
if ((request.StartTimeTicks ?? 0) > 0)
|
||||
{
|
||||
|
@ -322,7 +306,9 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex);
|
||||
|
||||
// Main stream
|
||||
var playlistUrl = "main.m3u8" + queryString;
|
||||
var playlistUrl = (state.RunTimeTicks ?? 0) > 0 ? "main.m3u8" : "live.m3u8";
|
||||
playlistUrl += queryString;
|
||||
|
||||
AppendPlaylist(builder, playlistUrl, totalBitrate);
|
||||
|
||||
if (state.VideoRequest.VideoBitRate.HasValue)
|
||||
|
@ -385,13 +371,6 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
return result;
|
||||
}
|
||||
|
||||
public object Get(GetBaselineHlsVideoStream request)
|
||||
{
|
||||
var result = GetPlaylistAsync(request, "baseline").Result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<object> GetPlaylistAsync(VideoStreamRequest request, string name)
|
||||
{
|
||||
var state = await GetState(request, CancellationToken.None).ConfigureAwait(false);
|
||||
|
@ -506,14 +485,6 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
/// <returns>System.String.</returns>
|
||||
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
|
||||
{
|
||||
var hlsVideoRequest = state.VideoRequest as GetHlsVideoStream;
|
||||
|
||||
var itsOffsetMs = hlsVideoRequest == null
|
||||
? 0
|
||||
: ((GetHlsVideoStream)state.VideoRequest).TimeStampOffsetMs;
|
||||
|
||||
var itsOffset = itsOffsetMs == 0 ? string.Empty : string.Format("-itsoffset {0} ", TimeSpan.FromMilliseconds(itsOffsetMs).TotalSeconds.ToString(UsCulture));
|
||||
|
||||
var threads = GetNumberOfThreads(state, false);
|
||||
|
||||
var inputModifier = GetInputModifier(state);
|
||||
|
@ -521,8 +492,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
// If isEncoding is true we're actually starting ffmpeg
|
||||
var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0";
|
||||
|
||||
var args = string.Format("{0} {1} -i {2} -map_metadata -1 -threads {3} {4} {5} -flags -global_header {6} -hls_time {7} -start_number {8} -hls_list_size {9} -y \"{10}\"",
|
||||
itsOffset,
|
||||
var args = string.Format("{0} -i {1} -map_metadata -1 -threads {2} {3} {4} -flags -global_header {5} -hls_time {6} -start_number {7} -hls_list_size {8} -y \"{9}\"",
|
||||
inputModifier,
|
||||
GetInputArgument(state),
|
||||
threads,
|
||||
|
|
|
@ -32,6 +32,12 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
public int TimeStampOffsetMs { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Videos/{Id}/live.m3u8", "GET")]
|
||||
[Api(Description = "Gets a video stream using HTTP live streaming.")]
|
||||
public class GetLiveHlsStream : VideoStreamRequest
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetHlsVideoSegment
|
||||
/// </summary>
|
||||
|
@ -105,7 +111,12 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetHlsVideoStream request)
|
||||
{
|
||||
return ProcessRequest(request);
|
||||
return ProcessRequest(request, false);
|
||||
}
|
||||
|
||||
public object Get(GetLiveHlsStream request)
|
||||
{
|
||||
return ProcessRequest(request, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -192,14 +192,14 @@ namespace MediaBrowser.Api
|
|||
{
|
||||
result.Series = season.Series.Name;
|
||||
|
||||
result.EpisodeCount = season.GetRecursiveChildren(i => i is Episode).Count;
|
||||
result.EpisodeCount = season.GetRecursiveChildren().Count(i => i is Episode);
|
||||
}
|
||||
|
||||
var series = item as Series;
|
||||
|
||||
if (series != null)
|
||||
{
|
||||
result.EpisodeCount = series.GetRecursiveChildren(i => i is Episode).Count;
|
||||
result.EpisodeCount = series.GetRecursiveChildren().Count(i => i is Episode);
|
||||
}
|
||||
|
||||
var album = item as MusicAlbum;
|
||||
|
|
|
@ -78,8 +78,8 @@ namespace MediaBrowser.Api
|
|||
var fields = request.GetItemFields().ToList();
|
||||
|
||||
var inputItems = user == null
|
||||
? libraryManager.RootFolder.GetRecursiveChildren(i => i.Id != item.Id)
|
||||
: user.RootFolder.GetRecursiveChildren(user, i => i.Id != item.Id);
|
||||
? libraryManager.RootFolder.GetRecursiveChildren().Where(i => i.Id != item.Id)
|
||||
: user.RootFolder.GetRecursiveChildren(user).Where(i => i.Id != item.Id);
|
||||
|
||||
var items = GetSimilaritems(item, inputItems.Where(includeInSearch), getSimilarityScore)
|
||||
.ToList();
|
||||
|
|
|
@ -541,7 +541,8 @@ namespace MediaBrowser.Api.UserLibrary
|
|||
if (series != null)
|
||||
{
|
||||
var dtos = series
|
||||
.GetRecursiveChildren(i => i is Episode && i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0)
|
||||
.GetRecursiveChildren()
|
||||
.Where(i => i is Episode && i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0)
|
||||
.OrderBy(i =>
|
||||
{
|
||||
if (i.PremiereDate.HasValue)
|
||||
|
|
|
@ -856,19 +856,6 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// <returns>IEnumerable{BaseItem}.</returns>
|
||||
/// <exception cref="System.ArgumentNullException"></exception>
|
||||
public IEnumerable<BaseItem> GetRecursiveChildren(User user, bool includeLinkedChildren = true)
|
||||
{
|
||||
return GetRecursiveChildren(user, null, includeLinkedChildren);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the recursive children.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="filter">The filter.</param>
|
||||
/// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param>
|
||||
/// <returns>IList{BaseItem}.</returns>
|
||||
/// <exception cref="System.ArgumentNullException"></exception>
|
||||
public IList<BaseItem> GetRecursiveChildren(User user, Func<BaseItem, bool> filter, bool includeLinkedChildren = true)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
|
@ -877,7 +864,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
var list = new List<BaseItem>();
|
||||
|
||||
var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, true, filter);
|
||||
var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, true, null);
|
||||
|
||||
return hasLinkedChildren ? list.DistinctBy(i => i.Id).ToList() : list;
|
||||
}
|
||||
|
@ -887,20 +874,10 @@ namespace MediaBrowser.Controller.Entities
|
|||
/// </summary>
|
||||
/// <returns>IList{BaseItem}.</returns>
|
||||
public IList<BaseItem> GetRecursiveChildren()
|
||||
{
|
||||
return GetRecursiveChildren(i => true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the recursive children.
|
||||
/// </summary>
|
||||
/// <param name="filter">The filter.</param>
|
||||
/// <returns>IEnumerable{BaseItem}.</returns>
|
||||
public IList<BaseItem> GetRecursiveChildren(Func<BaseItem, bool> filter)
|
||||
{
|
||||
var list = new List<BaseItem>();
|
||||
|
||||
AddChildrenToList(list, true, filter);
|
||||
AddChildrenToList(list, true, null);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
|
|
@ -18,11 +18,17 @@ namespace MediaBrowser.Controller.Entities
|
|||
switch (ViewType)
|
||||
{
|
||||
case CollectionType.Games:
|
||||
return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)).OfType<GameSystem>();
|
||||
return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren))
|
||||
.OfType<GameSystem>();
|
||||
case CollectionType.BoxSets:
|
||||
return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)).OfType<BoxSet>();
|
||||
return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren))
|
||||
.OfType<BoxSet>();
|
||||
case CollectionType.TvShows:
|
||||
return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)).OfType<Series>();
|
||||
return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren))
|
||||
.OfType<Series>();
|
||||
case CollectionType.Trailers:
|
||||
return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren))
|
||||
.OfType<Trailer>();
|
||||
default:
|
||||
return mediaFolders.SelectMany(i => i.GetChildren(user, includeLinkedChildren));
|
||||
}
|
||||
|
|
|
@ -217,6 +217,7 @@ namespace MediaBrowser.Model.ApiClient
|
|||
/// Gets the additional parts.
|
||||
/// </summary>
|
||||
/// <param name="itemId">The item identifier.</param>
|
||||
/// <param name="userId">The user identifier.</param>
|
||||
/// <returns>Task{BaseItemDto[]}.</returns>
|
||||
Task<ItemsResult> GetAdditionalParts(string itemId, string userId);
|
||||
|
||||
|
@ -241,6 +242,12 @@ namespace MediaBrowser.Model.ApiClient
|
|||
/// <returns>Task{SessionInfoDto[]}.</returns>
|
||||
Task<SessionInfoDto[]> GetClientSessionsAsync(SessionQuery query);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the client session asynchronous.
|
||||
/// </summary>
|
||||
/// <returns>Task{SessionInfoDto}.</returns>
|
||||
Task<SessionInfoDto> GetCurrentSessionAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item counts async.
|
||||
/// </summary>
|
||||
|
|
|
@ -93,8 +93,6 @@ namespace MediaBrowser.Model.Configuration
|
|||
BlockUnratedItems = new UnratedItem[] { };
|
||||
|
||||
ExcludeFoldersFromGrouping = new string[] { };
|
||||
|
||||
DisplayCollectionsView = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
|
@ -17,6 +16,12 @@ namespace MediaBrowser.Model.Dto
|
|||
/// <value>The name.</value>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier.
|
||||
/// </summary>
|
||||
/// <value>The identifier.</value>
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the primary image tag.
|
||||
/// </summary>
|
||||
|
|
|
@ -328,7 +328,8 @@ namespace MediaBrowser.Server.Implementations.Dto
|
|||
if (!string.IsNullOrEmpty(item.Album))
|
||||
{
|
||||
var parentAlbum = _libraryManager.RootFolder
|
||||
.GetRecursiveChildren(i => i is MusicAlbum)
|
||||
.GetRecursiveChildren()
|
||||
.Where(i => i is MusicAlbum)
|
||||
.FirstOrDefault(i => string.Equals(i.Name, item.Album, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (parentAlbum != null)
|
||||
|
@ -539,6 +540,7 @@ namespace MediaBrowser.Server.Implementations.Dto
|
|||
|
||||
if (dictionary.TryGetValue(studio, out entity))
|
||||
{
|
||||
studioDto.Id = entity.Id.ToString("N");
|
||||
studioDto.PrimaryImageTag = GetImageCacheTag(entity, ImageType.Primary);
|
||||
}
|
||||
|
||||
|
@ -1248,7 +1250,8 @@ namespace MediaBrowser.Server.Implementations.Dto
|
|||
}
|
||||
else
|
||||
{
|
||||
children = folder.GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual);
|
||||
children = folder.GetRecursiveChildren(user)
|
||||
.Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual);
|
||||
}
|
||||
|
||||
// Loop through each recursive child
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
{
|
||||
var user = _userManager.GetUserById(new Guid(query.UserId));
|
||||
|
||||
inputItems = user.RootFolder.GetRecursiveChildren(user, null);
|
||||
inputItems = user.RootFolder.GetRecursiveChildren(user, true);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Querying;
|
||||
|
||||
namespace MediaBrowser.Server.Implementations.Library
|
||||
{
|
||||
|
@ -84,7 +85,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
if (user.Configuration.DisplayCollectionsView ||
|
||||
recursiveChildren.OfType<BoxSet>().Any())
|
||||
{
|
||||
list.Add(await GetUserView(CollectionType.BoxSets, user, "zzz_" + CollectionType.BoxSets, cancellationToken).ConfigureAwait(false));
|
||||
list.Add(await GetUserView(CollectionType.BoxSets, user, CollectionType.BoxSets, cancellationToken).ConfigureAwait(false));
|
||||
}
|
||||
|
||||
if (query.IncludeExternalContent)
|
||||
|
@ -114,7 +115,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
}
|
||||
}
|
||||
|
||||
return list.OrderBy(i => i.SortName);
|
||||
return _libraryManager.Sort(list, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).Cast<Folder>();
|
||||
}
|
||||
|
||||
private Task<UserView> GetUserView(string type, User user, string sortName, CancellationToken cancellationToken)
|
||||
|
|
Loading…
Reference in New Issue
Block a user