浏览代码

Add ITranscodeManager service

Patrick Barron 1 年之前
父节点
当前提交
9215a4d40a

+ 2 - 1
Emby.Server.Implementations/ApplicationHost.cs

@@ -76,6 +76,7 @@ using MediaBrowser.Controller.TV;
 using MediaBrowser.LocalMetadata.Savers;
 using MediaBrowser.MediaEncoding.BdInfo;
 using MediaBrowser.MediaEncoding.Subtitles;
+using MediaBrowser.MediaEncoding.Transcoding;
 using MediaBrowser.Model.Cryptography;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.IO;
@@ -583,7 +584,7 @@ namespace Emby.Server.Implementations
 
             serviceCollection.AddSingleton<IAttachmentExtractor, MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor>();
 
-            serviceCollection.AddSingleton<TranscodingJobHelper>();
+            serviceCollection.AddSingleton<ITranscodeManager, TranscodeManager>();
             serviceCollection.AddScoped<MediaInfoHelper>();
             serviceCollection.AddScoped<AudioHelper>();
             serviceCollection.AddScoped<DynamicHlsHelper>();

+ 1 - 0
Jellyfin.Api/Controllers/AudioController.cs

@@ -6,6 +6,7 @@ using Jellyfin.Api.Attributes;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.Models.StreamingDtos;
 using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.Streaming;
 using MediaBrowser.Model.Dlna;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;

+ 24 - 22
Jellyfin.Api/Controllers/DynamicHlsController.cs

@@ -9,6 +9,7 @@ using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using Jellyfin.Api.Attributes;
+using Jellyfin.Api.Extensions;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.Models.StreamingDtos;
 using Jellyfin.Data.Enums;
@@ -18,6 +19,7 @@ using MediaBrowser.Common.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.Streaming;
 using MediaBrowser.MediaEncoding.Encoder;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Dlna;
@@ -50,7 +52,7 @@ public class DynamicHlsController : BaseJellyfinApiController
     private readonly IServerConfigurationManager _serverConfigurationManager;
     private readonly IMediaEncoder _mediaEncoder;
     private readonly IFileSystem _fileSystem;
-    private readonly TranscodingJobHelper _transcodingJobHelper;
+    private readonly ITranscodeManager _transcodeManager;
     private readonly ILogger<DynamicHlsController> _logger;
     private readonly EncodingHelper _encodingHelper;
     private readonly IDynamicHlsPlaylistGenerator _dynamicHlsPlaylistGenerator;
@@ -66,7 +68,7 @@ public class DynamicHlsController : BaseJellyfinApiController
     /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
     /// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
     /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
-    /// <param name="transcodingJobHelper">Instance of the <see cref="TranscodingJobHelper"/> class.</param>
+    /// <param name="transcodeManager">Instance of the <see cref="ITranscodeManager"/> interface.</param>
     /// <param name="logger">Instance of the <see cref="ILogger{DynamicHlsController}"/> interface.</param>
     /// <param name="dynamicHlsHelper">Instance of <see cref="DynamicHlsHelper"/>.</param>
     /// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</param>
@@ -78,7 +80,7 @@ public class DynamicHlsController : BaseJellyfinApiController
         IServerConfigurationManager serverConfigurationManager,
         IMediaEncoder mediaEncoder,
         IFileSystem fileSystem,
-        TranscodingJobHelper transcodingJobHelper,
+        ITranscodeManager transcodeManager,
         ILogger<DynamicHlsController> logger,
         DynamicHlsHelper dynamicHlsHelper,
         EncodingHelper encodingHelper,
@@ -90,7 +92,7 @@ public class DynamicHlsController : BaseJellyfinApiController
         _serverConfigurationManager = serverConfigurationManager;
         _mediaEncoder = mediaEncoder;
         _fileSystem = fileSystem;
-        _transcodingJobHelper = transcodingJobHelper;
+        _transcodeManager = transcodeManager;
         _logger = logger;
         _dynamicHlsHelper = dynamicHlsHelper;
         _encodingHelper = encodingHelper;
@@ -282,7 +284,7 @@ public class DynamicHlsController : BaseJellyfinApiController
                 _serverConfigurationManager,
                 _mediaEncoder,
                 _encodingHelper,
-                _transcodingJobHelper,
+                _transcodeManager,
                 TranscodingJobType,
                 cancellationToken)
             .ConfigureAwait(false);
@@ -292,7 +294,7 @@ public class DynamicHlsController : BaseJellyfinApiController
 
         if (!System.IO.File.Exists(playlistPath))
         {
-            var transcodingLock = _transcodingJobHelper.GetTranscodingLock(playlistPath);
+            var transcodingLock = _transcodeManager.GetTranscodingLock(playlistPath);
             await transcodingLock.WaitAsync(cancellationToken).ConfigureAwait(false);
             try
             {
@@ -301,11 +303,11 @@ public class DynamicHlsController : BaseJellyfinApiController
                     // If the playlist doesn't already exist, startup ffmpeg
                     try
                     {
-                        job = await _transcodingJobHelper.StartFfMpeg(
+                        job = await _transcodeManager.StartFfMpeg(
                                 state,
                                 playlistPath,
                                 GetCommandLineArguments(playlistPath, state, true, 0),
-                                Request,
+                                Request.HttpContext.User.GetUserId(),
                                 TranscodingJobType,
                                 cancellationTokenSource)
                             .ConfigureAwait(false);
@@ -330,11 +332,11 @@ public class DynamicHlsController : BaseJellyfinApiController
             }
         }
 
-        job ??= _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
+        job ??= _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
 
         if (job is not null)
         {
-            _transcodingJobHelper.OnTranscodeEndRequest(job);
+            _transcodeManager.OnTranscodeEndRequest(job);
         }
 
         var playlistText = HlsHelpers.GetLivePlaylistText(playlistPath, state);
@@ -1382,7 +1384,7 @@ public class DynamicHlsController : BaseJellyfinApiController
                 _serverConfigurationManager,
                 _mediaEncoder,
                 _encodingHelper,
-                _transcodingJobHelper,
+                _transcodeManager,
                 TranscodingJobType,
                 cancellationTokenSource.Token)
             .ConfigureAwait(false);
@@ -1420,7 +1422,7 @@ public class DynamicHlsController : BaseJellyfinApiController
                 _serverConfigurationManager,
                 _mediaEncoder,
                 _encodingHelper,
-                _transcodingJobHelper,
+                _transcodeManager,
                 TranscodingJobType,
                 cancellationToken)
             .ConfigureAwait(false);
@@ -1435,12 +1437,12 @@ public class DynamicHlsController : BaseJellyfinApiController
 
         if (System.IO.File.Exists(segmentPath))
         {
-            job = _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
+            job = _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
             _logger.LogDebug("returning {0} [it exists, try 1]", segmentPath);
             return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, segmentId, job, cancellationToken).ConfigureAwait(false);
         }
 
-        var transcodingLock = _transcodingJobHelper.GetTranscodingLock(playlistPath);
+        var transcodingLock = _transcodeManager.GetTranscodingLock(playlistPath);
         await transcodingLock.WaitAsync(cancellationToken).ConfigureAwait(false);
         var released = false;
         var startTranscoding = false;
