Compare commits

..
4 Commits
Author SHA1 Message Date
dtourolle 5c898a98f5 Use place holders for channel titles
🏗️ Build Plugin / build (push) Successful in 2m56s
🧪 Test Plugin / test (push) Successful in 1m25s
🚀 Release Plugin / build-and-release (push) Successful in 2m51s
2026-01-17 21:23:50 +01:00
Gitea Actions c282a98377 Update manifest.json for version 1.0.15 2026-01-17 09:54:39 +00:00
dtourolle 92dc6d8203 fix: change in server side API for live stream
🏗️ Build Plugin / build (push) Successful in 4m12s
🧪 Test Plugin / test (push) Successful in 1m24s
🚀 Release Plugin / build-and-release (push) Successful in 2m49s
2026-01-17 10:46:11 +01:00
Gitea Actions d313b68975 Update manifest.json for version 1.0.14 2025-12-30 12:38:41 +00:00
5 changed files with 45 additions and 9 deletions
@@ -200,7 +200,9 @@ public class SRFPlayChannel : IChannel, IHasCacheKey
continue;
}
items.Add(CreateFolder($"category_{topic.Id}", topic.Title ?? topic.Id!, topic.Lead));
// Generate placeholder image for topic
var placeholderUrl = CreatePlaceholderImageUrl(topic.Title ?? topic.Id!, _mediaSourceFactory.GetServerBaseUrl());
items.Add(CreateFolder($"category_{topic.Id}", topic.Title ?? topic.Id!, topic.Lead, placeholderUrl));
}
_logger.LogInformation("Added {Count} category folders", topics.Count);
@@ -214,7 +216,7 @@ public class SRFPlayChannel : IChannel, IHasCacheKey
return items;
}
private static ChannelItemInfo CreateFolder(string id, string name, string? overview = null)
private static ChannelItemInfo CreateFolder(string id, string name, string? overview = null, string? imageUrl = null)
{
return new ChannelItemInfo
{
@@ -222,7 +224,7 @@ public class SRFPlayChannel : IChannel, IHasCacheKey
Name = name,
Type = ChannelItemType.Folder,
FolderType = ChannelFolderType.Container,
ImageUrl = null,
ImageUrl = imageUrl,
Overview = overview
};
}
@@ -183,7 +183,8 @@ public class StreamProxyController : ControllerBase
try
{
// Fetch the variant manifest as a segment
var manifestData = await _proxyService.GetSegmentAsync(actualItemId, fullPath, cancellationToken).ConfigureAwait(false);
var queryString = Request.QueryString.HasValue ? Request.QueryString.Value : null;
var manifestData = await _proxyService.GetSegmentAsync(actualItemId, fullPath, queryString, cancellationToken).ConfigureAwait(false);
if (manifestData == null)
{
@@ -234,7 +235,9 @@ public class StreamProxyController : ControllerBase
try
{
var segmentData = await _proxyService.GetSegmentAsync(actualItemId, segmentPath, cancellationToken).ConfigureAwait(false);
// Pass the original query string to preserve segment-specific parameters (e.g., ?m=timestamp)
var queryString = Request.QueryString.HasValue ? Request.QueryString.Value : null;
var segmentData = await _proxyService.GetSegmentAsync(actualItemId, segmentPath, queryString, cancellationToken).ConfigureAwait(false);
if (segmentData == null)
{
@@ -56,9 +56,10 @@ public interface IStreamProxyService
/// </summary>
/// <param name="itemId">The item ID.</param>
/// <param name="segmentPath">The segment path.</param>
/// <param name="queryString">The original query string from the request (preserves segment-specific parameters like timestamps).</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The segment content as bytes.</returns>
Task<byte[]?> GetSegmentAsync(string itemId, string segmentPath, CancellationToken cancellationToken = default);
Task<byte[]?> GetSegmentAsync(string itemId, string segmentPath, string? queryString = null, CancellationToken cancellationToken = default);
/// <summary>
/// Cleans up old and expired stream mappings.
@@ -701,11 +701,13 @@ public class StreamProxyService : IStreamProxyService
/// </summary>
/// <param name="itemId">The item ID.</param>
/// <param name="segmentPath">The segment path.</param>
/// <param name="queryString">The original query string from the request (preserves segment-specific parameters like timestamps).</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The segment content as bytes.</returns>
public async Task<byte[]?> GetSegmentAsync(
string itemId,
string segmentPath,
string? queryString = null,
CancellationToken cancellationToken = default)
{
var authenticatedUrl = await GetAuthenticatedUrlAsync(itemId, cancellationToken).ConfigureAwait(false);
@@ -720,17 +722,29 @@ public class StreamProxyService : IStreamProxyService
var baseUri = new Uri(authenticatedUrl);
var baseUrl = $"{baseUri.Scheme}://{baseUri.Host}{string.Join('/', baseUri.AbsolutePath.Split('/')[..^1])}";
// Extract query parameters (auth tokens) from authenticated URL
var queryParams = baseUri.Query;
// Use the original query string from the request (preserves segment-specific params like ?m=timestamp)
// If no query string is provided, check if we need to add auth params from the master manifest
var queryParams = string.Empty;
if (!string.IsNullOrEmpty(queryString))
{
// Use the original query string from the segment request
queryParams = queryString.StartsWith('?') ? queryString : $"?{queryString}";
}
else if (!segmentPath.Contains("hdntl=", StringComparison.OrdinalIgnoreCase))
{
// Only append master manifest query params if segment doesn't have path-based auth
queryParams = baseUri.Query;
}
// Build full segment URL
var segmentUrl = $"{baseUrl}/{segmentPath}{queryParams}";
_logger.LogDebug(
"Fetching segment - BaseUri: {BaseUri}, BaseUrl: {BaseUrl}, SegmentPath: {SegmentPath}, FullUrl: {FullUrl}",
"Fetching segment - BaseUri: {BaseUri}, BaseUrl: {BaseUrl}, SegmentPath: {SegmentPath}, QueryString: {QueryString}, FullUrl: {FullUrl}",
authenticatedUrl,
baseUrl,
segmentPath,
queryString ?? "(none)",
segmentUrl);
using var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
var segmentData = await httpClient.GetByteArrayAsync(segmentUrl, cancellationToken).ConfigureAwait(false);
+16
View File
@@ -8,6 +8,22 @@
"category": "Live TV",
"imageUrl": "https://gitea.tourolle.paris/dtourolle/jellyfin-srfPlay/raw/branch/master/assests/main%20logo.png",
"versions": [
{
"version": "1.0.15",
"changelog": "Release 1.0.15",
"targetAbi": "10.9.0.0",
"sourceUrl": "https://gitea.tourolle.paris/dtourolle/jellyfin-srfPlay/releases/download/v1.0.15/srfplay_1.0.15.0.zip",
"checksum": "3140ef53a7460d7ce52daf48bbb37e4d",
"timestamp": "2026-01-17T09:54:39Z"
},
{
"version": "1.0.14",
"changelog": "Release 1.0.14",
"targetAbi": "10.9.0.0",
"sourceUrl": "https://gitea.tourolle.paris/dtourolle/jellyfin-srfPlay/releases/download/v1.0.14/srfplay_1.0.14.0.zip",
"checksum": "49e6afe16f7abf95c099ecc1d016e725",
"timestamp": "2025-12-30T12:38:41Z"
},
{
"version": "1.0.13",
"changelog": "Release 1.0.13",