mixed refactor
🏗️ Build Plugin / build (push) Successful in 2m47s
🧪 Test Plugin / test (push) Successful in 1m22s
🚀 Release Plugin / build-and-release (push) Successful in 2m39s

This commit is contained in:
2025-12-06 20:18:43 +01:00
parent 4f9ebe2bce
commit ed4cc0990c
12 changed files with 961 additions and 647 deletions
@@ -40,6 +40,32 @@ public class StreamProxyController : ControllerBase
_httpClientFactory = httpClientFactory;
}
/// <summary>
/// Adds CORS headers to allow cross-origin requests from hls.js in browsers.
/// </summary>
private void AddCorsHeaders()
{
Response.Headers["Access-Control-Allow-Origin"] = "*";
Response.Headers["Access-Control-Allow-Methods"] = "GET, HEAD, OPTIONS";
Response.Headers["Access-Control-Allow-Headers"] = "Content-Type, Range, Accept, Origin";
Response.Headers["Access-Control-Expose-Headers"] = "Content-Length, Content-Range, Accept-Ranges";
Response.Headers["Access-Control-Max-Age"] = "86400"; // Cache preflight for 24 hours
}
/// <summary>
/// Handles CORS preflight OPTIONS requests for all proxy endpoints.
/// </summary>
/// <returns>Empty response with CORS headers.</returns>
[HttpOptions("{itemId}/master.m3u8")]
[HttpOptions("{itemId}/{manifestPath}.m3u8")]
[HttpOptions("{itemId}/{*segmentPath}")]
[AllowAnonymous]
public IActionResult HandleOptions()
{
AddCorsHeaders();
return Ok();
}
/// <summary>
/// Proxies HLS master manifest requests.
/// </summary>
@@ -54,6 +80,7 @@ public class StreamProxyController : ControllerBase
[FromRoute] string itemId,
CancellationToken cancellationToken)
{
AddCorsHeaders();
_logger.LogInformation("Proxy request for master manifest - Path ItemId: {PathItemId}, Query params: {QueryString}", itemId, Request.QueryString);
// Try to resolve the actual item ID (path ID might be a session ID during transcoding)
@@ -119,6 +146,7 @@ public class StreamProxyController : ControllerBase
[FromRoute] string manifestPath,
CancellationToken cancellationToken)
{
AddCorsHeaders();
var fullPath = $"{manifestPath}.m3u8";
_logger.LogInformation("Proxy request for variant manifest - ItemId: {ItemId}, Path: {Path}", itemId, fullPath);
@@ -168,6 +196,7 @@ public class StreamProxyController : ControllerBase
[FromRoute] string segmentPath,
CancellationToken cancellationToken)
{
AddCorsHeaders();
_logger.LogDebug("Proxy request for segment - ItemId: {ItemId}, Path: {SegmentPath}", itemId, segmentPath);
// Try to resolve the actual item ID
@@ -244,8 +273,8 @@ public class StreamProxyController : ControllerBase
return queryItemId.ToString();
}
// No query parameters - use path ID as-is (TRANSCODING SESSION ID CASE)
_logger.LogWarning("⚠️ No query parameters found, using path ID as-is: {PathItemId} (likely transcoding session ID)", pathItemId);
// No query parameters - use path ID as-is (normal case for segments and transcoding sessions)
_logger.LogDebug("No query parameters, using path ID as-is: {PathItemId}", pathItemId);
return pathItemId;
}
@@ -272,28 +301,52 @@ public class StreamProxyController : ControllerBase
queryParams = string.Empty;
}
// Helper function to rewrite a single URL
string RewriteUrl(string url)
{
if (url.Contains("://", StringComparison.Ordinal))
{
// Absolute URL - extract filename and rewrite
var uri = new Uri(url.Trim());
var segments = uri.AbsolutePath.Split('/');
var fileName = segments[^1];
return $"{baseProxyUrl}/{fileName}{queryParams}";
}
// Relative URL - rewrite to proxy
return $"{baseProxyUrl}/{url.Trim()}{queryParams}";
}
var lines = manifestContent.Split('\n');
var result = new System.Text.StringBuilder();
foreach (var line in lines)
{
if (line.StartsWith('#') || string.IsNullOrWhiteSpace(line))
if (string.IsNullOrWhiteSpace(line))
{
// Keep metadata and blank lines as-is
result.AppendLine(line);
}
else if (line.Contains("://", StringComparison.Ordinal))
else if (line.StartsWith('#'))
{
// Absolute URL - extract the path and rewrite
var uri = new Uri(line.Trim());
var segments = uri.AbsolutePath.Split('/');
var fileName = segments[^1];
result.AppendLine(CultureInfo.InvariantCulture, $"{baseProxyUrl}/{fileName}{queryParams}");
// HLS tag line - check for URI="..." attributes (e.g., #EXT-X-MAP:URI="init.mp4")
if (line.Contains("URI=\"", StringComparison.Ordinal))
{
var rewrittenLine = System.Text.RegularExpressions.Regex.Replace(
line,
@"URI=""([^""]+)""",
match => $"URI=\"{RewriteUrl(match.Groups[1].Value)}\"");
result.AppendLine(rewrittenLine);
}
else
{
// Keep other metadata lines as-is
result.AppendLine(line);
}
}
else
{
// Relative URL - rewrite to proxy
result.AppendLine(CultureInfo.InvariantCulture, $"{baseProxyUrl}/{line.Trim()}{queryParams}");
// Non-tag line with URL - rewrite it
result.AppendLine(RewriteUrl(line));
}
}