@@ -1449,7 +1451,7 @@ public class DynamicHlsController : BaseJellyfinApiController
         {
             if (System.IO.File.Exists(segmentPath))
             {
-                job = _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
+                job = _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
                 transcodingLock.Release();
                 released = true;
                 _logger.LogDebug("returning {0} [it exists, try 2]", segmentPath);
@@ -1487,7 +1489,7 @@ public class DynamicHlsController : BaseJellyfinApiController
                     // If the playlist doesn't already exist, startup ffmpeg
                     try
                     {
-                        await _transcodingJobHelper.KillTranscodingJobs(streamingRequest.DeviceId, streamingRequest.PlaySessionId, p => false)
+                        await _transcodeManager.KillTranscodingJobs(streamingRequest.DeviceId, streamingRequest.PlaySessionId, p => false)
                             .ConfigureAwait(false);
 
                         if (currentTranscodingIndex.HasValue)
@@ -1498,11 +1500,11 @@ public class DynamicHlsController : BaseJellyfinApiController
                         streamingRequest.StartTimeTicks = streamingRequest.CurrentRuntimeTicks;
 
                         state.WaitForPath = segmentPath;
-                        job = await _transcodingJobHelper.StartFfMpeg(
+                        job = await _transcodeManager.StartFfMpeg(
                             state,
                             playlistPath,
                             GetCommandLineArguments(playlistPath, state, false, segmentId),
-                            Request,
+                            Request.HttpContext.User.GetUserId(),
                             TranscodingJobType,
                             cancellationTokenSource).ConfigureAwait(false);
                     }
@@ -1516,7 +1518,7 @@ public class DynamicHlsController : BaseJellyfinApiController
                 }
                 else
                 {
-                    job = _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
+                    job = _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
                     if (job?.TranscodingThrottler is not null)
                     {
                         await job.TranscodingThrottler.UnpauseTranscoding().ConfigureAwait(false);
@@ -1533,7 +1535,7 @@ public class DynamicHlsController : BaseJellyfinApiController
         }
 
         _logger.LogDebug("returning {0} [general case]", segmentPath);
-        job ??= _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
+        job ??= _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
         return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, segmentId, job, cancellationToken).ConfigureAwait(false);
     }
 
@@ -2000,7 +2002,7 @@ public class DynamicHlsController : BaseJellyfinApiController
             if (transcodingJob is not null)
             {
                 transcodingJob.DownloadPositionTicks = Math.Max(transcodingJob.DownloadPositionTicks ?? segmentEndingPositionTicks, segmentEndingPositionTicks);
-                _transcodingJobHelper.OnTranscodeEndRequest(transcodingJob);
+                _transcodeManager.OnTranscodeEndRequest(transcodingJob);
             }
 
             return Task.CompletedTask;
@@ -2011,7 +2013,7 @@ public class DynamicHlsController : BaseJellyfinApiController
 
     private int? GetCurrentTranscodingIndex(string playlist, string segmentExtension)
     {
-        var job = _transcodingJobHelper.GetTranscodingJob(playlist, TranscodingJobType);
+        var job = _transcodeManager.GetTranscodingJob(playlist, TranscodingJobType);
 
         if (job is null || job.HasExited)
         {

+ 7 - 7
Jellyfin.Api/Controllers/HlsSegmentController.cs

@@ -24,22 +24,22 @@ public class HlsSegmentController : BaseJellyfinApiController
 {
     private readonly IFileSystem _fileSystem;
     private readonly IServerConfigurationManager _serverConfigurationManager;
-    private readonly TranscodingJobHelper _transcodingJobHelper;
+    private readonly ITranscodeManager _transcodeManager;
 
     /// <summary>
     /// Initializes a new instance of the <see cref="HlsSegmentController"/> class.
     /// </summary>
     /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
     /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
-    /// <param name="transcodingJobHelper">Initialized instance of the <see cref="TranscodingJobHelper"/>.</param>
+    /// <param name="transcodeManager">Instance of the <see cref="ITranscodeManager"/> interface.</param>
     public HlsSegmentController(
         IFileSystem fileSystem,
         IServerConfigurationManager serverConfigurationManager,
-        TranscodingJobHelper transcodingJobHelper)
+        ITranscodeManager transcodeManager)
     {
         _fileSystem = fileSystem;
         _serverConfigurationManager = serverConfigurationManager;
-        _transcodingJobHelper = transcodingJobHelper;
+        _transcodeManager = transcodeManager;
     }
 
     /// <summary>
@@ -112,7 +112,7 @@ public class HlsSegmentController : BaseJellyfinApiController
         [FromQuery, Required] string deviceId,
         [FromQuery, Required] string playSessionId)
     {
-        _transcodingJobHelper.KillTranscodingJobs(deviceId, playSessionId, path => true);
+        _transcodeManager.KillTranscodingJobs(deviceId, playSessionId, _ => true);
         return NoContent();
     }
 
@@ -174,13 +174,13 @@ public class HlsSegmentController : BaseJellyfinApiController
 
     private ActionResult GetFileResult(string path, string playlistPath)
     {
-        var transcodingJob = _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, TranscodingJobType.Hls);
+        var transcodingJob = _transcodeManager.OnTranscodeBeginRequest(playlistPath, TranscodingJobType.Hls);
 
         Response.OnCompleted(() =>
         {
             if (transcodingJob is not null)
             {
-                _transcodingJobHelper.OnTranscodeEndRequest(transcodingJob);
+                _transcodeManager.OnTranscodeEndRequest(transcodingJob);
             }
 
             return Task.CompletedTask;

+ 6 - 5
Jellyfin.Api/Controllers/LiveTvController.cs

@@ -24,6 +24,7 @@ using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.LiveTv;
@@ -47,7 +48,7 @@ public class LiveTvController : BaseJellyfinApiController
     private readonly IDtoService _dtoService;
     private readonly IMediaSourceManager _mediaSourceManager;
     private readonly IConfigurationManager _configurationManager;
-    private readonly TranscodingJobHelper _transcodingJobHelper;
+    private readonly ITranscodeManager _transcodeManager;
 
     /// <summary>
     /// Initializes a new instance of the <see cref="LiveTvController"/> class.
@@ -59,7 +60,7 @@ public class LiveTvController : BaseJellyfinApiController
     /// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param>
     /// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
     /// <param name="configurationManager">Instance of the <see cref="IConfigurationManager"/> interface.</param>
-    /// <param name="transcodingJobHelper">Instance of the <see cref="TranscodingJobHelper"/> class.</param>
+    /// <param name="transcodeManager">Instance of the <see cref="ITranscodeManager"/> interface.</param>
     public LiveTvController(
         ILiveTvManager liveTvManager,
         IUserManager userManager,
@@ -68,7 +69,7 @@ public class LiveTvController : BaseJellyfinApiController
         IDtoService dtoService,
         IMediaSourceManager mediaSourceManager,
         IConfigurationManager configurationManager,
-        TranscodingJobHelper transcodingJobHelper)
+        ITranscodeManager transcodeManager)
     {
         _liveTvManager = liveTvManager;
         _userManager = userManager;
@@ -77,7 +78,7 @@ public class LiveTvController : BaseJellyfinApiController
         _dtoService = dtoService;
         _mediaSourceManager = mediaSourceManager;
         _configurationManager = configurationManager;
-        _transcodingJobHelper = transcodingJobHelper;
+        _transcodeManager = transcodeManager;
     }
 
     /// <summary>
@@ -1171,7 +1172,7 @@ public class LiveTvController : BaseJellyfinApiController
             return NotFound();
         }
 
-        var stream = new ProgressiveFileStream(path, null, _transcodingJobHelper);
+        var stream = new ProgressiveFileStream(path, null, _transcodeManager);
         return new FileStreamResult(stream, MimeTypes.GetMimeType(path));
     }
 

+ 9 - 8
Jellyfin.Api/Controllers/PlaystateController.cs

@@ -8,6 +8,7 @@ using Jellyfin.Api.ModelBinders;
 using Jellyfin.Data.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Controller.Session;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Session;
@@ -30,7 +31,7 @@ public class PlaystateController : BaseJellyfinApiController
     private readonly ILibraryManager _libraryManager;
     private readonly ISessionManager _sessionManager;
     private readonly ILogger<PlaystateController> _logger;
-    private readonly TranscodingJobHelper _transcodingJobHelper;
+    private readonly ITranscodeManager _transcodeManager;
 
     /// <summary>
     /// Initializes a new instance of the <see cref="PlaystateController"/> class.
@@ -40,14 +41,14 @@ public class PlaystateController : BaseJellyfinApiController
     /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
     /// <param name="sessionManager">Instance of the <see cref="ISessionManager"/> interface.</param>
     /// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/> interface.</param>
-    /// <param name="transcodingJobHelper">Th <see cref="TranscodingJobHelper"/> singleton.</param>
+    /// <param name="transcodeManager">Instance of the <see cref="ITranscodeManager"/> interface.</param>
     public PlaystateController(
         IUserManager userManager,
         IUserDataManager userDataRepository,
         ILibraryManager libraryManager,
         ISessionManager sessionManager,
         ILoggerFactory loggerFactory,
-        TranscodingJobHelper transcodingJobHelper)
+        ITranscodeManager transcodeManager)
     {
         _userManager = userManager;
         _userDataRepository = userDataRepository;
@@ -55,7 +56,7 @@ public class PlaystateController : BaseJellyfinApiController
         _sessionManager = sessionManager;
         _logger = loggerFactory.CreateLogger<PlaystateController>();
 
-        _transcodingJobHelper = transcodingJobHelper;
+        _transcodeManager = transcodeManager;
     }
 
     /// <summary>
