aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Api/Controllers
diff options
context:
space:
mode:
Diffstat (limited to 'Jellyfin.Api/Controllers')
-rw-r--r--Jellyfin.Api/Controllers/DynamicHlsController.cs133
-rw-r--r--Jellyfin.Api/Controllers/LibraryController.cs4
-rw-r--r--Jellyfin.Api/Controllers/LibraryStructureController.cs8
-rw-r--r--Jellyfin.Api/Controllers/LiveTvController.cs58
-rw-r--r--Jellyfin.Api/Controllers/SystemController.cs12
-rw-r--r--Jellyfin.Api/Controllers/VideosController.cs6
6 files changed, 89 insertions, 132 deletions
diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs
index dda1e9d56..590cdc33f 100644
--- a/Jellyfin.Api/Controllers/DynamicHlsController.cs
+++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs
@@ -294,9 +294,7 @@ public class DynamicHlsController : BaseJellyfinApiController
if (!System.IO.File.Exists(playlistPath))
{
- var transcodingLock = _transcodeManager.GetTranscodingLock(playlistPath);
- await transcodingLock.WaitAsync(cancellationToken).ConfigureAwait(false);
- try
+ using (await _transcodeManager.LockAsync(playlistPath, cancellationToken).ConfigureAwait(false))
{
if (!System.IO.File.Exists(playlistPath))
{
@@ -326,10 +324,6 @@ public class DynamicHlsController : BaseJellyfinApiController
}
}
}
- finally
- {
- transcodingLock.Release();
- }
}
job ??= _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
@@ -1442,95 +1436,80 @@ public class DynamicHlsController : BaseJellyfinApiController
return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, segmentId, job, cancellationToken).ConfigureAwait(false);
}
- var transcodingLock = _transcodeManager.GetTranscodingLock(playlistPath);
- await transcodingLock.WaitAsync(cancellationToken).ConfigureAwait(false);
- var released = false;
- var startTranscoding = false;
-
- try
+ using (await _transcodeManager.LockAsync(playlistPath, cancellationToken).ConfigureAwait(false))
{
+ var startTranscoding = false;
if (System.IO.File.Exists(segmentPath))
{
job = _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
- transcodingLock.Release();
- released = true;
_logger.LogDebug("returning {0} [it exists, try 2]", segmentPath);
return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, segmentId, job, cancellationToken).ConfigureAwait(false);
}
- else
- {
- var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension);
- var segmentGapRequiringTranscodingChange = 24 / state.SegmentLength;
-
- if (segmentId == -1)
- {
- _logger.LogDebug("Starting transcoding because fmp4 init file is being requested");
- startTranscoding = true;
- segmentId = 0;
- }
- else if (currentTranscodingIndex is null)
- {
- _logger.LogDebug("Starting transcoding because currentTranscodingIndex=null");
- startTranscoding = true;
- }
- else if (segmentId < currentTranscodingIndex.Value)
- {
- _logger.LogDebug("Starting transcoding because requestedIndex={0} and currentTranscodingIndex={1}", segmentId, currentTranscodingIndex);
- startTranscoding = true;
- }
- else if (segmentId - currentTranscodingIndex.Value > segmentGapRequiringTranscodingChange)
- {
- _logger.LogDebug("Starting transcoding because segmentGap is {0} and max allowed gap is {1}. requestedIndex={2}", segmentId - currentTranscodingIndex.Value, segmentGapRequiringTranscodingChange, segmentId);
- startTranscoding = true;
- }
- if (startTranscoding)
- {
- // If the playlist doesn't already exist, startup ffmpeg
- try
- {
- await _transcodeManager.KillTranscodingJobs(streamingRequest.DeviceId, streamingRequest.PlaySessionId, p => false)
- .ConfigureAwait(false);
+ var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension);
+ var segmentGapRequiringTranscodingChange = 24 / state.SegmentLength;
- if (currentTranscodingIndex.HasValue)
- {
- DeleteLastFile(playlistPath, segmentExtension, 0);
- }
+ if (segmentId == -1)
+ {
+ _logger.LogDebug("Starting transcoding because fmp4 init file is being requested");
+ startTranscoding = true;
+ segmentId = 0;
+ }
+ else if (currentTranscodingIndex is null)
+ {
+ _logger.LogDebug("Starting transcoding because currentTranscodingIndex=null");
+ startTranscoding = true;
+ }
+ else if (segmentId < currentTranscodingIndex.Value)
+ {
+ _logger.LogDebug("Starting transcoding because requestedIndex={0} and currentTranscodingIndex={1}", segmentId, currentTranscodingIndex);
+ startTranscoding = true;
+ }
+ else if (segmentId - currentTranscodingIndex.Value > segmentGapRequiringTranscodingChange)
+ {
+ _logger.LogDebug("Starting transcoding because segmentGap is {0} and max allowed gap is {1}. requestedIndex={2}", segmentId - currentTranscodingIndex.Value, segmentGapRequiringTranscodingChange, segmentId);
+ startTranscoding = true;
+ }
- streamingRequest.StartTimeTicks = streamingRequest.CurrentRuntimeTicks;
+ if (startTranscoding)
+ {
+ // If the playlist doesn't already exist, startup ffmpeg
+ try
+ {
+ await _transcodeManager.KillTranscodingJobs(streamingRequest.DeviceId, streamingRequest.PlaySessionId, p => false)
+ .ConfigureAwait(false);
- state.WaitForPath = segmentPath;
- job = await _transcodeManager.StartFfMpeg(
- state,
- playlistPath,
- GetCommandLineArguments(playlistPath, state, false, segmentId),
- Request.HttpContext.User.GetUserId(),
- TranscodingJobType,
- cancellationTokenSource).ConfigureAwait(false);
- }
- catch
+ if (currentTranscodingIndex.HasValue)
{
- state.Dispose();
- throw;
+ DeleteLastFile(playlistPath, segmentExtension, 0);
}
- // await WaitForMinimumSegmentCount(playlistPath, 1, cancellationTokenSource.Token).ConfigureAwait(false);
+ streamingRequest.StartTimeTicks = streamingRequest.CurrentRuntimeTicks;
+
+ state.WaitForPath = segmentPath;
+ job = await _transcodeManager.StartFfMpeg(
+ state,
+ playlistPath,
+ GetCommandLineArguments(playlistPath, state, false, segmentId),
+ Request.HttpContext.User.GetUserId(),
+ TranscodingJobType,
+ cancellationTokenSource).ConfigureAwait(false);
}
- else
+ catch
{
- job = _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
- if (job?.TranscodingThrottler is not null)
- {
- await job.TranscodingThrottler.UnpauseTranscoding().ConfigureAwait(false);
- }
+ state.Dispose();
+ throw;
}
+
+ // await WaitForMinimumSegmentCount(playlistPath, 1, cancellationTokenSource.Token).ConfigureAwait(false);
}
- }
- finally
- {
- if (!released)
+ else
{
- transcodingLock.Release();
+ job = _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
+ if (job?.TranscodingThrottler is not null)
+ {
+ await job.TranscodingThrottler.UnpauseTranscoding().ConfigureAwait(false);
+ }
}
}
diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs
index a0bbc961f..e357588d1 100644
--- a/Jellyfin.Api/Controllers/LibraryController.cs
+++ b/Jellyfin.Api/Controllers/LibraryController.cs
@@ -7,7 +7,6 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Api.Attributes;
-using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
@@ -17,7 +16,6 @@ using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
using MediaBrowser.Common.Api;
using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -313,7 +311,7 @@ public class LibraryController : BaseJellyfinApiController
{
try
{
- await _libraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None).ConfigureAwait(false);
+ await _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
diff --git a/Jellyfin.Api/Controllers/LibraryStructureController.cs b/Jellyfin.Api/Controllers/LibraryStructureController.cs
index d483ca4d2..23c430f85 100644
--- a/Jellyfin.Api/Controllers/LibraryStructureController.cs
+++ b/Jellyfin.Api/Controllers/LibraryStructureController.cs
@@ -6,11 +6,9 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using Jellyfin.Api.Constants;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Api.Models.LibraryStructureDto;
using MediaBrowser.Common.Api;
-using MediaBrowser.Common.Progress;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@@ -180,7 +178,7 @@ public class LibraryStructureController : BaseJellyfinApiController
// No need to start if scanning the library because it will handle it
if (refreshLibrary)
{
- await _libraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None).ConfigureAwait(false);
+ await _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None).ConfigureAwait(false);
}
else
{
@@ -224,7 +222,7 @@ public class LibraryStructureController : BaseJellyfinApiController
// No need to start if scanning the library because it will handle it
if (refreshLibrary)
{
- await _libraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None).ConfigureAwait(false);
+ await _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None).ConfigureAwait(false);
}
else
{
@@ -293,7 +291,7 @@ public class LibraryStructureController : BaseJellyfinApiController
// No need to start if scanning the library because it will handle it
if (refreshLibrary)
{
- await _libraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None).ConfigureAwait(false);
+ await _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None).ConfigureAwait(false);
}
else
{
diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs
index 1b2f5750f..7f4cad951 100644
--- a/Jellyfin.Api/Controllers/LiveTvController.cs
+++ b/Jellyfin.Api/Controllers/LiveTvController.cs
@@ -43,7 +43,9 @@ namespace Jellyfin.Api.Controllers;
public class LiveTvController : BaseJellyfinApiController
{
private readonly ILiveTvManager _liveTvManager;
+ private readonly IGuideManager _guideManager;
private readonly ITunerHostManager _tunerHostManager;
+ private readonly IListingsManager _listingsManager;
private readonly IUserManager _userManager;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILibraryManager _libraryManager;
@@ -56,7 +58,9 @@ public class LiveTvController : BaseJellyfinApiController
/// Initializes a new instance of the <see cref="LiveTvController"/> class.
/// </summary>
/// <param name="liveTvManager">Instance of the <see cref="ILiveTvManager"/> interface.</param>
+ /// <param name="guideManager">Instance of the <see cref="IGuideManager"/> interface.</param>
/// <param name="tunerHostManager">Instance of the <see cref="ITunerHostManager"/> interface.</param>
+ /// <param name="listingsManager">Instance of the <see cref="IListingsManager"/> interface.</param>
/// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
/// <param name="httpClientFactory">Instance of the <see cref="IHttpClientFactory"/> interface.</param>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
@@ -66,7 +70,9 @@ public class LiveTvController : BaseJellyfinApiController
/// <param name="transcodeManager">Instance of the <see cref="ITranscodeManager"/> interface.</param>
public LiveTvController(
ILiveTvManager liveTvManager,
+ IGuideManager guideManager,
ITunerHostManager tunerHostManager,
+ IListingsManager listingsManager,
IUserManager userManager,
IHttpClientFactory httpClientFactory,
ILibraryManager libraryManager,
@@ -76,7 +82,9 @@ public class LiveTvController : BaseJellyfinApiController
ITranscodeManager transcodeManager)
{
_liveTvManager = liveTvManager;
+ _guideManager = guideManager;
_tunerHostManager = tunerHostManager;
+ _listingsManager = listingsManager;
_userManager = userManager;
_httpClientFactory = httpClientFactory;
_libraryManager = libraryManager;
@@ -941,9 +949,7 @@ public class LiveTvController : BaseJellyfinApiController
[Authorize(Policy = Policies.LiveTvAccess)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<GuideInfo> GetGuideInfo()
- {
- return _liveTvManager.GetGuideInfo();
- }
+ => _guideManager.GetGuideInfo();
/// <summary>
/// Adds a tuner host.
@@ -1013,7 +1019,7 @@ public class LiveTvController : BaseJellyfinApiController
listingsProviderInfo.Password = Convert.ToHexString(SHA1.HashData(Encoding.UTF8.GetBytes(pw))).ToLowerInvariant();
}
- return await _liveTvManager.SaveListingProvider(listingsProviderInfo, validateLogin, validateListings).ConfigureAwait(false);
+ return await _listingsManager.SaveListingProvider(listingsProviderInfo, validateLogin, validateListings).ConfigureAwait(false);
}
/// <summary>
@@ -1027,7 +1033,7 @@ public class LiveTvController : BaseJellyfinApiController
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult DeleteListingProvider([FromQuery] string? id)
{
- _liveTvManager.DeleteListingsProvider(id);
+ _listingsManager.DeleteListingsProvider(id);
return NoContent();
}
@@ -1048,9 +1054,7 @@ public class LiveTvController : BaseJellyfinApiController
[FromQuery] string? type,
[FromQuery] string? location,
[FromQuery] string? country)
- {
- return await _liveTvManager.GetLineups(type, id, country, location).ConfigureAwait(false);
- }
+ => await _listingsManager.GetLineups(type, id, country, location).ConfigureAwait(false);
/// <summary>
/// Gets available countries.
@@ -1081,48 +1085,20 @@ public class LiveTvController : BaseJellyfinApiController
[HttpGet("ChannelMappingOptions")]
[Authorize(Policy = Policies.LiveTvAccess)]
[ProducesResponseType(StatusCodes.Status200OK)]
- public async Task<ActionResult<ChannelMappingOptionsDto>> GetChannelMappingOptions([FromQuery] string? providerId)
- {
- var config = _configurationManager.GetConfiguration<LiveTvOptions>("livetv");
-
- var listingsProviderInfo = config.ListingProviders.First(i => string.Equals(providerId, i.Id, StringComparison.OrdinalIgnoreCase));
-
- var listingsProviderName = _liveTvManager.ListingProviders.First(i => string.Equals(i.Type, listingsProviderInfo.Type, StringComparison.OrdinalIgnoreCase)).Name;
-
- var tunerChannels = await _liveTvManager.GetChannelsForListingsProvider(providerId, CancellationToken.None)
- .ConfigureAwait(false);
-
- var providerChannels = await _liveTvManager.GetChannelsFromListingsProviderData(providerId, CancellationToken.None)
- .ConfigureAwait(false);
-
- var mappings = listingsProviderInfo.ChannelMappings;
-
- return new ChannelMappingOptionsDto
- {
- TunerChannels = tunerChannels.Select(i => _liveTvManager.GetTunerChannelMapping(i, mappings, providerChannels)).ToList(),
- ProviderChannels = providerChannels.Select(i => new NameIdPair
- {
- Name = i.Name,
- Id = i.Id
- }).ToList(),
- Mappings = mappings,
- ProviderName = listingsProviderName
- };
- }
+ public Task<ChannelMappingOptionsDto> GetChannelMappingOptions([FromQuery] string? providerId)
+ => _listingsManager.GetChannelMappingOptions(providerId);
/// <summary>
/// Set channel mappings.
/// </summary>
- /// <param name="setChannelMappingDto">The set channel mapping dto.</param>
+ /// <param name="dto">The set channel mapping dto.</param>
/// <response code="200">Created channel mapping returned.</response>
/// <returns>An <see cref="OkResult"/> containing the created channel mapping.</returns>
[HttpPost("ChannelMappings")]
[Authorize(Policy = Policies.LiveTvManagement)]
[ProducesResponseType(StatusCodes.Status200OK)]
- public async Task<ActionResult<TunerChannelMapping>> SetChannelMapping([FromBody, Required] SetChannelMappingDto setChannelMappingDto)
- {
- return await _liveTvManager.SetChannelMapping(setChannelMappingDto.ProviderId, setChannelMappingDto.TunerChannelId, setChannelMappingDto.ProviderChannelId).ConfigureAwait(false);
- }
+ public Task<TunerChannelMapping> SetChannelMapping([FromBody, Required] SetChannelMappingDto dto)
+ => _listingsManager.SetChannelMapping(dto.ProviderId, dto.TunerChannelId, dto.ProviderChannelId);
/// <summary>
/// Get tuner host types.
diff --git a/Jellyfin.Api/Controllers/SystemController.cs b/Jellyfin.Api/Controllers/SystemController.cs
index 3d4df0386..6c5ce4715 100644
--- a/Jellyfin.Api/Controllers/SystemController.cs
+++ b/Jellyfin.Api/Controllers/SystemController.cs
@@ -188,16 +188,24 @@ public class SystemController : BaseJellyfinApiController
/// <param name="name">The name of the log file to get.</param>
/// <response code="200">Log file retrieved.</response>
/// <response code="403">User does not have permission to get log files.</response>
+ /// <response code="404">Could not find a log file with the name.</response>
/// <returns>The log file.</returns>
[HttpGet("Logs/Log")]
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesFile(MediaTypeNames.Text.Plain)]
public ActionResult GetLogFile([FromQuery, Required] string name)
{
- var file = _fileSystem.GetFiles(_appPaths.LogDirectoryPath)
- .First(i => string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase));
+ var file = _fileSystem
+ .GetFiles(_appPaths.LogDirectoryPath)
+ .FirstOrDefault(i => string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase));
+
+ if (file is null)
+ {
+ return NotFound("Log file not found.");
+ }
// For older files, assume fully static
var fileShare = file.LastWriteTimeUtc < DateTime.UtcNow.AddHours(-1) ? FileShare.Read : FileShare.ReadWrite;
diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs
index e6c319869..b3029d6fa 100644
--- a/Jellyfin.Api/Controllers/VideosController.cs
+++ b/Jellyfin.Api/Controllers/VideosController.cs
@@ -458,10 +458,8 @@ public class VideosController : BaseJellyfinApiController
return BadRequest($"Input protocol {state.InputProtocol} cannot be streamed statically");
}
- var outputPath = state.OutputFilePath;
-
// Static stream
- if (@static.HasValue && @static.Value)
+ if (@static.HasValue && @static.Value && !(state.MediaSource.VideoType == VideoType.BluRay || state.MediaSource.VideoType == VideoType.Dvd))
{
var contentType = state.GetMimeType("." + state.OutputContainer, false) ?? state.GetMimeType(state.MediaPath);
@@ -478,7 +476,7 @@ public class VideosController : BaseJellyfinApiController
// Need to start ffmpeg (because media can't be returned directly)
var encodingOptions = _serverConfigurationManager.GetEncodingOptions();
- var ffmpegCommandLineArguments = _encodingHelper.GetProgressiveVideoFullCommandLine(state, encodingOptions, outputPath, "superfast");
+ var ffmpegCommandLineArguments = _encodingHelper.GetProgressiveVideoFullCommandLine(state, encodingOptions, "superfast");
return await FileStreamResponseHelpers.GetTranscodedFile(
state,
isHeadRequest,