add possible solution for later
This commit is contained in:
102
feat/ScriptInjectionMiddleware.cs
Normal file
102
feat/ScriptInjectionMiddleware.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Jellyfin.Plugin.Seasonals;
|
||||
|
||||
/// <summary>
|
||||
/// Middleware to inject the Seasonals script into the Jellyfin web interface.
|
||||
/// </summary>
|
||||
public class ScriptInjectionMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly ILogger<ScriptInjectionMiddleware> _logger;
|
||||
private const string ScriptTag = "<script src=\"Seasonals/Resources/seasonals.js\"></script>";
|
||||
private const string Marker = "</body>";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ScriptInjectionMiddleware"/> class.
|
||||
/// </summary>
|
||||
/// <param name="next">The next delegate in the pipeline.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
public ScriptInjectionMiddleware(RequestDelegate next, ILogger<ScriptInjectionMiddleware> logger)
|
||||
{
|
||||
_next = next;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the middleware.
|
||||
/// </summary>
|
||||
/// <param name="context">The HTTP context.</param>
|
||||
/// <returns>A task representing the asynchronous operation.</returns>
|
||||
public async Task InvokeAsync(HttpContext context)
|
||||
{
|
||||
if (IsIndexRequest(context.Request.Path))
|
||||
{
|
||||
var originalBodyStream = context.Response.Body;
|
||||
using var responseBody = new MemoryStream();
|
||||
context.Response.Body = responseBody;
|
||||
|
||||
try
|
||||
{
|
||||
await _next(context);
|
||||
|
||||
context.Response.Body = originalBodyStream;
|
||||
responseBody.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
if (context.Response.StatusCode == 200 &&
|
||||
context.Response.ContentType != null &&
|
||||
context.Response.ContentType.Contains("text/html", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
using var reader = new StreamReader(responseBody);
|
||||
var content = await reader.ReadToEndAsync();
|
||||
|
||||
if (!content.Contains(ScriptTag, StringComparison.Ordinal) && content.Contains(Marker, StringComparison.Ordinal))
|
||||
{
|
||||
var newContent = content.Replace(Marker, $"{ScriptTag}\n{Marker}", StringComparison.Ordinal);
|
||||
var bytes = Encoding.UTF8.GetBytes(newContent);
|
||||
|
||||
context.Response.ContentLength = bytes.Length;
|
||||
await context.Response.Body.WriteAsync(bytes, 0, bytes.Length);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
responseBody.Seek(0, SeekOrigin.Begin);
|
||||
await responseBody.CopyToAsync(originalBodyStream);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error injecting Seasonals script via middleware.");
|
||||
// Ensure we try to write back the original response if something failed
|
||||
if (responseBody.Length > 0 && context.Response.Body == originalBodyStream)
|
||||
{
|
||||
responseBody.Seek(0, SeekOrigin.Begin);
|
||||
await responseBody.CopyToAsync(originalBodyStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await _next(context);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsIndexRequest(PathString path)
|
||||
{
|
||||
if (!path.HasValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var p = path.Value;
|
||||
// Check for root, index.html, or web/index.html
|
||||
return p.Equals("/", StringComparison.OrdinalIgnoreCase) ||
|
||||
p.Equals("/index.html", StringComparison.OrdinalIgnoreCase) ||
|
||||
p.EndsWith("/web/index.html", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user