@@ -188,7 +189,7 @@ public class PlaystateController : BaseJellyfinApiController
     [ProducesResponseType(StatusCodes.Status204NoContent)]
     public ActionResult PingPlaybackSession([FromQuery, Required] string playSessionId)
     {
-        _transcodingJobHelper.PingTranscodingJob(playSessionId, null);
+        _transcodeManager.PingTranscodingJob(playSessionId, null);
         return NoContent();
     }
 
@@ -205,7 +206,7 @@ public class PlaystateController : BaseJellyfinApiController
         _logger.LogDebug("ReportPlaybackStopped PlaySessionId: {0}", playbackStopInfo.PlaySessionId ?? string.Empty);
         if (!string.IsNullOrWhiteSpace(playbackStopInfo.PlaySessionId))
         {
-            await _transcodingJobHelper.KillTranscodingJobs(User.GetDeviceId()!, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false);
+            await _transcodeManager.KillTranscodingJobs(User.GetDeviceId()!, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false);
         }
 
         playbackStopInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);
@@ -354,7 +355,7 @@ public class PlaystateController : BaseJellyfinApiController
         _logger.LogDebug("ReportPlaybackStopped PlaySessionId: {0}", playbackStopInfo.PlaySessionId ?? string.Empty);
         if (!string.IsNullOrWhiteSpace(playbackStopInfo.PlaySessionId))
         {
-            await _transcodingJobHelper.KillTranscodingJobs(User.GetDeviceId()!, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false);
+            await _transcodeManager.KillTranscodingJobs(User.GetDeviceId()!, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false);
         }
 
         playbackStopInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);
@@ -388,7 +389,7 @@ public class PlaystateController : BaseJellyfinApiController
     {
         if (method == PlayMethod.Transcode)
         {
-            var job = string.IsNullOrWhiteSpace(playSessionId) ? null : _transcodingJobHelper.GetTranscodingJob(playSessionId);
+            var job = string.IsNullOrWhiteSpace(playSessionId) ? null : _transcodeManager.GetTranscodingJob(playSessionId);
             if (job is null)
             {
                 return PlayMethod.DirectPlay;

+ 1 - 0
Jellyfin.Api/Controllers/UniversalAudioController.cs

@@ -11,6 +11,7 @@ using Jellyfin.Api.Models.StreamingDtos;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.Streaming;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.MediaInfo;
 using Microsoft.AspNetCore.Authorization;

+ 8 - 8
Jellyfin.Api/Controllers/VideosController.cs

@@ -11,7 +11,6 @@ using Jellyfin.Api.Constants;
 using Jellyfin.Api.Extensions;
 using Jellyfin.Api.Helpers;
 using Jellyfin.Api.ModelBinders;
-using Jellyfin.Api.Models.StreamingDtos;
 using MediaBrowser.Common.Api;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Net;
@@ -20,6 +19,7 @@ using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.Streaming;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
@@ -43,7 +43,7 @@ public class VideosController : BaseJellyfinApiController
     private readonly IMediaSourceManager _mediaSourceManager;
     private readonly IServerConfigurationManager _serverConfigurationManager;
     private readonly IMediaEncoder _mediaEncoder;
-    private readonly TranscodingJobHelper _transcodingJobHelper;
+    private readonly ITranscodeManager _transcodeManager;
     private readonly IHttpClientFactory _httpClientFactory;
     private readonly EncodingHelper _encodingHelper;
 
@@ -58,7 +58,7 @@ public class VideosController : BaseJellyfinApiController
     /// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
     /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
     /// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
-    /// <param name="transcodingJobHelper">Instance of the <see cref="TranscodingJobHelper"/> class.</param>
+    /// <param name="transcodeManager">Instance of the <see cref="ITranscodeManager"/> interface.</param>
     /// <param name="httpClientFactory">Instance of the <see cref="IHttpClientFactory"/> interface.</param>
     /// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</param>
     public VideosController(
@@ -68,7 +68,7 @@ public class VideosController : BaseJellyfinApiController
         IMediaSourceManager mediaSourceManager,
         IServerConfigurationManager serverConfigurationManager,
         IMediaEncoder mediaEncoder,
-        TranscodingJobHelper transcodingJobHelper,
+        ITranscodeManager transcodeManager,
         IHttpClientFactory httpClientFactory,
         EncodingHelper encodingHelper)
     {
@@ -78,7 +78,7 @@ public class VideosController : BaseJellyfinApiController
         _mediaSourceManager = mediaSourceManager;
         _serverConfigurationManager = serverConfigurationManager;
         _mediaEncoder = mediaEncoder;
-        _transcodingJobHelper = transcodingJobHelper;
+        _transcodeManager = transcodeManager;
         _httpClientFactory = httpClientFactory;
         _encodingHelper = encodingHelper;
     }
@@ -427,7 +427,7 @@ public class VideosController : BaseJellyfinApiController
                 _serverConfigurationManager,
                 _mediaEncoder,
                 _encodingHelper,
-                _transcodingJobHelper,
+                _transcodeManager,
                 _transcodingJobType,
                 cancellationTokenSource.Token)
             .ConfigureAwait(false);
@@ -466,7 +466,7 @@ public class VideosController : BaseJellyfinApiController
 
             if (state.MediaSource.IsInfiniteStream)
             {
-                var liveStream = new ProgressiveFileStream(state.MediaPath, null, _transcodingJobHelper);
+                var liveStream = new ProgressiveFileStream(state.MediaPath, null, _transcodeManager);
                 return File(liveStream, contentType);
             }
 
@@ -482,7 +482,7 @@ public class VideosController : BaseJellyfinApiController
             state,
             isHeadRequest,
             HttpContext,
-            _transcodingJobHelper,
+            _transcodeManager,
             ffmpegCommandLineArguments,
             _transcodingJobType,
             cancellationTokenSource).ConfigureAwait(false);

+ 8 - 8
Jellyfin.Api/Helpers/AudioHelper.cs

@@ -2,13 +2,13 @@
 using System.Net.Http;
 using System.Threading;
 using System.Threading.Tasks;
-using Jellyfin.Api.Models.StreamingDtos;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.Streaming;
 using MediaBrowser.Model.MediaInfo;
 using MediaBrowser.Model.Net;
 using Microsoft.AspNetCore.Http;
@@ -26,7 +26,7 @@ public class AudioHelper
     private readonly IMediaSourceManager _mediaSourceManager;
     private readonly IServerConfigurationManager _serverConfigurationManager;
     private readonly IMediaEncoder _mediaEncoder;
-    private readonly TranscodingJobHelper _transcodingJobHelper;
+    private readonly ITranscodeManager _transcodeManager;
     private readonly IHttpClientFactory _httpClientFactory;
     private readonly IHttpContextAccessor _httpContextAccessor;
     private readonly EncodingHelper _encodingHelper;
@@ -39,7 +39,7 @@ public class AudioHelper
     /// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
     /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
     /// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
-    /// <param name="transcodingJobHelper">Instance of <see cref="TranscodingJobHelper"/>.</param>
+    /// <param name="transcodeManager">Instance of <see cref="ITranscodeManager"/> interface.</param>
     /// <param name="httpClientFactory">Instance of the <see cref="IHttpClientFactory"/> interface.</param>
     /// <param name="httpContextAccessor">Instance of the <see cref="IHttpContextAccessor"/> interface.</param>
     /// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</param>
@@ -49,7 +49,7 @@ public class AudioHelper
         IMediaSourceManager mediaSourceManager,
         IServerConfigurationManager serverConfigurationManager,
         IMediaEncoder mediaEncoder,
-        TranscodingJobHelper transcodingJobHelper,
+        ITranscodeManager transcodeManager,
         IHttpClientFactory httpClientFactory,
         IHttpContextAccessor httpContextAccessor,
         EncodingHelper encodingHelper)
@@ -59,7 +59,7 @@ public class AudioHelper
         _mediaSourceManager = mediaSourceManager;
         _serverConfigurationManager = serverConfigurationManager;
         _mediaEncoder = mediaEncoder;
-        _transcodingJobHelper = transcodingJobHelper;
+        _transcodeManager = transcodeManager;
         _httpClientFactory = httpClientFactory;
         _httpContextAccessor = httpContextAccessor;
         _encodingHelper = encodingHelper;
