瀏覽代碼

Merge pull request #3718 from Ullmie02/api-hlsegment

Move HlsSegmentService to Jellyfin.Api
Patrick Barron 4 年之前
父節點
當前提交
62fa72114c

+ 153 - 0
Jellyfin.Api/Controllers/HlsSegmentController.cs

@@ -0,0 +1,153 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Jellyfin.Api.Constants;
+using Jellyfin.Api.Helpers;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Net;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Jellyfin.Api.Controllers
+{
+    /// <summary>
+    /// The hls segment controller.
+    /// </summary>
+    public class HlsSegmentController : BaseJellyfinApiController
+    {
+        private readonly IFileSystem _fileSystem;
+        private readonly IServerConfigurationManager _serverConfigurationManager;
+        private readonly TranscodingJobHelper _transcodingJobHelper;
+
+        /// <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>
+        public HlsSegmentController(
+            IFileSystem fileSystem,
+            IServerConfigurationManager serverConfigurationManager,
+            TranscodingJobHelper transcodingJobHelper)
+        {
+            _fileSystem = fileSystem;
+            _serverConfigurationManager = serverConfigurationManager;
+            _transcodingJobHelper = transcodingJobHelper;
+        }
+
+        /// <summary>
+        /// Gets the specified audio segment for an audio item.
+        /// </summary>
+        /// <param name="itemId">The item id.</param>
+        /// <param name="segmentId">The segment id.</param>
+        /// <response code="200">Hls audio segment returned.</response>
+        /// <returns>A <see cref="FileStreamResult"/> containing the audio stream.</returns>
+        // Can't require authentication just yet due to seeing some requests come from Chrome without full query string
+        // [Authenticated]
+        [HttpGet("/Audio/{itemId}/hls/{segmentId}/stream.mp3")]
+        [HttpGet("/Audio/{itemId}/hls/{segmentId}/stream.aac")]
+        [ProducesResponseType(StatusCodes.Status200OK)]
+        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "itemId", Justification = "Required for ServiceStack")]
+        public ActionResult GetHlsAudioSegmentLegacy([FromRoute] string itemId, [FromRoute] string segmentId)
+        {
+            // TODO: Deprecate with new iOS app
+            var file = segmentId + Path.GetExtension(Request.Path);
+            file = Path.Combine(_serverConfigurationManager.GetTranscodePath(), file);
+
+            return FileStreamResponseHelpers.GetStaticFileResult(file, MimeTypes.GetMimeType(file)!, false, this);
+        }
+
+        /// <summary>
+        /// Gets a hls video playlist.
+        /// </summary>
+        /// <param name="itemId">The video id.</param>
+        /// <param name="playlistId">The playlist id.</param>
+        /// <response code="200">Hls video playlist returned.</response>
+        /// <returns>A <see cref="FileStreamResult"/> containing the playlist.</returns>
+        [HttpGet("/Videos/{itemId}/hls/{playlistId}/stream.m3u8")]
+        [Authorize(Policy = Policies.DefaultAuthorization)]
+        [ProducesResponseType(StatusCodes.Status200OK)]
+        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "itemId", Justification = "Required for ServiceStack")]
+        public ActionResult GetHlsPlaylistLegacy([FromRoute] string itemId, [FromRoute] string playlistId)
+        {
+            var file = playlistId + Path.GetExtension(Request.Path);
+            file = Path.Combine(_serverConfigurationManager.GetTranscodePath(), file);
+
+            return GetFileResult(file, file);
+        }
+
+        /// <summary>
+        /// Stops an active encoding.
+        /// </summary>
+        /// <param name="deviceId">The device id of the client requesting. Used to stop encoding processes when needed.</param>
+        /// <param name="playSessionId">The play session id.</param>
+        /// <response code="204">Encoding stopped successfully.</response>
+        /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
+        [HttpDelete("/Videos/ActiveEncodings")]
+        [Authorize(Policy = Policies.DefaultAuthorization)]
+        [ProducesResponseType(StatusCodes.Status204NoContent)]
+        public ActionResult StopEncodingProcess([FromQuery] string deviceId, [FromQuery] string playSessionId)
+        {
+            _transcodingJobHelper.KillTranscodingJobs(deviceId, playSessionId, path => true);
+            return NoContent();
+        }
+
+        /// <summary>
+        /// Gets a hls video segment.
+        /// </summary>
+        /// <param name="itemId">The item id.</param>
+        /// <param name="playlistId">The playlist id.</param>
+        /// <param name="segmentId">The segment id.</param>
+        /// <param name="segmentContainer">The segment container.</param>
+        /// <response code="200">Hls video segment returned.</response>
+        /// <returns>A <see cref="FileStreamResult"/> containing the video segment.</returns>
+        // Can't require authentication just yet due to seeing some requests come from Chrome without full query string
+        // [Authenticated]
+        [HttpGet("/Videos/{itemId}/hls/{playlistId}/{segmentId}.{segmentContainer}")]
+        [ProducesResponseType(StatusCodes.Status200OK)]
+        [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "itemId", Justification = "Required for ServiceStack")]
+        public ActionResult GetHlsVideoSegmentLegacy(
+            [FromRoute] string itemId,
+            [FromRoute] string playlistId,
+            [FromRoute] string segmentId,
+            [FromRoute] string segmentContainer)
+        {
+            var file = segmentId + Path.GetExtension(Request.Path);
+            var transcodeFolderPath = _serverConfigurationManager.GetTranscodePath();
+
+            file = Path.Combine(transcodeFolderPath, file);
+
+            var normalizedPlaylistId = playlistId;
+
+            var playlistPath = _fileSystem.GetFilePaths(transcodeFolderPath)
+                .FirstOrDefault(i =>
+                    string.Equals(Path.GetExtension(i), ".m3u8", StringComparison.OrdinalIgnoreCase)
+                    && i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1);
+
+            return GetFileResult(file, playlistPath);
+        }
+
+        private ActionResult GetFileResult(string path, string playlistPath)
+        {
+            var transcodingJob = _transcodingJobHelper.OnTranscodeBeginRequest(playlistPath, TranscodingJobType.Hls);
+
+            Response.OnCompleted(() =>
+            {
+                if (transcodingJob != null)
+                {
+                    _transcodingJobHelper.OnTranscodeEndRequest(transcodingJob);
+                }
+
+                return Task.CompletedTask;
+            });
+
+            return FileStreamResponseHelpers.GetStaticFileResult(path, MimeTypes.GetMimeType(path)!, false, this);
+        }
+    }
+}

