aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Jellyfin.Api/Controllers/TrickplayController.cs86
-rw-r--r--MediaBrowser.Controller/Trickplay/ITrickplayManager.cs9
-rw-r--r--MediaBrowser.Providers/Trickplay/TrickplayManager.cs76
3 files changed, 94 insertions, 77 deletions
diff --git a/Jellyfin.Api/Controllers/TrickplayController.cs b/Jellyfin.Api/Controllers/TrickplayController.cs
index ac71eff19..36464d726 100644
--- a/Jellyfin.Api/Controllers/TrickplayController.cs
+++ b/Jellyfin.Api/Controllers/TrickplayController.cs
@@ -1,6 +1,5 @@
using System;
using System.ComponentModel.DataAnnotations;
-using System.Globalization;
using System.Net.Mime;
using System.Text;
using Jellyfin.Api.Attributes;
@@ -54,7 +53,14 @@ public class TrickplayController : BaseJellyfinApiController
[FromRoute, Required] int width,
[FromQuery] Guid? mediaSourceId)
{
- return GetTrickplayPlaylistInternal(width, mediaSourceId ?? itemId);
+ string? playlist = _trickplayManager.GetHlsPlaylist(mediaSourceId ?? itemId, width, User.GetToken());
+
+ if (string.IsNullOrEmpty(playlist))
+ {
+ return NotFound();
+ }
+
+ return new FileContentResult(Encoding.UTF8.GetBytes(playlist), MimeTypes.GetMimeType("playlist.m3u8"));
}
/// <summary>
@@ -71,7 +77,7 @@ public class TrickplayController : BaseJellyfinApiController
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesImageFile]
- public ActionResult GetTrickplayHlsPlaylist(
+ public ActionResult GetTrickplayGridImage(
[FromRoute, Required] Guid itemId,
[FromRoute, Required] int width,
[FromRoute, Required] int index,
@@ -91,78 +97,4 @@ public class TrickplayController : BaseJellyfinApiController
return NotFound();
}
-
- private ActionResult GetTrickplayPlaylistInternal(int width, Guid mediaSourceId)
- {
- var tilesResolutions = _trickplayManager.GetTilesResolutions(mediaSourceId);
- if (tilesResolutions is not null && tilesResolutions.TryGetValue(width, out var tilesInfo))
- {
- var builder = new StringBuilder(128);
-
- if (tilesInfo.TileCount > 0)
- {
- const string urlFormat = "Trickplay/{0}/{1}.jpg?MediaSourceId={2}&api_key={3}";
- const string decimalFormat = "{0:0.###}";
-
- var resolution = $"{tilesInfo.Width}x{tilesInfo.Height}";
- var layout = $"{tilesInfo.TileWidth}x{tilesInfo.TileHeight}";
- var tilesPerGrid = tilesInfo.TileWidth * tilesInfo.TileHeight;
- var tileDuration = tilesInfo.Interval / 1000d;
- var infDuration = tileDuration * tilesPerGrid;
- var tileGridCount = (int)Math.Ceiling((decimal)tilesInfo.TileCount / tilesPerGrid);
-
- builder
- .AppendLine("#EXTM3U")
- .Append("#EXT-X-TARGETDURATION:")
- .AppendLine(tileGridCount.ToString(CultureInfo.InvariantCulture))
- .AppendLine("#EXT-X-VERSION:7")
- .AppendLine("#EXT-X-MEDIA-SEQUENCE:1")
- .AppendLine("#EXT-X-PLAYLIST-TYPE:VOD")
- .AppendLine("#EXT-X-IMAGES-ONLY");
-
- for (int i = 0; i < tileGridCount; i++)
- {
- // All tile grids before the last one must contain full amount of tiles.
- // The final grid will be 0 < count <= maxTiles
- if (i == tileGridCount - 1)
- {
- tilesPerGrid = tilesInfo.TileCount - (i * tilesPerGrid);
- infDuration = tileDuration * tilesPerGrid;
- }
-
- // EXTINF
- builder
- .Append("#EXTINF:")
- .AppendFormat(CultureInfo.InvariantCulture, decimalFormat, infDuration)
- .AppendLine(",");
-
- // EXT-X-TILES
- builder
- .Append("#EXT-X-TILES:RESOLUTION=")
- .Append(resolution)
- .Append(",LAYOUT=")
- .Append(layout)
- .Append(",DURATION=")
- .AppendFormat(CultureInfo.InvariantCulture, decimalFormat, tileDuration)
- .AppendLine();
-
- // URL
- builder
- .AppendFormat(
- CultureInfo.InvariantCulture,
- urlFormat,
- width.ToString(CultureInfo.InvariantCulture),
- i.ToString(CultureInfo.InvariantCulture),
- mediaSourceId.ToString("N"),
- User.GetToken())
- .AppendLine();
- }
-
- builder.AppendLine("#EXT-X-ENDLIST");
- return new FileContentResult(Encoding.UTF8.GetBytes(builder.ToString()), MimeTypes.GetMimeType("playlist.m3u8"));
- }
- }
-
- return NotFound();
- }
}
diff --git a/MediaBrowser.Controller/Trickplay/ITrickplayManager.cs b/MediaBrowser.Controller/Trickplay/ITrickplayManager.cs
index 8e82c57d4..8d36fc3ff 100644
--- a/MediaBrowser.Controller/Trickplay/ITrickplayManager.cs
+++ b/MediaBrowser.Controller/Trickplay/ITrickplayManager.cs
@@ -50,4 +50,13 @@ public interface ITrickplayManager
/// <param name="index">The tile grid's index.</param>
/// <returns>The absolute path.</returns>
string GetTrickplayTilePath(BaseItem item, int width, int index);
+
+ /// <summary>
+ /// Gets the trickplay HLS playlist.
+ /// </summary>
+ /// <param name="itemId">The item.</param>
+ /// <param name="width">The width of a single tile.</param>
+ /// <param name="apiKey">Optional api key of the requesting user.</param>
+ /// <returns>The text content of the .m3u8 playlist.</returns>
+ string? GetHlsPlaylist(Guid itemId, int width, string? apiKey);
}
diff --git a/MediaBrowser.Providers/Trickplay/TrickplayManager.cs b/MediaBrowser.Providers/Trickplay/TrickplayManager.cs
index 419adc4b0..9fe3a330a 100644
--- a/MediaBrowser.Providers/Trickplay/TrickplayManager.cs
+++ b/MediaBrowser.Providers/Trickplay/TrickplayManager.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration;
@@ -321,6 +322,81 @@ public class TrickplayManager : ITrickplayManager
return Path.Combine(GetTrickplayDirectory(item, width), index + ".jpg");
}
+ /// <inheritdoc />
+ public string? GetHlsPlaylist(Guid itemId, int width, string? apiKey)
+ {
+ var tilesResolutions = GetTilesResolutions(itemId);
+ if (tilesResolutions is not null && tilesResolutions.TryGetValue(width, out var tilesInfo))
+ {
+ var builder = new StringBuilder(128);
+
+ if (tilesInfo.TileCount > 0)
+ {
+ const string urlFormat = "Trickplay/{0}/{1}.jpg?MediaSourceId={2}&api_key={3}";
+ const string decimalFormat = "{0:0.###}";
+
+ var resolution = $"{tilesInfo.Width}x{tilesInfo.Height}";
+ var layout = $"{tilesInfo.TileWidth}x{tilesInfo.TileHeight}";
+ var tilesPerGrid = tilesInfo.TileWidth * tilesInfo.TileHeight;
+ var tileDuration = tilesInfo.Interval / 1000d;
+ var infDuration = tileDuration * tilesPerGrid;
+ var tileGridCount = (int)Math.Ceiling((decimal)tilesInfo.TileCount / tilesPerGrid);
+
+ builder
+ .AppendLine("#EXTM3U")
+ .Append("#EXT-X-TARGETDURATION:")
+ .AppendLine(tileGridCount.ToString(CultureInfo.InvariantCulture))
+ .AppendLine("#EXT-X-VERSION:7")
+ .AppendLine("#EXT-X-MEDIA-SEQUENCE:1")
+ .AppendLine("#EXT-X-PLAYLIST-TYPE:VOD")
+ .AppendLine("#EXT-X-IMAGES-ONLY");
+
+ for (int i = 0; i < tileGridCount; i++)
+ {
+ // All tile grids before the last one must contain full amount of tiles.
+ // The final grid will be 0 < count <= maxTiles
+ if (i == tileGridCount - 1)
+ {
+ tilesPerGrid = tilesInfo.TileCount - (i * tilesPerGrid);
+ infDuration = tileDuration * tilesPerGrid;
+ }
+
+ // EXTINF
+ builder
+ .Append("#EXTINF:")
+ .AppendFormat(CultureInfo.InvariantCulture, decimalFormat, infDuration)
+ .AppendLine(",");
+
+ // EXT-X-TILES
+ builder
+ .Append("#EXT-X-TILES:RESOLUTION=")
+ .Append(resolution)
+ .Append(",LAYOUT=")
+ .Append(layout)
+ .Append(",DURATION=")
+ .AppendFormat(CultureInfo.InvariantCulture, decimalFormat, tileDuration)
+ .AppendLine();
+
+ // URL
+ builder
+ .AppendFormat(
+ CultureInfo.InvariantCulture,
+ urlFormat,
+ width.ToString(CultureInfo.InvariantCulture),
+ i.ToString(CultureInfo.InvariantCulture),
+ itemId.ToString("N"),
+ apiKey)
+ .AppendLine();
+ }
+
+ builder.AppendLine("#EXT-X-ENDLIST");
+ return builder.ToString();
+ }
+ }
+
+ return null;
+ }
+
private string GetTrickplayDirectory(BaseItem item, int? width = null)
{
var path = Path.Combine(item.GetInternalMetadataPath(), "trickplay");