@@ -94,7 +94,7 @@ public class AudioHelper
                 _serverConfigurationManager,
                 _mediaEncoder,
                 _encodingHelper,
-                _transcodingJobHelper,
+                _transcodeManager,
                 transcodingJobType,
                 cancellationTokenSource.Token)
             .ConfigureAwait(false);
@@ -133,7 +133,7 @@ public class AudioHelper
 
             if (state.MediaSource.IsInfiniteStream)
             {
-                var stream = new ProgressiveFileStream(state.MediaPath, null, _transcodingJobHelper);
+                var stream = new ProgressiveFileStream(state.MediaPath, null, _transcodeManager);
                 return new FileStreamResult(stream, contentType);
             }
 
@@ -149,7 +149,7 @@ public class AudioHelper
             state,
             isHeadRequest,
             _httpContextAccessor.HttpContext,
-            _transcodingJobHelper,
+            _transcodeManager,
             ffmpegCommandLineArguments,
             transcodingJobType,
             cancellationTokenSource).ConfigureAwait(false);

+ 6 - 6
Jellyfin.Api/Helpers/DynamicHlsHelper.cs

@@ -8,7 +8,6 @@ using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using Jellyfin.Api.Extensions;
-using Jellyfin.Api.Models.StreamingDtos;
 using Jellyfin.Data.Entities;
 using Jellyfin.Data.Enums;
 using Jellyfin.Extensions;
@@ -18,6 +17,7 @@ using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.Streaming;
 using MediaBrowser.Controller.Trickplay;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Entities;
@@ -39,7 +39,7 @@ public class DynamicHlsHelper
     private readonly IMediaSourceManager _mediaSourceManager;
     private readonly IServerConfigurationManager _serverConfigurationManager;
     private readonly IMediaEncoder _mediaEncoder;
-    private readonly TranscodingJobHelper _transcodingJobHelper;
+    private readonly ITranscodeManager _transcodeManager;
     private readonly INetworkManager _networkManager;
     private readonly ILogger<DynamicHlsHelper> _logger;
     private readonly IHttpContextAccessor _httpContextAccessor;
@@ -54,7 +54,7 @@ public class DynamicHlsHelper
     /// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
     /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
     /// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
-    /// <param name="transcodingJobHelper">Instance of <see cref="TranscodingJobHelper"/>.</param>
+    /// <param name="transcodeManager">Instance of <see cref="ITranscodeManager"/>.</param>
     /// <param name="networkManager">Instance of the <see cref="INetworkManager"/> interface.</param>
     /// <param name="logger">Instance of the <see cref="ILogger{DynamicHlsHelper}"/> interface.</param>
     /// <param name="httpContextAccessor">Instance of the <see cref="IHttpContextAccessor"/> interface.</param>
@@ -66,7 +66,7 @@ public class DynamicHlsHelper
         IMediaSourceManager mediaSourceManager,
         IServerConfigurationManager serverConfigurationManager,
         IMediaEncoder mediaEncoder,
-        TranscodingJobHelper transcodingJobHelper,
+        ITranscodeManager transcodeManager,
         INetworkManager networkManager,
         ILogger<DynamicHlsHelper> logger,
         IHttpContextAccessor httpContextAccessor,
@@ -78,7 +78,7 @@ public class DynamicHlsHelper
         _mediaSourceManager = mediaSourceManager;
         _serverConfigurationManager = serverConfigurationManager;
         _mediaEncoder = mediaEncoder;
-        _transcodingJobHelper = transcodingJobHelper;
+        _transcodeManager = transcodeManager;
         _networkManager = networkManager;
         _logger = logger;
         _httpContextAccessor = httpContextAccessor;
@@ -130,7 +130,7 @@ public class DynamicHlsHelper
                 _serverConfigurationManager,
                 _mediaEncoder,
                 _encodingHelper,
-                _transcodingJobHelper,
+                _transcodeManager,
                 transcodingJobType,
                 cancellationTokenSource.Token)
             .ConfigureAwait(false);

+ 14 - 7
Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs

@@ -4,8 +4,9 @@ using System.Net.Http;
 using System.Net.Mime;
 using System.Threading;
 using System.Threading.Tasks;
-using Jellyfin.Api.Models.StreamingDtos;
+using Jellyfin.Api.Extensions;
 using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.Streaming;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Net.Http.Headers;
@@ -64,7 +65,7 @@ public static class FileStreamResponseHelpers
     /// <param name="state">The current <see cref="StreamState"/>.</param>
     /// <param name="isHeadRequest">Whether the current request is a HTTP HEAD request so only the headers get returned.</param>
     /// <param name="httpContext">The current http context.</param>
-    /// <param name="transcodingJobHelper">The <see cref="TranscodingJobHelper"/> singleton.</param>
+    /// <param name="transcodeManager">The <see cref="ITranscodeManager"/> singleton.</param>
     /// <param name="ffmpegCommandLineArguments">The command line arguments to start ffmpeg.</param>
     /// <param name="transcodingJobType">The <see cref="TranscodingJobType"/>.</param>
     /// <param name="cancellationTokenSource">The <see cref="CancellationTokenSource"/>.</param>
@@ -73,7 +74,7 @@ public static class FileStreamResponseHelpers
         StreamState state,
         bool isHeadRequest,
         HttpContext httpContext,
-        TranscodingJobHelper transcodingJobHelper,
+        ITranscodeManager transcodeManager,
         string ffmpegCommandLineArguments,
         TranscodingJobType transcodingJobType,
         CancellationTokenSource cancellationTokenSource)
@@ -92,22 +93,28 @@ public static class FileStreamResponseHelpers
             return new OkResult();
         }
 
-        var transcodingLock = transcodingJobHelper.GetTranscodingLock(outputPath);
+        var transcodingLock = transcodeManager.GetTranscodingLock(outputPath);
         await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
         try
         {
             TranscodingJob? job;
             if (!File.Exists(outputPath))
             {
-                job = await transcodingJobHelper.StartFfMpeg(state, outputPath, ffmpegCommandLineArguments, httpContext.Request, transcodingJobType, cancellationTokenSource).ConfigureAwait(false);
+                job = await transcodeManager.StartFfMpeg(
+                    state,
+                    outputPath,
+                    ffmpegCommandLineArguments,
+                    httpContext.User.GetUserId(),
+                    transcodingJobType,
+                    cancellationTokenSource).ConfigureAwait(false);
             }
             else
             {
-                job = transcodingJobHelper.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive);
+                job = transcodeManager.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive);
                 state.Dispose();
             }
 
-            var stream = new ProgressiveFileStream(outputPath, job, transcodingJobHelper);
+            var stream = new ProgressiveFileStream(outputPath, job, transcodeManager);
             return new FileStreamResult(stream, contentType);
         }
         finally

+ 1 - 0
Jellyfin.Api/Helpers/HlsHelpers.cs

@@ -5,6 +5,7 @@ using System.Threading;
 using System.Threading.Tasks;
 using Jellyfin.Api.Models.StreamingDtos;
 using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.Streaming;
 using MediaBrowser.Model.IO;
 using Microsoft.Extensions.Logging;
 

+ 6 - 6
Jellyfin.Api/Helpers/ProgressiveFileStream.cs

@@ -15,7 +15,7 @@ public class ProgressiveFileStream : Stream
 {
     private readonly Stream _stream;
     private readonly TranscodingJob? _job;
-    private readonly TranscodingJobHelper? _transcodingJobHelper;
+    private readonly ITranscodeManager? _transcodeManager;
     private readonly int _timeoutMs;
     private bool _disposed;
 
@@ -24,12 +24,12 @@ public class ProgressiveFileStream : Stream
     /// </summary>
     /// <param name="filePath">The path to the transcoded file.</param>
     /// <param name="job">The transcoding job information.</param>
-    /// <param name="transcodingJobHelper">The transcoding job helper.</param>
+    /// <param name="transcodeManager">The transcode manager.</param>
     /// <param name="timeoutMs">The timeout duration in milliseconds.</param>
-    public ProgressiveFileStream(string filePath, TranscodingJob? job, TranscodingJobHelper transcodingJobHelper, int timeoutMs = 30000)
+    public ProgressiveFileStream(string filePath, TranscodingJob? job, ITranscodeManager transcodeManager, int timeoutMs = 30000)
     {
         _job = job;
-        _transcodingJobHelper = transcodingJobHelper;
+        _transcodeManager = transcodeManager;
         _timeoutMs = timeoutMs;
 
         _stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous | FileOptions.SequentialScan);
@@ -43,7 +43,7 @@ public class ProgressiveFileStream : Stream
     public ProgressiveFileStream(Stream stream, int timeoutMs = 30000)
     {
         _job = null;
-        _transcodingJobHelper = null;
+        _transcodeManager = null;
         _timeoutMs = timeoutMs;
         _stream = stream;
     }
@@ -153,7 +153,7 @@ public class ProgressiveFileStream : Stream
 
                 if (_job is not null)
                 {
-                    _transcodingJobHelper?.OnTranscodeEndRequest(_job);
+                    _transcodeManager?.OnTranscodeEndRequest(_job);
                 }
             }
         }

