aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Api
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Api')
-rw-r--r--MediaBrowser.Api/ApiEntryPoint.cs13
-rw-r--r--MediaBrowser.Api/BaseApiService.cs18
-rw-r--r--MediaBrowser.Api/IHasItemFields.cs3
-rw-r--r--MediaBrowser.Api/ItemLookupService.cs31
-rw-r--r--MediaBrowser.Api/ItemRefreshService.cs2
-rw-r--r--MediaBrowser.Api/ItemUpdateService.cs16
-rw-r--r--MediaBrowser.Api/LiveTv/LiveTvService.cs7
-rw-r--r--MediaBrowser.Api/MediaBrowser.Api.csproj2
-rw-r--r--MediaBrowser.Api/PackageService.cs33
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs8
-rw-r--r--MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs110
-rw-r--r--MediaBrowser.Api/Session/SessionsService.cs2
-rw-r--r--MediaBrowser.Api/Subtitles/SubtitleService.cs3
-rw-r--r--MediaBrowser.Api/UserLibrary/PersonsService.cs5
-rw-r--r--MediaBrowser.Api/UserLibrary/UserLibraryService.cs2
15 files changed, 151 insertions, 104 deletions
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index 7dca7e8147..a5d65a7160 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -40,14 +40,13 @@ namespace MediaBrowser.Api
internal IHttpResultFactory ResultFactory { get; private set; }
/// <summary>
- /// The application paths
+ /// Gets the configuration manager.
/// </summary>
- private readonly IServerConfigurationManager _config;
+ internal IServerConfigurationManager ConfigurationManager { get; }
private readonly ISessionManager _sessionManager;
private readonly IFileSystem _fileSystem;
private readonly IMediaSourceManager _mediaSourceManager;
- public readonly IProcessFactory ProcessFactory;
/// <summary>
/// The active transcoding jobs
@@ -73,15 +72,13 @@ namespace MediaBrowser.Api
IServerConfigurationManager config,
IFileSystem fileSystem,
IMediaSourceManager mediaSourceManager,
- IProcessFactory processFactory,
IHttpResultFactory resultFactory)
{
Logger = logger;
_sessionManager = sessionManager;
- _config = config;
+ ConfigurationManager = config;
_fileSystem = fileSystem;
_mediaSourceManager = mediaSourceManager;
- ProcessFactory = processFactory;
ResultFactory = resultFactory;
_sessionManager.PlaybackProgress += _sessionManager_PlaybackProgress;
@@ -162,7 +159,7 @@ namespace MediaBrowser.Api
public EncodingOptions GetEncodingOptions()
{
- return ConfigurationManagerExtensions.GetConfiguration<EncodingOptions>(_config, "encoding");
+ return ConfigurationManagerExtensions.GetConfiguration<EncodingOptions>(ConfigurationManager, "encoding");
}
/// <summary>
@@ -170,7 +167,7 @@ namespace MediaBrowser.Api
/// </summary>
private void DeleteEncodedMediaCache()
{
- var path = _config.ApplicationPaths.GetTranscodingTempPath();
+ var path = ConfigurationManager.ApplicationPaths.GetTranscodingTempPath();
if (!Directory.Exists(path))
{
diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs
index 49f8c6ace0..3111055968 100644
--- a/MediaBrowser.Api/BaseApiService.cs
+++ b/MediaBrowser.Api/BaseApiService.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -298,11 +297,24 @@ namespace MediaBrowser.Api
var pathInfo = Parse(Request.PathInfo);
var first = pathInfo[0];
+ string baseUrl = ApiEntryPoint.Instance.ConfigurationManager.Configuration.BaseUrl;
+
// backwards compatibility
- if (string.Equals(first, "mediabrowser", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(first, "emby", StringComparison.OrdinalIgnoreCase))
+ if (baseUrl.Length == 0
+ && (string.Equals(first, "mediabrowser", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(first, "emby", StringComparison.OrdinalIgnoreCase)))
+ {
+ index++;
+ }
+ else if (string.Equals(first, baseUrl.Remove(0, 1)))
{
index++;
+ var second = pathInfo[1];
+ if (string.Equals(second, "mediabrowser", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(second, "emby", StringComparison.OrdinalIgnoreCase))
+ {
+ index++;
+ }
}
return pathInfo[index];
diff --git a/MediaBrowser.Api/IHasItemFields.cs b/MediaBrowser.Api/IHasItemFields.cs
index 8598ea262f..85b4a7e2d2 100644
--- a/MediaBrowser.Api/IHasItemFields.cs
+++ b/MediaBrowser.Api/IHasItemFields.cs
@@ -32,7 +32,7 @@ namespace MediaBrowser.Api
if (string.IsNullOrEmpty(val))
{
- return new ItemFields[] { };
+ return Array.Empty<ItemFields>();
}
return val.Split(',').Select(v =>
@@ -41,6 +41,7 @@ namespace MediaBrowser.Api
{
return (ItemFields?)value;
}
+
return null;
}).Where(i => i.HasValue).Select(i => i.Value).ToArray();
diff --git a/MediaBrowser.Api/ItemLookupService.cs b/MediaBrowser.Api/ItemLookupService.cs
index f3ea7907f6..084b20bc15 100644
--- a/MediaBrowser.Api/ItemLookupService.cs
+++ b/MediaBrowser.Api/ItemLookupService.cs
@@ -227,15 +227,17 @@ namespace MediaBrowser.Api
//item.ProductionYear = request.ProductionYear;
//item.Name = request.Name;
- return _providerManager.RefreshFullItem(item, new MetadataRefreshOptions(new DirectoryService(Logger, _fileSystem))
- {
- MetadataRefreshMode = MetadataRefreshMode.FullRefresh,
- ImageRefreshMode = MetadataRefreshMode.FullRefresh,
- ReplaceAllMetadata = true,
- ReplaceAllImages = request.ReplaceAllImages,
- SearchResult = request
-
- }, CancellationToken.None);
+ return _providerManager.RefreshFullItem(
+ item,
+ new MetadataRefreshOptions(new DirectoryService(_fileSystem))
+ {
+ MetadataRefreshMode = MetadataRefreshMode.FullRefresh,
+ ImageRefreshMode = MetadataRefreshMode.FullRefresh,
+ ReplaceAllMetadata = true,
+ ReplaceAllImages = request.ReplaceAllImages,
+ SearchResult = request
+ },
+ CancellationToken.None);
}
/// <summary>
@@ -294,11 +296,9 @@ namespace MediaBrowser.Api
Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath));
using (var stream = result.Content)
+ using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
{
- using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
- {
- await stream.CopyToAsync(filestream).ConfigureAwait(false);
- }
+ await stream.CopyToAsync(filestream).ConfigureAwait(false);
}
Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
@@ -311,9 +311,6 @@ namespace MediaBrowser.Api
/// <param name="filename">The filename.</param>
/// <returns>System.String.</returns>
private string GetFullCachePath(string filename)
- {
- return Path.Combine(_appPaths.CachePath, "remote-images", filename.Substring(0, 1), filename);
- }
-
+ => Path.Combine(_appPaths.CachePath, "remote-images", filename.Substring(0, 1), filename);
}
}
diff --git a/MediaBrowser.Api/ItemRefreshService.cs b/MediaBrowser.Api/ItemRefreshService.cs
index 8238ad19c5..a1d69cd2b0 100644
--- a/MediaBrowser.Api/ItemRefreshService.cs
+++ b/MediaBrowser.Api/ItemRefreshService.cs
@@ -63,7 +63,7 @@ namespace MediaBrowser.Api
private MetadataRefreshOptions GetRefreshOptions(RefreshItem request)
{
- return new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem))
+ return new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{
MetadataRefreshMode = request.MetadataRefreshMode,
ImageRefreshMode = request.ImageRefreshMode,
diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs
index d6514d62e9..5d524b1859 100644
--- a/MediaBrowser.Api/ItemUpdateService.cs
+++ b/MediaBrowser.Api/ItemUpdateService.cs
@@ -225,13 +225,15 @@ namespace MediaBrowser.Api
if (displayOrderChanged)
{
- _providerManager.QueueRefresh(series.Id, new MetadataRefreshOptions(new DirectoryService(Logger, _fileSystem))
- {
- MetadataRefreshMode = MetadataRefreshMode.FullRefresh,
- ImageRefreshMode = MetadataRefreshMode.FullRefresh,
- ReplaceAllMetadata = true
-
- }, RefreshPriority.High);
+ _providerManager.QueueRefresh(
+ series.Id,
+ new MetadataRefreshOptions(new DirectoryService(_fileSystem))
+ {
+ MetadataRefreshMode = MetadataRefreshMode.FullRefresh,
+ ImageRefreshMode = MetadataRefreshMode.FullRefresh,
+ ReplaceAllMetadata = true
+ },
+ RefreshPriority.High);
}
}
diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs
index b05e8c9495..2b9a64e975 100644
--- a/MediaBrowser.Api/LiveTv/LiveTvService.cs
+++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs
@@ -8,6 +8,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Api.UserLibrary;
+using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
@@ -25,7 +26,6 @@ using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Services;
using Microsoft.Net.Http.Headers;
-using static MediaBrowser.Common.HexHelper;
namespace MediaBrowser.Api.LiveTv
{
@@ -887,8 +887,9 @@ namespace MediaBrowser.Api.LiveTv
{
// SchedulesDirect requires a SHA1 hash of the user's password
// https://github.com/SchedulesDirect/JSON-Service/wiki/API-20141201#obtain-a-token
- using (SHA1 sha = SHA1.Create()) {
- return ToHexString(
+ using (SHA1 sha = SHA1.Create())
+ {
+ return Hex.Encode(
sha.ComputeHash(Encoding.UTF8.GetBytes(str)));
}
}
diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj
index f653270a6c..0d62cf8c59 100644
--- a/MediaBrowser.Api/MediaBrowser.Api.csproj
+++ b/MediaBrowser.Api/MediaBrowser.Api.csproj
@@ -10,7 +10,7 @@
</ItemGroup>
<PropertyGroup>
- <TargetFramework>netstandard2.0</TargetFramework>
+ <TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
diff --git a/MediaBrowser.Api/PackageService.cs b/MediaBrowser.Api/PackageService.cs
index baa6f7bb97..1e5a932107 100644
--- a/MediaBrowser.Api/PackageService.cs
+++ b/MediaBrowser.Api/PackageService.cs
@@ -1,11 +1,11 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common;
using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Progress;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Services;
@@ -131,15 +131,9 @@ namespace MediaBrowser.Api
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
- ///
- /// <summary>
- /// Gets the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>System.Object.</returns>
public object Get(GetPackage request)
{
- var packages = _installationManager.GetAvailablePackages(CancellationToken.None, applicationVersion: typeof(PackageService).Assembly.GetName().Version).Result;
+ var packages = _installationManager.GetAvailablePackages().Result;
var result = packages.FirstOrDefault(p => string.Equals(p.guid, request.AssemblyGuid ?? "none", StringComparison.OrdinalIgnoreCase))
?? packages.FirstOrDefault(p => p.name.Equals(request.Name, StringComparison.OrdinalIgnoreCase));
@@ -154,7 +148,7 @@ namespace MediaBrowser.Api
/// <returns>System.Object.</returns>
public async Task<object> Get(GetPackages request)
{
- IEnumerable<PackageInfo> packages = await _installationManager.GetAvailablePackages(CancellationToken.None, false, request.PackageType, typeof(PackageService).Assembly.GetName().Version).ConfigureAwait(false);
+ IEnumerable<PackageInfo> packages = await _installationManager.GetAvailablePackages().ConfigureAwait(false);
if (!string.IsNullOrEmpty(request.TargetSystems))
{
@@ -163,11 +157,6 @@ namespace MediaBrowser.Api
packages = packages.Where(p => apps.Contains(p.targetSystem));
}
- if (request.IsPremium.HasValue)
- {
- packages = packages.Where(p => p.isPremium == request.IsPremium.Value);
- }
-
if (request.IsAdult.HasValue)
{
packages = packages.Where(p => p.adult == request.IsAdult.Value);
@@ -188,13 +177,21 @@ namespace MediaBrowser.Api
/// <exception cref="ResourceNotFoundException"></exception>
public async Task Post(InstallPackage request)
{
- var package = string.IsNullOrEmpty(request.Version) ?
- await _installationManager.GetLatestCompatibleVersion(request.Name, request.AssemblyGuid, typeof(PackageService).Assembly.GetName().Version, request.UpdateClass).ConfigureAwait(false) :
- await _installationManager.GetPackage(request.Name, request.AssemblyGuid, request.UpdateClass, Version.Parse(request.Version)).ConfigureAwait(false);
+ var packages = await _installationManager.GetAvailablePackages().ConfigureAwait(false);
+ var package = _installationManager.GetCompatibleVersions(
+ packages,
+ request.Name,
+ new Guid(request.AssemblyGuid),
+ string.IsNullOrEmpty(request.Version) ? null : Version.Parse(request.Version),
+ request.UpdateClass).FirstOrDefault();
if (package == null)
{
- throw new ResourceNotFoundException(string.Format("Package not found: {0}", request.Name));
+ throw new ResourceNotFoundException(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ "Package not found: {0}",
+ request.Name));
}
await _installationManager.InstallPackage(package);
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 8c4ccfa22c..7bfe0e0cea 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -289,17 +289,22 @@ namespace MediaBrowser.Api.Playback
throw;
}
+ Logger.LogDebug("Launched ffmpeg process");
state.TranscodingJob = transcodingJob;
// Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
_ = new JobLogger(Logger).StartStreamingLog(state, process.StandardError.BaseStream, logStream);
// Wait for the file to exist before proceeeding
- while (!File.Exists(state.WaitForPath ?? outputPath) && !transcodingJob.HasExited)
+ var ffmpegTargetFile = state.WaitForPath ?? outputPath;
+ Logger.LogDebug("Waiting for the creation of {0}", ffmpegTargetFile);
+ while (!File.Exists(ffmpegTargetFile) && !transcodingJob.HasExited)
{
await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false);
}
+ Logger.LogDebug("File {0} created or transcoding has finished", ffmpegTargetFile);
+
if (state.IsInputVideo && transcodingJob.Type == TranscodingJobType.Progressive && !transcodingJob.HasExited)
{
await Task.Delay(1000, cancellationTokenSource.Token).ConfigureAwait(false);
@@ -314,6 +319,7 @@ namespace MediaBrowser.Api.Playback
{
StartThrottler(state, transcodingJob);
}
+ Logger.LogDebug("StartFfMpeg() finished successfully");
return transcodingJob;
}
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index f5f7536843..9ecb5fe8c5 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -192,6 +192,7 @@ namespace MediaBrowser.Api.Playback.Hls
if (File.Exists(segmentPath))
{
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
+ Logger.LogDebug("returning {0} [it exists, try 1]", segmentPath);
return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, requestedIndex, job, cancellationToken).ConfigureAwait(false);
}
@@ -207,6 +208,7 @@ namespace MediaBrowser.Api.Playback.Hls
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
transcodingLock.Release();
released = true;
+ Logger.LogDebug("returning {0} [it exists, try 2]", segmentPath);
return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, requestedIndex, job, cancellationToken).ConfigureAwait(false);
}
else
@@ -243,6 +245,7 @@ namespace MediaBrowser.Api.Playback.Hls
request.StartTimeTicks = GetStartPositionTicks(state, requestedIndex);
+ state.WaitForPath = segmentPath;
job = await StartFfMpeg(state, playlistPath, cancellationTokenSource).ConfigureAwait(false);
}
catch
@@ -277,7 +280,7 @@ namespace MediaBrowser.Api.Playback.Hls
// await Task.Delay(50, cancellationToken).ConfigureAwait(false);
//}
- Logger.LogInformation("returning {0}", segmentPath);
+ Logger.LogDebug("returning {0} [general case]", segmentPath);
job = job ?? ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, requestedIndex, job, cancellationToken).ConfigureAwait(false);
}
@@ -458,56 +461,68 @@ namespace MediaBrowser.Api.Playback.Hls
TranscodingJob transcodingJob,
CancellationToken cancellationToken)
{
- var segmentFileExists = File.Exists(segmentPath);
-
- // If all transcoding has completed, just return immediately
- if (transcodingJob != null && transcodingJob.HasExited && segmentFileExists)
+ var segmentExists = File.Exists(segmentPath);
+ if (segmentExists)
{
- return await GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob).ConfigureAwait(false);
- }
+ if (transcodingJob != null && transcodingJob.HasExited)
+ {
+ // Transcoding job is over, so assume all existing files are ready
+ Logger.LogDebug("serving up {0} as transcode is over", segmentPath);
+ return await GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob).ConfigureAwait(false);
+ }
- if (segmentFileExists)
- {
var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension);
// If requested segment is less than transcoding position, we can't transcode backwards, so assume it's ready
if (segmentIndex < currentTranscodingIndex)
{
+ Logger.LogDebug("serving up {0} as transcode index {1} is past requested point {2}", segmentPath, currentTranscodingIndex, segmentIndex);
return await GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob).ConfigureAwait(false);
}
}
- var segmentFilename = Path.GetFileName(segmentPath);
-
- while (!cancellationToken.IsCancellationRequested)
+ var nextSegmentPath = GetSegmentPath(state, playlistPath, segmentIndex + 1);
+ if (transcodingJob != null)
{
- try
+ while (!cancellationToken.IsCancellationRequested && !transcodingJob.HasExited)
{
- var text = File.ReadAllText(playlistPath, Encoding.UTF8);
-
- // If it appears in the playlist, it's done
- if (text.IndexOf(segmentFilename, StringComparison.OrdinalIgnoreCase) != -1)
+ // To be considered ready, the segment file has to exist AND
+ // either the transcoding job should be done or next segment should also exist
+ if (segmentExists)
{
- if (!segmentFileExists)
+ if (transcodingJob.HasExited || File.Exists(nextSegmentPath))
{
- segmentFileExists = File.Exists(segmentPath);
+ Logger.LogDebug("serving up {0} as it deemed ready", segmentPath);
+ return await GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob).ConfigureAwait(false);
}
- if (segmentFileExists)
+ }
+ else
+ {
+ segmentExists = File.Exists(segmentPath);
+ if (segmentExists)
{
- return await GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob).ConfigureAwait(false);
+ continue; // avoid unnecessary waiting if segment just became available
}
- //break;
}
+
+ await Task.Delay(100, cancellationToken).ConfigureAwait(false);
+ }
+
+ if (!File.Exists(segmentPath))
+ {
+ Logger.LogWarning("cannot serve {0} as transcoding quit before we got there", segmentPath);
}
- catch (IOException)
+ else
{
- // May get an error if the file is locked
+ Logger.LogDebug("serving {0} as it's on disk and transcoding stopped", segmentPath);
}
-
- await Task.Delay(100, cancellationToken).ConfigureAwait(false);
+ cancellationToken.ThrowIfCancellationRequested();
+ }
+ else
+ {
+ Logger.LogWarning("cannot serve {0} as it doesn't exist and no transcode is running", segmentPath);
}
- cancellationToken.ThrowIfCancellationRequested();
return await GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob).ConfigureAwait(false);
}
@@ -521,6 +536,7 @@ namespace MediaBrowser.Api.Playback.Hls
FileShare = FileShareMode.ReadWrite,
OnComplete = () =>
{
+ Logger.LogDebug("finished serving {0}", segmentPath);
if (transcodingJob != null)
{
transcodingJob.DownloadPositionTicks = Math.Max(transcodingJob.DownloadPositionTicks ?? segmentEndingPositionTicks, segmentEndingPositionTicks);
@@ -909,9 +925,23 @@ namespace MediaBrowser.Api.Playback.Hls
else
{
var keyFrameArg = string.Format(
+ CultureInfo.InvariantCulture,
" -force_key_frames:0 \"expr:gte(t,{0}+n_forced*{1})\"",
GetStartNumber(state) * state.SegmentLength,
- state.SegmentLength.ToString(CultureInfo.InvariantCulture));
+ state.SegmentLength);
+ if (state.TargetFramerate.HasValue)
+ {
+ // This is to make sure keyframe interval is limited to our segment,
+ // as forcing keyframes is not enough.
+ // Example: we encoded half of desired length, then codec detected
+ // scene cut and inserted a keyframe; next forced keyframe would
+ // be created outside of segment, which breaks seeking.
+ keyFrameArg += string.Format(
+ CultureInfo.InvariantCulture,
+ " -g {0} -keyint_min {0}",
+ (int)(state.SegmentLength * state.TargetFramerate)
+ );
+ }
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
@@ -955,6 +985,15 @@ namespace MediaBrowser.Api.Playback.Hls
var threads = EncodingHelper.GetNumberOfThreads(state, encodingOptions, videoCodec);
+ if (state.BaseRequest.BreakOnNonKeyFrames)
+ {
+ // FIXME: this is actually a workaround, as ideally it really should be the client which decides whether non-keyframe
+ // breakpoints are supported; but current implementation always uses "ffmpeg input seeking" which is liable
+ // to produce a missing part of video stream before first keyframe is encountered, which may lead to
+ // awkward cases like a few starting HLS segments having no video whatsoever, which breaks hls.js
+ Logger.LogInformation("Current HLS implementation doesn't support non-keyframe breaks but one is requested, ignoring that request");
+ state.BaseRequest.BreakOnNonKeyFrames = false;
+ }
var inputModifier = EncodingHelper.GetInputModifier(state, encodingOptions);
// If isEncoding is true we're actually starting ffmpeg
@@ -965,14 +1004,6 @@ namespace MediaBrowser.Api.Playback.Hls
var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state.Request);
- var timeDeltaParam = string.Empty;
-
- if (isEncoding && state.TargetFramerate > 0)
- {
- float startTime = 1 / (state.TargetFramerate.Value * 2);
- timeDeltaParam = string.Format(CultureInfo.InvariantCulture, "-segment_time_delta {0:F3}", startTime);
- }
-
var segmentFormat = GetSegmentFileExtension(state.Request).TrimStart('.');
if (string.Equals(segmentFormat, "ts", StringComparison.OrdinalIgnoreCase))
{
@@ -980,7 +1011,7 @@ namespace MediaBrowser.Api.Playback.Hls
}
return string.Format(
- "{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f segment -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -segment_time {6} {10} -individual_header_trailer 0 -segment_format {11} -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"",
+ "{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f hls -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -hls_time {6} -individual_header_trailer 0 -hls_segment_type {7} -start_number {8} -hls_segment_filename \"{9}\" -hls_playlist_type vod -hls_list_size 0 -y \"{10}\"",
inputModifier,
EncodingHelper.GetInputArgument(state, encodingOptions),
threads,
@@ -988,11 +1019,10 @@ namespace MediaBrowser.Api.Playback.Hls
GetVideoArguments(state, encodingOptions),
GetAudioArguments(state, encodingOptions),
state.SegmentLength.ToString(CultureInfo.InvariantCulture),
+ segmentFormat,
startNumberParam,
- outputPath,
outputTsArg,
- timeDeltaParam,
- segmentFormat
+ outputPath
).Trim();
}
}
diff --git a/MediaBrowser.Api/Session/SessionsService.cs b/MediaBrowser.Api/Session/SessionsService.cs
index 76392e27c9..6caf3b4809 100644
--- a/MediaBrowser.Api/Session/SessionsService.cs
+++ b/MediaBrowser.Api/Session/SessionsService.cs
@@ -321,7 +321,7 @@ namespace MediaBrowser.Api.Session
DateCreated = DateTime.UtcNow,
DeviceId = _appHost.SystemId,
DeviceName = _appHost.FriendlyName,
- AppVersion = _appHost.ApplicationVersion
+ AppVersion = _appHost.ApplicationVersionString
});
}
diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs
index 52043d3df6..1878f51d05 100644
--- a/MediaBrowser.Api/Subtitles/SubtitleService.cs
+++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs
@@ -279,13 +279,12 @@ namespace MediaBrowser.Api.Subtitles
await _subtitleManager.DownloadSubtitles(video, request.SubtitleId, CancellationToken.None)
.ConfigureAwait(false);
- _providerManager.QueueRefresh(video.Id, new MetadataRefreshOptions(new DirectoryService(Logger, _fileSystem)), RefreshPriority.High);
+ _providerManager.QueueRefresh(video.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.High);
}
catch (Exception ex)
{
Logger.LogError(ex, "Error downloading subtitles");
}
-
});
}
}
diff --git a/MediaBrowser.Api/UserLibrary/PersonsService.cs b/MediaBrowser.Api/UserLibrary/PersonsService.cs
index c264577788..99f8300132 100644
--- a/MediaBrowser.Api/UserLibrary/PersonsService.cs
+++ b/MediaBrowser.Api/UserLibrary/PersonsService.cs
@@ -109,6 +109,11 @@ namespace MediaBrowser.Api.UserLibrary
NameContains = query.NameContains ?? query.SearchTerm
});
+ if (query.IsFavorite ?? false && query.User != null)
+ {
+ items = items.Where(i => UserDataRepository.GetUserData(query.User, i).IsFavorite).ToList();
+ }
+
return new QueryResult<(BaseItem, ItemCounts)>
{
TotalRecordCount = items.Count,
diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
index 45694a6780..da0bf6dcb0 100644
--- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
+++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
@@ -413,7 +413,7 @@ namespace MediaBrowser.Api.UserLibrary
if (!hasMetdata)
{
- var options = new MetadataRefreshOptions(new DirectoryService(Logger, _fileSystem))
+ var options = new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{
MetadataRefreshMode = MetadataRefreshMode.FullRefresh,
ImageRefreshMode = MetadataRefreshMode.FullRefresh,