+ 14 - 0
Jellyfin.Api/Helpers/TranscodingJobHelper.cs

@@ -712,6 +712,20 @@ namespace Jellyfin.Api.Helpers
             }
             }
         }
         }
 
 
+        /// <summary>
+        /// Transcoding video finished. Decrement the active request counter.
+        /// </summary>
+        /// <param name="job">The <see cref="TranscodingJobDto"/> which ended.</param>
+        public void OnTranscodeEndRequest(TranscodingJobDto job)
+        {
+            job.ActiveRequestCount--;
+            _logger.LogDebug("OnTranscodeEndRequest job.ActiveRequestCount={0}", job.ActiveRequestCount);
+            if (job.ActiveRequestCount <= 0)
+            {
+                PingTimer(job, false);
+            }
+        }
+
         /// <summary>
         /// <summary>
         /// Processes the exited.
         /// Processes the exited.
         /// </summary>
         /// </summary>

+ 0 - 164
MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs

@@ -1,164 +0,0 @@
-using System;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Services;
-using Microsoft.Extensions.Logging;
-
-namespace MediaBrowser.Api.Playback.Hls
-{
-    /// <summary>
-    /// Class GetHlsAudioSegment.
-    /// </summary>
-    // Can't require authentication just yet due to seeing some requests come from Chrome without full query string
-    //[Authenticated]
-    [Route("/Audio/{Id}/hls/{SegmentId}/stream.mp3", "GET")]
-    [Route("/Audio/{Id}/hls/{SegmentId}/stream.aac", "GET")]
-    public class GetHlsAudioSegmentLegacy
-    {
-        // TODO: Deprecate with new iOS app
-
-        /// <summary>
-        /// Gets or sets the id.
-        /// </summary>
-        /// <value>The id.</value>
-        public string Id { get; set; }
-
-        /// <summary>
-        /// Gets or sets the segment id.
-        /// </summary>
-        /// <value>The segment id.</value>
-        public string SegmentId { get; set; }
-    }
-
-    /// <summary>
-    /// Class GetHlsVideoSegment.
-    /// </summary>
-    [Route("/Videos/{Id}/hls/{PlaylistId}/stream.m3u8", "GET")]
-    [Authenticated]
-    public class GetHlsPlaylistLegacy
-    {
-        // TODO: Deprecate with new iOS app
-
-        /// <summary>
-        /// Gets or sets the id.
-        /// </summary>
-        /// <value>The id.</value>
-        public string Id { get; set; }
-
-        public string PlaylistId { get; set; }
-    }
-
-    [Route("/Videos/ActiveEncodings", "DELETE")]
-    [Authenticated]
-    public class StopEncodingProcess
-    {
-        [ApiMember(Name = "DeviceId", Description = "The device id of the client requesting. Used to stop encoding processes when needed.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
-        public string DeviceId { get; set; }
-
-        [ApiMember(Name = "PlaySessionId", Description = "The play session id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
-        public string PlaySessionId { get; set; }
-    }
-
-    /// <summary>
-    /// Class GetHlsVideoSegment.
-    /// </summary>
-    // Can't require authentication just yet due to seeing some requests come from Chrome without full query string
-    //[Authenticated]
-    [Route("/Videos/{Id}/hls/{PlaylistId}/{SegmentId}.{SegmentContainer}", "GET")]
-    public class GetHlsVideoSegmentLegacy : VideoStreamRequest
-    {
-        public string PlaylistId { get; set; }
-
-        /// <summary>
-        /// Gets or sets the segment id.
-        /// </summary>
-        /// <value>The segment id.</value>
-        public string SegmentId { get; set; }
-    }
-
-    public class HlsSegmentService : BaseApiService
-    {
-        private readonly IFileSystem _fileSystem;
-
-        public HlsSegmentService(
-            ILogger<HlsSegmentService> logger,
-            IServerConfigurationManager serverConfigurationManager,
-            IHttpResultFactory httpResultFactory,
-            IFileSystem fileSystem)
-            : base(logger, serverConfigurationManager, httpResultFactory)
-        {
-            _fileSystem = fileSystem;
-        }
-
-        public Task<object> Get(GetHlsPlaylistLegacy request)
-        {
-            var file = request.PlaylistId + Path.GetExtension(Request.PathInfo);
-            file = Path.Combine(ServerConfigurationManager.GetTranscodePath(), file);
-
-            return GetFileResult(file, file);
-        }
-
-        public Task Delete(StopEncodingProcess request)
-        {
-            return ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, request.PlaySessionId, path => true);
-        }
-
-        /// <summary>
-        /// Gets the specified request.
-        /// </summary>
-        /// <param name="request">The request.</param>
-        /// <returns>System.Object.</returns>
-        public Task<object> Get(GetHlsVideoSegmentLegacy request)
-        {
-            var file = request.SegmentId + Path.GetExtension(Request.PathInfo);
-            var transcodeFolderPath = ServerConfigurationManager.GetTranscodePath();
-
-            file = Path.Combine(transcodeFolderPath, file);
-
-            var normalizedPlaylistId = request.PlaylistId;
-
-            var playlistPath = _fileSystem.GetFilePaths(transcodeFolderPath)
-                .FirstOrDefault(i => string.Equals(Path.GetExtension(i), ".m3u8", StringComparison.OrdinalIgnoreCase) && i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1);
-
-            return GetFileResult(file, playlistPath);
-        }
-
-        /// <summary>
-        /// Gets the specified request.
-        /// </summary>
-        /// <param name="request">The request.</param>
-        /// <returns>System.Object.</returns>
-        public Task<object> Get(GetHlsAudioSegmentLegacy request)
-        {
-            // TODO: Deprecate with new iOS app
-            var file = request.SegmentId + Path.GetExtension(Request.PathInfo);
-            file = Path.Combine(ServerConfigurationManager.GetTranscodePath(), file);
-
-            return ResultFactory.GetStaticFileResult(Request, file, FileShare.ReadWrite);
-        }
-
-        private Task<object> GetFileResult(string path, string playlistPath)
-        {
-            var transcodingJob = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType.Hls);
-
-            return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
-            {
-                Path = path,
-                FileShare = FileShare.ReadWrite,
-                OnComplete = () =>
-                {
-                    if (transcodingJob != null)
-                    {
-                        ApiEntryPoint.Instance.OnTranscodeEndRequest(transcodingJob);
-                    }
-                }
-            });
-        }
-    }
-}