+ 5 - 5
Jellyfin.Api/Helpers/StreamingHelpers.cs

@@ -6,7 +6,6 @@ using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 using Jellyfin.Api.Extensions;
-using Jellyfin.Api.Models.StreamingDtos;
 using Jellyfin.Data.Enums;
 using Jellyfin.Extensions;
 using MediaBrowser.Common.Configuration;
@@ -14,6 +13,7 @@ using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.Streaming;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
@@ -38,7 +38,7 @@ public static class StreamingHelpers
     /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
     /// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
     /// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</param>
-    /// <param name="transcodingJobHelper">Initialized <see cref="TranscodingJobHelper"/>.</param>
+    /// <param name="transcodeManager">Instance of the <see cref="ITranscodeManager"/> interface.</param>
     /// <param name="transcodingJobType">The <see cref="TranscodingJobType"/>.</param>
     /// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param>
     /// <returns>A <see cref="Task"/> containing the current <see cref="StreamState"/>.</returns>
@@ -51,7 +51,7 @@ public static class StreamingHelpers
         IServerConfigurationManager serverConfigurationManager,
         IMediaEncoder mediaEncoder,
         EncodingHelper encodingHelper,
-        TranscodingJobHelper transcodingJobHelper,
+        ITranscodeManager transcodeManager,
         TranscodingJobType transcodingJobType,
         CancellationToken cancellationToken)
     {
@@ -74,7 +74,7 @@ public static class StreamingHelpers
             streamingRequest.AudioCodec = encodingHelper.InferAudioCodec(url);
         }
 
