diff --git a/MediaBrowser.Api/Playback/Dash/MpegDashService.cs b/MediaBrowser.Api/Playback/Dash/MpegDashService.cs
index f2754fe7cc..2655ff8ad1 100644
--- a/MediaBrowser.Api/Playback/Dash/MpegDashService.cs
+++ b/MediaBrowser.Api/Playback/Dash/MpegDashService.cs
@@ -209,14 +209,14 @@ namespace MediaBrowser.Api.Playback.Dash
ApiEntryPoint.Instance.KillTranscodingJobs(j => j.Type == TranscodingJobType && string.Equals(j.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase), p => !string.Equals(p, playlistPath, StringComparison.OrdinalIgnoreCase));
}
- private long GetPositionTicks(StreamState state, int segmentIndex)
+ private long GetPositionTicks(StreamState state, int requestedIndex)
{
- if (segmentIndex <= 1)
+ if (requestedIndex <= 0)
{
return 0;
}
- var startSeconds = segmentIndex * state.SegmentLength;
+ var startSeconds = requestedIndex * state.SegmentLength;
return TimeSpan.FromSeconds(startSeconds).Ticks;
}
@@ -291,6 +291,13 @@ namespace MediaBrowser.Api.Playback.Dash
public int? GetCurrentTranscodingIndex(string playlist, string segmentExtension)
{
+ var job = ApiEntryPoint.Instance.GetTranscodingJob(playlist, TranscodingJobType);
+
+ if (job == null || job.HasExited)
+ {
+ return null;
+ }
+
var file = GetLastTranscodingFiles(playlist, segmentExtension, FileSystem, 1).FirstOrDefault();
if (file == null)
@@ -341,11 +348,11 @@ namespace MediaBrowser.Api.Playback.Dash
}
}
- private string FindSegment(string playlist, string representationId, string segmentExtension, int index)
+ private string FindSegment(string playlist, string representationId, string segmentExtension, int requestedIndex)
{
var folder = Path.GetDirectoryName(playlist);
- if (index == -1)
+ if (requestedIndex == -1)
{
var path = Path.Combine(folder, "0", "stream" + representationId + "-" + "init" + segmentExtension);
return File.Exists(path) ? path : null;
@@ -355,12 +362,16 @@ namespace MediaBrowser.Api.Playback.Dash
{
foreach (var subfolder in new DirectoryInfo(folder).EnumerateDirectories().ToList())
{
+ var subfolderName = Path.GetFileNameWithoutExtension(subfolder.FullName);
int startNumber;
- if (int.TryParse(Path.GetFileNameWithoutExtension(subfolder.FullName), NumberStyles.Any, UsCulture, out startNumber))
+ if (int.TryParse(subfolderName, NumberStyles.Any, UsCulture, out startNumber))
{
- var segmentIndex = index - startNumber + 1;
- var path = Path.Combine(folder, "0", "stream" + representationId + "-" + segmentIndex.ToString("00000", CultureInfo.InvariantCulture) + segmentExtension);
- return File.Exists(path) ? path : null;
+ var segmentIndex = requestedIndex - startNumber + 1;
+ var path = Path.Combine(folder, subfolderName, "stream" + representationId + "-" + segmentIndex.ToString("00000", CultureInfo.InvariantCulture) + segmentExtension);
+ if (File.Exists(path))
+ {
+ return path;
+ }
}
}
}
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index 719e7f1566..32bbda8dbc 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -100,13 +100,13 @@ namespace MediaBrowser.Api.Playback.Hls
var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
- var index = int.Parse(segmentId, NumberStyles.Integer, UsCulture);
+ var requestedIndex = int.Parse(segmentId, NumberStyles.Integer, UsCulture);
var state = await GetState(request, cancellationToken).ConfigureAwait(false);
var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8");
- var segmentPath = GetSegmentPath(playlistPath, index);
+ var segmentPath = GetSegmentPath(playlistPath, requestedIndex);
var segmentLength = state.SegmentLength;
var segmentExtension = GetSegmentFileExtension(state);
@@ -116,7 +116,7 @@ namespace MediaBrowser.Api.Playback.Hls
if (File.Exists(segmentPath))
{
job = ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType);
- return await GetSegmentResult(playlistPath, segmentPath, index, segmentLength, job, cancellationToken).ConfigureAwait(false);
+ return await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false);
}
await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
@@ -125,13 +125,13 @@ namespace MediaBrowser.Api.Playback.Hls
if (File.Exists(segmentPath))
{
job = ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType);
- return await GetSegmentResult(playlistPath, segmentPath, index, segmentLength, job, cancellationToken).ConfigureAwait(false);
+ return await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false);
}
else
{
var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension);
var segmentGapRequiringTranscodingChange = 24/state.SegmentLength;
- if (currentTranscodingIndex == null || index < currentTranscodingIndex.Value || (index - currentTranscodingIndex.Value) > segmentGapRequiringTranscodingChange)
+ if (currentTranscodingIndex == null || requestedIndex < currentTranscodingIndex.Value || (requestedIndex - currentTranscodingIndex.Value) > segmentGapRequiringTranscodingChange)
{
// If the playlist doesn't already exist, startup ffmpeg
try
@@ -143,8 +143,7 @@ namespace MediaBrowser.Api.Playback.Hls
DeleteLastFile(playlistPath, segmentExtension, 0);
}
- var startSeconds = index * state.SegmentLength;
- request.StartTimeTicks = TimeSpan.FromSeconds(startSeconds).Ticks;
+ request.StartTimeTicks = GetSeekPositionTicks(state, requestedIndex);
job = await StartFfMpeg(state, playlistPath, cancellationTokenSource).ConfigureAwait(false);
}
@@ -171,11 +170,26 @@ namespace MediaBrowser.Api.Playback.Hls
Logger.Info("returning {0}", segmentPath);
job = job ?? ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType);
- return await GetSegmentResult(playlistPath, segmentPath, index, segmentLength, job, cancellationToken).ConfigureAwait(false);
+ return await GetSegmentResult(playlistPath, segmentPath, requestedIndex, segmentLength, job, cancellationToken).ConfigureAwait(false);
+ }
+
+ private long GetSeekPositionTicks(StreamState state, int requestedIndex)
+ {
+ var startSeconds = requestedIndex * state.SegmentLength;
+ var position = TimeSpan.FromSeconds(startSeconds).Ticks;
+
+ return position;
}
public int? GetCurrentTranscodingIndex(string playlist, string segmentExtension)
{
+ var job = ApiEntryPoint.Instance.GetTranscodingJob(playlist, TranscodingJobType);
+
+ if (job == null || job.HasExited)
+ {
+ return null;
+ }
+
var file = GetLastTranscodingFile(playlist, segmentExtension, FileSystem);
if (file == null)
diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs
index bdaad49e64..4d222c9bb3 100644
--- a/MediaBrowser.Api/UserLibrary/ItemsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs
@@ -39,6 +39,9 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "Person", Description = "Optional. If specified, results will be filtered to include only those containing the specified person.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string Person { get; set; }
+ [ApiMember(Name = "PersonIds", Description = "Optional. If specified, results will be filtered to include only those containing the specified person.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string PersonIds { get; set; }
+
///
/// If the Person filter is used, this can also be used to restrict to a specific person type
///
@@ -244,6 +247,11 @@ namespace MediaBrowser.Api.UserLibrary
return (PersonTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
}
+ public string[] GetPersonIds()
+ {
+ return (PersonIds ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+ }
+
public VideoType[] GetVideoTypes()
{
var val = VideoTypes;
@@ -477,6 +485,7 @@ namespace MediaBrowser.Api.UserLibrary
Studios = request.GetStudios(),
StudioIds = request.GetStudioIds(),
Person = request.Person,
+ PersonIds = request.GetPersonIds(),
PersonTypes = request.GetPersonTypes(),
Years = request.GetYears(),
ImageTypes = request.GetImageTypes().ToArray(),
@@ -968,6 +977,13 @@ namespace MediaBrowser.Api.UserLibrary
// 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 && !(personIds.Any(v => i.People.Select(p => p.Name).Contains(v, StringComparer.OrdinalIgnoreCase))))
{
return false;
}
diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
index e1344009f0..727f756f15 100644
--- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
+++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
@@ -41,6 +41,7 @@ namespace MediaBrowser.Controller.Entities
public string NameLessThan { get; set; }
public string Person { get; set; }
+ public string[] PersonIds { get; set; }
public string AdjacentTo { get; set; }
public string[] PersonTypes { get; set; }
@@ -87,6 +88,7 @@ namespace MediaBrowser.Controller.Entities
VideoTypes = new VideoType[] { };
Years = new int[] { };
PersonTypes = new string[] { };
+ PersonIds = new string[] { };
}
}
}
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
index 2f182273be..f21add553d 100644
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
@@ -115,6 +115,7 @@ namespace MediaBrowser.Controller.Entities
case CollectionType.Books:
case CollectionType.Photos:
case CollectionType.HomeVideos:
+ case CollectionType.MusicVideos:
return GetResult(queryParent.GetChildren(user, true), queryParent, query);
case CollectionType.Folders:
@@ -1056,6 +1057,11 @@ namespace MediaBrowser.Controller.Entities
return false;
}
+ if (request.PersonIds.Length > 0)
+ {
+ return false;
+ }
+
if (request.Studios.Length > 0)
{
return false;
@@ -1632,7 +1638,13 @@ namespace MediaBrowser.Controller.Entities
}
// Apply person filter
- if (!string.IsNullOrEmpty(query.Person))
+ if (query.PersonIds.Length > 0 && !(query.PersonIds.Any(v => item.People.Select(i => i.Name).Contains(v, StringComparer.OrdinalIgnoreCase))))
+ {
+ return false;
+ }
+
+ // Apply person filter
+ if (!string.IsNullOrWhiteSpace(query.Person))
{
var personTypes = query.PersonTypes;
diff --git a/MediaBrowser.Model/Querying/ItemQuery.cs b/MediaBrowser.Model/Querying/ItemQuery.cs
index 6d26a2c0a7..0cdf5ca7ab 100644
--- a/MediaBrowser.Model/Querying/ItemQuery.cs
+++ b/MediaBrowser.Model/Querying/ItemQuery.cs
@@ -120,7 +120,7 @@ namespace MediaBrowser.Model.Querying
/// Limit results to items containing a specific person
///
/// The person.
- public string Person { get; set; }
+ public string[] PersonIds { get; set; }
///
/// If the Person filter is used, this can also be used to restrict to a specific person type
@@ -307,6 +307,7 @@ namespace MediaBrowser.Model.Querying
PersonTypes = new string[] { };
Ids = new string[] { };
ArtistIds = new string[] { };
+ PersonIds = new string[] { };
ImageTypes = new ImageType[] { };
AirDays = new DayOfWeek[] { };
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
index 0fda026fd6..1f82e7ef13 100644
--- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
@@ -1673,7 +1673,7 @@ namespace MediaBrowser.Server.Implementations.Library
throw new ArgumentNullException("viewType");
}
- var id = GetNewItemId("36_namedview_" + name + user.Id.ToString("N") + (parentId ?? string.Empty), typeof(UserView));
+ var id = GetNewItemId("37_namedview_" + name + user.Id.ToString("N") + (parentId ?? string.Empty), typeof(UserView));
var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N"));
diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
index fb14628d83..e63a275511 100644
--- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
@@ -71,7 +71,15 @@ namespace MediaBrowser.Server.Implementations.Library
{
var collectionFolder = folder as ICollectionFolder;
var folderViewType = collectionFolder == null ? null : collectionFolder.CollectionType;
- list.Add(await GetUserView(folder.Id, folder.Name, folderViewType, string.Empty, user, cancellationToken).ConfigureAwait(false));
+
+ if (string.IsNullOrWhiteSpace(folderViewType))
+ {
+ list.Add(folder);
+ }
+ else
+ {
+ list.Add(await GetUserView(folder.Id, folder.Name, folderViewType, string.Empty, user, cancellationToken).ConfigureAwait(false));
+ }
}
}
else
diff --git a/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs b/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs
index 2af4c6a8cd..b98dd2c49b 100644
--- a/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs
+++ b/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs
@@ -231,6 +231,7 @@ namespace MediaBrowser.Server.Implementations.UserViews
CollectionType.Books,
CollectionType.Photos,
CollectionType.HomeVideos,
+ CollectionType.MusicVideos,
string.Empty
};