diff options
Diffstat (limited to 'MediaBrowser.Api')
| -rw-r--r-- | MediaBrowser.Api/ApiEntryPoint.cs | 13 | ||||
| -rw-r--r-- | MediaBrowser.Api/BaseApiService.cs | 18 | ||||
| -rw-r--r-- | MediaBrowser.Api/IHasItemFields.cs | 3 | ||||
| -rw-r--r-- | MediaBrowser.Api/ItemLookupService.cs | 31 | ||||
| -rw-r--r-- | MediaBrowser.Api/ItemRefreshService.cs | 2 | ||||
| -rw-r--r-- | MediaBrowser.Api/ItemUpdateService.cs | 16 | ||||
| -rw-r--r-- | MediaBrowser.Api/LiveTv/LiveTvService.cs | 7 | ||||
| -rw-r--r-- | MediaBrowser.Api/MediaBrowser.Api.csproj | 2 | ||||
| -rw-r--r-- | MediaBrowser.Api/PackageService.cs | 33 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/BaseStreamingService.cs | 8 | ||||
| -rw-r--r-- | MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 110 | ||||
| -rw-r--r-- | MediaBrowser.Api/Session/SessionsService.cs | 2 | ||||
| -rw-r--r-- | MediaBrowser.Api/Subtitles/SubtitleService.cs | 3 | ||||
| -rw-r--r-- | MediaBrowser.Api/UserLibrary/PersonsService.cs | 5 | ||||
| -rw-r--r-- | MediaBrowser.Api/UserLibrary/UserLibraryService.cs | 2 |
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, |