-        var state = new StreamState(mediaSourceManager, transcodingJobType, transcodingJobHelper)
+        var state = new StreamState(mediaSourceManager, transcodingJobType, transcodeManager)
         {
             Request = streamingRequest,
             RequestedUrl = url,
@@ -115,7 +115,7 @@ public static class StreamingHelpers
         if (string.IsNullOrWhiteSpace(streamingRequest.LiveStreamId))
         {
             var currentJob = !string.IsNullOrWhiteSpace(streamingRequest.PlaySessionId)
-                ? transcodingJobHelper.GetTranscodingJob(streamingRequest.PlaySessionId)
+                ? transcodeManager.GetTranscodingJob(streamingRequest.PlaySessionId)
                 : null;
 
             if (currentJob is not null)

+ 3 - 1
Jellyfin.Api/Models/StreamingDtos/HlsAudioRequestDto.cs

@@ -1,4 +1,6 @@
-namespace Jellyfin.Api.Models.StreamingDtos;
+using MediaBrowser.Controller.Streaming;
+
+namespace Jellyfin.Api.Models.StreamingDtos;
 
 /// <summary>
 /// The hls video request dto.

+ 3 - 1
Jellyfin.Api/Models/StreamingDtos/HlsVideoRequestDto.cs

@@ -1,4 +1,6 @@
-namespace Jellyfin.Api.Models.StreamingDtos;
+using MediaBrowser.Controller.Streaming;
+
+namespace Jellyfin.Api.Models.StreamingDtos;
 
 /// <summary>
 /// The hls video request dto.

+ 104 - 0
MediaBrowser.Controller/MediaEncoding/ITranscodeManager.cs

@@ -0,0 +1,104 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Streaming;
+
+namespace MediaBrowser.Controller.MediaEncoding;
+
+/// <summary>
+/// A service for managing media transcoding.
+/// </summary>
+public interface ITranscodeManager
+{
+    /// <summary>
+    /// Get transcoding job.
+    /// </summary>
+    /// <param name="playSessionId">Playback session id.</param>
+    /// <returns>The transcoding job.</returns>
+    public TranscodingJob? GetTranscodingJob(string playSessionId);
+
+    /// <summary>
+    /// Get transcoding job.
+    /// </summary>
+    /// <param name="path">Path to the transcoding file.</param>
+    /// <param name="type">The <see cref="TranscodingJobType"/>.</param>
+    /// <returns>The transcoding job.</returns>
+    public TranscodingJob? GetTranscodingJob(string path, TranscodingJobType type);
+
+    /// <summary>
+    /// Ping transcoding job.
+    /// </summary>
+    /// <param name="playSessionId">Play session id.</param>
+    /// <param name="isUserPaused">Is user paused.</param>
+    /// <exception cref="ArgumentNullException">Play session id is null.</exception>
+    public void PingTranscodingJob(string playSessionId, bool? isUserPaused);
+
+    /// <summary>
+    /// Kills the single transcoding job.
+    /// </summary>
+    /// <param name="deviceId">The device id.</param>
+    /// <param name="playSessionId">The play session identifier.</param>
+    /// <param name="deleteFiles">The delete files.</param>
+    /// <returns>Task.</returns>
+    public Task KillTranscodingJobs(string deviceId, string? playSessionId, Func<string, bool> deleteFiles);
+
+    /// <summary>
+    /// Report the transcoding progress to the session manager.
+    /// </summary>
+    /// <param name="job">The <see cref="TranscodingJob"/> of which the progress will be reported.</param>
+    /// <param name="state">The <see cref="StreamState"/> of the current transcoding job.</param>
+    /// <param name="transcodingPosition">The current transcoding position.</param>
+    /// <param name="framerate">The framerate of the transcoding job.</param>
+    /// <param name="percentComplete">The completion percentage of the transcode.</param>
+    /// <param name="bytesTranscoded">The number of bytes transcoded.</param>
+    /// <param name="bitRate">The bitrate of the transcoding job.</param>
+    public void ReportTranscodingProgress(
+        TranscodingJob job,
+        StreamState state,
+        TimeSpan? transcodingPosition,
+        float? framerate,
+        double? percentComplete,
+        long? bytesTranscoded,
+        int? bitRate);
+
+    /// <summary>
+    /// Starts FFMpeg.
+    /// </summary>
+    /// <param name="state">The state.</param>
+    /// <param name="outputPath">The output path.</param>
+    /// <param name="commandLineArguments">The command line arguments for FFmpeg.</param>
+    /// <param name="userId">The user id.</param>
+    /// <param name="transcodingJobType">The <see cref="TranscodingJobType"/>.</param>
+    /// <param name="cancellationTokenSource">The cancellation token source.</param>
+    /// <param name="workingDirectory">The working directory.</param>
+    /// <returns>Task.</returns>
+    public Task<TranscodingJob> StartFfMpeg(
+        StreamState state,
+        string outputPath,
+        string commandLineArguments,
+        Guid userId,
+        TranscodingJobType transcodingJobType,
+        CancellationTokenSource cancellationTokenSource,
+        string? workingDirectory = null);
+
+    /// <summary>
+    /// Called when [transcode begin request].
+    /// </summary>
+    /// <param name="path">The path.</param>
+    /// <param name="type">The type.</param>
+    /// <returns>The <see cref="TranscodingJob"/>.</returns>
+    public TranscodingJob? OnTranscodeBeginRequest(string path, TranscodingJobType type);
+
+    /// <summary>
+    /// Called when [transcode end].
+    /// </summary>
+    /// <param name="job">The transcode job.</param>
+    public void OnTranscodeEndRequest(TranscodingJob job);
+
+    /// <summary>
+    /// Gets the transcoding lock.
+    /// </summary>
+    /// <param name="outputPath">The output path of the transcoded file.</param>
+    /// <returns>A <see cref="SemaphoreSlim"/>.</returns>
+    public SemaphoreSlim GetTranscodingLock(string outputPath);
+}

+ 6 - 7
Jellyfin.Api/Models/StreamingDtos/StreamState.cs → MediaBrowser.Controller/Streaming/StreamState.cs

@@ -1,10 +1,9 @@
 using System;
-using Jellyfin.Api.Helpers;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Model.Dlna;
 
-namespace Jellyfin.Api.Models.StreamingDtos;
+namespace MediaBrowser.Controller.Streaming;
 
 /// <summary>
 /// The stream state dto.
@@ -12,7 +11,7 @@ namespace Jellyfin.Api.Models.StreamingDtos;
 public class StreamState : EncodingJobInfo, IDisposable
 {
     private readonly IMediaSourceManager _mediaSourceManager;
-    private readonly TranscodingJobHelper _transcodingJobHelper;
+    private readonly ITranscodeManager _transcodeManager;
     private bool _disposed;
 
     /// <summary>
@@ -20,12 +19,12 @@ public class StreamState : EncodingJobInfo, IDisposable
     /// </summary>
     /// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager" /> interface.</param>
     /// <param name="transcodingType">The <see cref="TranscodingJobType" />.</param>
-    /// <param name="transcodingJobHelper">The <see cref="TranscodingJobHelper" /> singleton.</param>
-    public StreamState(IMediaSourceManager mediaSourceManager, TranscodingJobType transcodingType, TranscodingJobHelper transcodingJobHelper)
+    /// <param name="transcodeManager">The <see cref="ITranscodeManager" /> singleton.</param>
+    public StreamState(IMediaSourceManager mediaSourceManager, TranscodingJobType transcodingType, ITranscodeManager transcodeManager)
         : base(transcodingType)
     {
         _mediaSourceManager = mediaSourceManager;
-        _transcodingJobHelper = transcodingJobHelper;
+        _transcodeManager = transcodeManager;
     }
 
     /// <summary>
@@ -152,7 +151,7 @@ public class StreamState : EncodingJobInfo, IDisposable
     /// <inheritdoc />
     public override void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate)
     {
-        _transcodingJobHelper.ReportTranscodingProgress(TranscodingJob!, this, transcodingPosition, framerate, percentComplete, bytesTranscoded, bitRate);
+        _transcodeManager.ReportTranscodingProgress(TranscodingJob!, this, transcodingPosition, framerate, percentComplete, bytesTranscoded, bitRate);
     }
 
     /// <summary>

+ 1 - 1
Jellyfin.Api/Models/StreamingDtos/StreamingRequestDto.cs → MediaBrowser.Controller/Streaming/StreamingRequestDto.cs

@@ -1,6 +1,6 @@
 using MediaBrowser.Controller.MediaEncoding;
 
-namespace Jellyfin.Api.Models.StreamingDtos;
+namespace MediaBrowser.Controller.Streaming;
 
 /// <summary>
 /// The audio streaming request dto.

+ 1 - 1
Jellyfin.Api/Models/StreamingDtos/VideoRequestDto.cs → MediaBrowser.Controller/Streaming/VideoRequestDto.cs

@@ -1,4 +1,4 @@
-namespace Jellyfin.Api.Models.StreamingDtos;
+namespace MediaBrowser.Controller.Streaming;
 
 /// <summary>
 /// The video request dto.

+ 89 - 235
Jellyfin.Api/Helpers/TranscodingJobHelper.cs → MediaBrowser.MediaEncoding/Transcoding/TranscodeManager.cs

@@ -8,8 +8,6 @@ using System.Text;
 using System.Text.Json;
 using System.Threading;
 using System.Threading.Tasks;
-using Jellyfin.Api.Extensions;
-using Jellyfin.Api.Models.StreamingDtos;
 using Jellyfin.Data.Enums;
 using MediaBrowser.Common;
 using MediaBrowser.Common.Configuration;
@@ -18,93 +16,77 @@ using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Controller.Session;
+using MediaBrowser.Controller.Streaming;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.MediaInfo;
 using MediaBrowser.Model.Session;
-using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.Logging;
 
-namespace Jellyfin.Api.Helpers;
+namespace MediaBrowser.MediaEncoding.Transcoding;
 
-/// <summary>
-/// Transcoding job helpers.
-/// </summary>
-public class TranscodingJobHelper : IDisposable
+/// <inheritdoc cref="ITranscodeManager"/>
+public sealed class TranscodeManager : ITranscodeManager, IDisposable
 {
-    /// <summary>
-    /// The active transcoding jobs.
-    /// </summary>
-    private static readonly List<TranscodingJob> _activeTranscodingJobs = new List<TranscodingJob>();
-
-    /// <summary>
-    /// The transcoding locks.
-    /// </summary>
-    private static readonly Dictionary<string, SemaphoreSlim> _transcodingLocks = new Dictionary<string, SemaphoreSlim>();
-
-    private readonly IAttachmentExtractor _attachmentExtractor;
+    private readonly ILoggerFactory _loggerFactory;
+    private readonly ILogger<TranscodeManager> _logger;
+    private readonly IFileSystem _fileSystem;
     private readonly IApplicationPaths _appPaths;
+    private readonly IServerConfigurationManager _serverConfigurationManager;
+    private readonly IUserManager _userManager;
+    private readonly ISessionManager _sessionManager;
     private readonly EncodingHelper _encodingHelper;
-    private readonly IFileSystem _fileSystem;
-    private readonly ILogger<TranscodingJobHelper> _logger;
     private readonly IMediaEncoder _mediaEncoder;
     private readonly IMediaSourceManager _mediaSourceManager;
-    private readonly IServerConfigurationManager _serverConfigurationManager;
-    private readonly ISessionManager _sessionManager;
-    private readonly ILoggerFactory _loggerFactory;
-    private readonly IUserManager _userManager;
+    private readonly IAttachmentExtractor _attachmentExtractor;
+
+    private readonly List<TranscodingJob> _activeTranscodingJobs = new();
+    private readonly Dictionary<string, SemaphoreSlim> _transcodingLocks = new();
 
     /// <summary>
-    /// Initializes a new instance of the <see cref="TranscodingJobHelper"/> class.
+    /// Initializes a new instance of the <see cref="TranscodeManager"/> class.
     /// </summary>
-    /// <param name="attachmentExtractor">Instance of the <see cref="IAttachmentExtractor"/> interface.</param>
-    /// <param name="appPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param>
-    /// <param name="logger">Instance of the <see cref="ILogger{TranscodingJobHelpers}"/> interface.</param>
-    /// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
-    /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
-    /// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
-    /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
-    /// <param name="sessionManager">Instance of the <see cref="ISessionManager"/> interface.</param>
-    /// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</param>
-    /// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/> interface.</param>
-    /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
-    public TranscodingJobHelper(
-        IAttachmentExtractor attachmentExtractor,
-        IApplicationPaths appPaths,
-        ILogger<TranscodingJobHelper> logger,
-        IMediaSourceManager mediaSourceManager,
+    /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
+    /// <param name="fileSystem">The <see cref="IFileSystem"/>.</param>
+    /// <param name="appPaths">The <see cref="IApplicationPaths"/>.</param>
+    /// <param name="serverConfigurationManager">The <see cref="IServerConfigurationManager"/>.</param>
+    /// <param name="userManager">The <see cref="IUserManager"/>.</param>
+    /// <param name="sessionManager">The <see cref="ISessionManager"/>.</param>
+    /// <param name="encodingHelper">The <see cref="EncodingHelper"/>.</param>
+    /// <param name="mediaEncoder">The <see cref="IMediaEncoder"/>.</param>
+    /// <param name="mediaSourceManager">The <see cref="IMediaSourceManager"/>.</param>
+    /// <param name="attachmentExtractor">The <see cref="IAttachmentExtractor"/>.</param>
+    public TranscodeManager(
+        ILoggerFactory loggerFactory,
         IFileSystem fileSystem,
-        IMediaEncoder mediaEncoder,
+        IApplicationPaths appPaths,
         IServerConfigurationManager serverConfigurationManager,
+        IUserManager userManager,
         ISessionManager sessionManager,
         EncodingHelper encodingHelper,
-        ILoggerFactory loggerFactory,
-        IUserManager userManager)
+        IMediaEncoder mediaEncoder,
+        IMediaSourceManager mediaSourceManager,
+        IAttachmentExtractor attachmentExtractor)
     {
-        _attachmentExtractor = attachmentExtractor;
-        _appPaths = appPaths;
-        _logger = logger;
-        _mediaSourceManager = mediaSourceManager;
+        _loggerFactory = loggerFactory;
         _fileSystem = fileSystem;
-        _mediaEncoder = mediaEncoder;
+        _appPaths = appPaths;
         _serverConfigurationManager = serverConfigurationManager;
+        _userManager = userManager;
         _sessionManager = sessionManager;
         _encodingHelper = encodingHelper;
-        _loggerFactory = loggerFactory;
-        _userManager = userManager;
+        _mediaEncoder = mediaEncoder;
+        _mediaSourceManager = mediaSourceManager;
+        _attachmentExtractor = attachmentExtractor;
 
+        _logger = loggerFactory.CreateLogger<TranscodeManager>();
         DeleteEncodedMediaCache();
-
-        sessionManager.PlaybackProgress += OnPlaybackProgress;
-        sessionManager.PlaybackStart += OnPlaybackProgress;
+        _sessionManager.PlaybackProgress += OnPlaybackProgress;
+        _sessionManager.PlaybackStart += OnPlaybackProgress;
     }
 
-    /// <summary>
-    /// Get transcoding job.
-    /// </summary>
-    /// <param name="playSessionId">Playback session id.</param>
-    /// <returns>The transcoding job.</returns>
+    /// <inheritdoc />
     public TranscodingJob? GetTranscodingJob(string playSessionId)
     {
         lock (_activeTranscodingJobs)
@@ -113,12 +95,7 @@ public class TranscodingJobHelper : IDisposable
         }
     }
 
-    /// <summary>
-    /// Get transcoding job.
-    /// </summary>
-    /// <param name="path">Path to the transcoding file.</param>
-    /// <param name="type">The <see cref="TranscodingJobType"/>.</param>
-    /// <returns>The transcoding job.</returns>
+    /// <inheritdoc />
     public TranscodingJob? GetTranscodingJob(string path, TranscodingJobType type)
     {
         lock (_activeTranscodingJobs)
@@ -127,12 +104,7 @@ public class TranscodingJobHelper : IDisposable
         }
     }
 
-    /// <summary>
-    /// Ping transcoding job.
-    /// </summary>
-    /// <param name="playSessionId">Play session id.</param>
-    /// <param name="isUserPaused">Is user paused.</param>
-    /// <exception cref="ArgumentNullException">Play session id is null.</exception>
+    /// <inheritdoc />
     public void PingTranscodingJob(string playSessionId, bool? isUserPaused)
     {
         ArgumentException.ThrowIfNullOrEmpty(playSessionId);
@@ -189,10 +161,6 @@ public class TranscodingJobHelper : IDisposable
         }
     }
 
-    /// <summary>
-    /// Called when [transcode kill timer stopped].
-    /// </summary>
-    /// <param name="state">The state.</param>
     private async void OnTranscodeKillTimerStopped(object? state)
     {
         var job = state as TranscodingJob ?? throw new ArgumentException($"{nameof(state)} is not of type {nameof(TranscodingJob)}", nameof(state));
@@ -212,29 +180,8 @@ public class TranscodingJobHelper : IDisposable
         await KillTranscodingJob(job, true, path => true).ConfigureAwait(false);
     }
 
-    /// <summary>
-    /// Kills the single transcoding job.
-    /// </summary>
-    /// <param name="deviceId">The device id.</param>
-    /// <param name="playSessionId">The play session identifier.</param>
-    /// <param name="deleteFiles">The delete files.</param>
-    /// <returns>Task.</returns>
+    /// <inheritdoc />
     public Task KillTranscodingJobs(string deviceId, string? playSessionId, Func<string, bool> deleteFiles)
-    {
-        return KillTranscodingJobs(
-            j => string.IsNullOrWhiteSpace(playSessionId)
-                ? string.Equals(deviceId, j.DeviceId, StringComparison.OrdinalIgnoreCase)
-                : string.Equals(playSessionId, j.PlaySessionId, StringComparison.OrdinalIgnoreCase),
-            deleteFiles);
-    }
-
-    /// <summary>
-    /// Kills the transcoding jobs.
-    /// </summary>
-    /// <param name="killJob">The kill job.</param>
-    /// <param name="deleteFiles">The delete files.</param>
-    /// <returns>Task.</returns>
-    private Task KillTranscodingJobs(Func<TranscodingJob, bool> killJob, Func<string, bool> deleteFiles)
     {
         var jobs = new List<TranscodingJob>();
 
@@ -242,13 +189,12 @@ public class TranscodingJobHelper : IDisposable
         {
             // This is really only needed for HLS.
             // Progressive streams can stop on their own reliably.
-            jobs.AddRange(_activeTranscodingJobs.Where(killJob));
+            jobs.AddRange(_activeTranscodingJobs.Where(j => string.IsNullOrWhiteSpace(playSessionId)
+                ? string.Equals(deviceId, j.DeviceId, StringComparison.OrdinalIgnoreCase)
+                : string.Equals(playSessionId, j.PlaySessionId, StringComparison.OrdinalIgnoreCase)));
         }
 
-        if (jobs.Count == 0)
-        {
-            return Task.CompletedTask;
-        }
+        return Task.WhenAll(GetKillJobs());
 
         IEnumerable<Task> GetKillJobs()
         {
@@ -257,16 +203,8 @@ public class TranscodingJobHelper : IDisposable
                 yield return KillTranscodingJob(job, false, deleteFiles);
             }
         }
-
-        return Task.WhenAll(GetKillJobs());
     }
 
-    /// <summary>
-    /// Kills the transcoding job.
-    /// </summary>
-    /// <param name="job">The job.</param>
-    /// <param name="closeLiveStream">if set to <c>true</c> [close live stream].</param>
-    /// <param name="delete">The delete.</param>
     private async Task KillTranscodingJob(TranscodingJob job, bool closeLiveStream, Func<string, bool> delete)
     {
         job.DisposeKillTimer();
@@ -353,10 +291,6 @@ public class TranscodingJobHelper : IDisposable
         }
     }
 
-    /// <summary>
-    /// Deletes the progressive partial stream files.
-    /// </summary>
-    /// <param name="outputFilePath">The output file path.</param>
     private void DeleteProgressivePartialStreamFiles(string outputFilePath)
     {
         if (File.Exists(outputFilePath))
@@ -365,10 +299,6 @@ public class TranscodingJobHelper : IDisposable
         }
     }
 
-    /// <summary>
-    /// Deletes the HLS partial stream files.
-    /// </summary>
-    /// <param name="outputFilePath">The output file path.</param>
     private void DeleteHlsPartialStreamFiles(string outputFilePath)
     {
         var directory = Path.GetDirectoryName(outputFilePath)
@@ -400,16 +330,7 @@ public class TranscodingJobHelper : IDisposable
         }
     }
 
-    /// <summary>
-    /// Report the transcoding progress to the session manager.
-    /// </summary>
-    /// <param name="job">The <see cref="TranscodingJob"/> of which the progress will be reported.</param>
-    /// <param name="state">The <see cref="StreamState"/> of the current transcoding job.</param>
-    /// <param name="transcodingPosition">The current transcoding position.</param>
-    /// <param name="framerate">The framerate of the transcoding job.</param>
-    /// <param name="percentComplete">The completion percentage of the transcode.</param>
-    /// <param name="bytesTranscoded">The number of bytes transcoded.</param>
-    /// <param name="bitRate">The bitrate of the transcoding job.</param>
+    /// <inheritdoc />
     public void ReportTranscodingProgress(
         TranscodingJob job,
         StreamState state,
@@ -462,22 +383,12 @@ public class TranscodingJobHelper : IDisposable
         }
     }
 
-    /// <summary>
-    /// Starts FFmpeg.
-    /// </summary>
-    /// <param name="state">The state.</param>
-    /// <param name="outputPath">The output path.</param>
-    /// <param name="commandLineArguments">The command line arguments for FFmpeg.</param>
-    /// <param name="request">The <see cref="HttpRequest"/>.</param>
-    /// <param name="transcodingJobType">The <see cref="TranscodingJobType"/>.</param>
-    /// <param name="cancellationTokenSource">The cancellation token source.</param>
-    /// <param name="workingDirectory">The working directory.</param>
-    /// <returns>Task.</returns>
+    /// <inheritdoc />
     public async Task<TranscodingJob> StartFfMpeg(
         StreamState state,
         string outputPath,
         string commandLineArguments,
-        HttpRequest request,
+        Guid userId,
         TranscodingJobType transcodingJobType,
         CancellationTokenSource cancellationTokenSource,
         string? workingDirectory = null)
@@ -489,7 +400,6 @@ public class TranscodingJobHelper : IDisposable
 
         if (state.VideoRequest is not null && !EncodingHelper.IsCopyCodec(state.OutputVideoCodec))
         {
-            var userId = request.HttpContext.User.GetUserId();
             var user = userId.Equals(default) ? null : _userManager.GetUserById(userId);
             if (user is not null && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding))
             {
@@ -567,13 +477,26 @@ public class TranscodingJobHelper : IDisposable
             $"{logFilePrefix}{DateTime.Now:yyyy-MM-dd_HH-mm-ss}_{state.Request.MediaSourceId}_{Guid.NewGuid().ToString()[..8]}.log");
 
         // FFmpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
-        Stream logStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
+        Stream logStream = new FileStream(
+            logFilePath,
+            FileMode.Create,
+            FileAccess.Write,
+            FileShare.Read,
+            IODefaults.FileStreamBufferSize,
+            FileOptions.Asynchronous);
 
         var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;
-        var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(request.Path + Environment.NewLine + Environment.NewLine + JsonSerializer.Serialize(state.MediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
+        var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(
+            JsonSerializer.Serialize(state.MediaSource)
+            + Environment.NewLine
+            + Environment.NewLine
+            + commandLineLogMessage
+            + Environment.NewLine
+            + Environment.NewLine);
+
         await logStream.WriteAsync(commandLineLogMessageBytes, cancellationTokenSource.Token).ConfigureAwait(false);
 
-        process.Exited += (sender, args) => OnFfMpegProcessExited(process, transcodingJob, state);
+        process.Exited += (_, _) => OnFfMpegProcessExited(process, transcodingJob, state);
 
         try
         {
@@ -582,7 +505,6 @@ public class TranscodingJobHelper : IDisposable
         catch (Exception ex)
         {
             _logger.LogError(ex, "Error starting FFmpeg");
-
             this.OnTranscodeFailedToStart(outputPath, transcodingJobType, state);
 
             throw;
@@ -637,31 +559,14 @@ public class TranscodingJobHelper : IDisposable
         }
     }
 
-    private bool EnableThrottling(StreamState state)
-    {
-        var encodingOptions = _serverConfigurationManager.GetEncodingOptions();
-
-        return state.InputProtocol == MediaProtocol.File &&
-               state.RunTimeTicks.HasValue &&
-               state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks &&
-               state.IsInputVideo &&
-               state.VideoType == VideoType.VideoFile;
-    }
+    private static bool EnableThrottling(StreamState state)
+        => state.InputProtocol == MediaProtocol.File
+           && state.RunTimeTicks.HasValue
+           && state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks
+           && state.IsInputVideo
+           && state.VideoType == VideoType.VideoFile;
 
-    /// <summary>
-    /// Called when [transcode beginning].
-    /// </summary>
-    /// <param name="path">The path.</param>
-    /// <param name="playSessionId">The play session identifier.</param>
-    /// <param name="liveStreamId">The live stream identifier.</param>
-    /// <param name="transcodingJobId">The transcoding job identifier.</param>
-    /// <param name="type">The type.</param>
-    /// <param name="process">The process.</param>
-    /// <param name="deviceId">The device id.</param>
-    /// <param name="state">The state.</param>
-    /// <param name="cancellationTokenSource">The cancellation token source.</param>
-    /// <returns>TranscodingJob.</returns>
-    public TranscodingJob OnTranscodeBeginning(
+    private TranscodingJob OnTranscodeBeginning(
         string path,
         string? playSessionId,
         string? liveStreamId,
@@ -696,10 +601,7 @@ public class TranscodingJobHelper : IDisposable
         }
     }
 
-    /// <summary>
-    /// Called when [transcode end].
-    /// </summary>
-    /// <param name="job">The transcode job.</param>
+    /// <inheritdoc />
     public void OnTranscodeEndRequest(TranscodingJob job)
     {
         job.ActiveRequestCount--;
@@ -710,16 +612,7 @@ public class TranscodingJobHelper : IDisposable
         }
     }
 
-    /// <summary>
-    /// <summary>
-    /// The progressive
-    /// </summary>
-    /// Called when [transcode failed to start].
-    /// </summary>
-    /// <param name="path">The path.</param>
-    /// <param name="type">The type.</param>
-    /// <param name="state">The state.</param>
-    public void OnTranscodeFailedToStart(string path, TranscodingJobType type, StreamState state)
+    private void OnTranscodeFailedToStart(string path, TranscodingJobType type, StreamState state)
     {
         lock (_activeTranscodingJobs)
         {
@@ -742,12 +635,6 @@ public class TranscodingJobHelper : IDisposable
         }
     }
 
-    /// <summary>
-    /// Processes the exited.
-    /// </summary>
-    /// <param name="process">The process.</param>
-    /// <param name="job">The job.</param>
-    /// <param name="state">The state.</param>
     private void OnFfMpegProcessExited(Process process, TranscodingJob job, StreamState state)
     {
         job.HasExited = true;
@@ -794,44 +681,30 @@ public class TranscodingJobHelper : IDisposable
         }
     }
 
-    /// <summary>
-    /// Called when [transcode begin request].
-    /// </summary>
-    /// <param name="path">The path.</param>
-    /// <param name="type">The type.</param>
-    /// <returns>The <see cref="TranscodingJob"/>.</returns>
+    /// <inheritdoc />
     public TranscodingJob? OnTranscodeBeginRequest(string path, TranscodingJobType type)
     {
         lock (_activeTranscodingJobs)
         {
-            var job = _activeTranscodingJobs.FirstOrDefault(j => j.Type == type && string.Equals(j.Path, path, StringComparison.OrdinalIgnoreCase));
+            var job = _activeTranscodingJobs
+                .FirstOrDefault(j => j.Type == type && string.Equals(j.Path, path, StringComparison.OrdinalIgnoreCase));
 
             if (job is null)
             {
                 return null;
             }
 
-            OnTranscodeBeginRequest(job);
+            job.ActiveRequestCount++;
+            if (string.IsNullOrWhiteSpace(job.PlaySessionId) || job.Type == TranscodingJobType.Progressive)
+            {
+                job.StopKillTimer();
+            }
 
             return job;
         }
     }
 
-    private void OnTranscodeBeginRequest(TranscodingJob job)
-    {
-        job.ActiveRequestCount++;
-
-        if (string.IsNullOrWhiteSpace(job.PlaySessionId) || job.Type == TranscodingJobType.Progressive)
-        {
-            job.StopKillTimer();
-        }
-    }
-
-    /// <summary>
-    /// Gets the transcoding lock.
-    /// </summary>
-    /// <param name="outputPath">The output path of the transcoded file.</param>
-    /// <returns>A <see cref="SemaphoreSlim"/>.</returns>
+    /// <inheritdoc />
     public SemaphoreSlim GetTranscodingLock(string outputPath)
     {
         lock (_transcodingLocks)
@@ -854,9 +727,6 @@ public class TranscodingJobHelper : IDisposable
         }
     }
 
-    /// <summary>
-    /// Deletes the encoded media cache.
-    /// </summary>
     private void DeleteEncodedMediaCache()
     {
         var path = _serverConfigurationManager.GetTranscodePath();
@@ -871,26 +741,10 @@ public class TranscodingJobHelper : IDisposable
         }
     }
 
-    /// <summary>
-    /// Dispose transcoding job helper.
-    /// </summary>
+    /// <inheritdoc />
     public void Dispose()
     {
-        Dispose(true);
-        GC.SuppressFinalize(this);
-    }
-
-    /// <summary>
-    /// Dispose throttler.
-    /// </summary>
-    /// <param name="disposing">Disposing.</param>
-    protected virtual void Dispose(bool disposing)
-    {
-        if (disposing)
-        {
-            _loggerFactory.Dispose();
-            _sessionManager.PlaybackProgress -= OnPlaybackProgress;
-            _sessionManager.PlaybackStart -= OnPlaybackProgress;
-        }
+        _sessionManager.PlaybackProgress -= OnPlaybackProgress;
+        _sessionManager.PlaybackStart -= OnPlaybackProgress;
     }
 }