fix: fix server mode crop application, request rewrite, and prevent infinite library scan loop
Build and Release Plugin / release (push) Failing after 1m7s
Build and Release Plugin / release (push) Failing after 1m7s
This commit is contained in:
@@ -63,8 +63,8 @@ namespace JellyfinPlugin
|
||||
// --- HLS STREAM QUERY REWRITE ---
|
||||
// If the client requests the m3u8 playlist, we simply tell Jellyfin NOT to allow video stream copy.
|
||||
// This elegantly forces transcoding for the BEST supported codec without breaking FFmpeg tags/filters!
|
||||
if (path.EndsWith("master.m3u8", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.EndsWith("main.m3u8", StringComparison.OrdinalIgnoreCase))
|
||||
if (path.Contains("/Videos/", StringComparison.OrdinalIgnoreCase) &&
|
||||
(path.Contains(".m3u8", StringComparison.OrdinalIgnoreCase) || path.Contains("/stream", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
var matchId = Regex.Match(path, @"/Videos/([^/]+)/", RegexOptions.IgnoreCase);
|
||||
if (matchId.Success)
|
||||
@@ -112,106 +112,40 @@ namespace JellyfinPlugin
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogInformation($"[Black Bar Remover DEBUG] Intercepting PlaybackInfo for {itemId}. Found crop in DB: {cropData}. Forcing transcode.");
|
||||
_logger.LogInformation($"[Black Bar Remover DEBUG] Intercepting PlaybackInfo request for {itemId}. Found crop in DB: {cropData}. Disabling DirectPlayProfiles in incoming request.");
|
||||
|
||||
// Kestrel middleware do nadpisania odpowiedzi
|
||||
context.Request.Headers.Remove("Accept-Encoding");
|
||||
var originalBody = context.Response.Body;
|
||||
using var buffer = new MemoryStream();
|
||||
context.Response.Body = buffer;
|
||||
// Modify incoming request body to clear DirectPlayProfiles
|
||||
context.Request.EnableBuffering();
|
||||
string reqJson = string.Empty;
|
||||
using (var reader = new StreamReader(context.Request.Body, Encoding.UTF8, leaveOpen: true))
|
||||
{
|
||||
reqJson = await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
JsonNode rootNode;
|
||||
try
|
||||
{
|
||||
await nextMw().ConfigureAwait(false);
|
||||
rootNode = string.IsNullOrWhiteSpace(reqJson) ? new JsonObject() : (JsonNode.Parse(reqJson) ?? new JsonObject());
|
||||
}
|
||||
catch
|
||||
{
|
||||
context.Response.Body = originalBody;
|
||||
throw;
|
||||
rootNode = new JsonObject();
|
||||
}
|
||||
|
||||
context.Response.Body = originalBody;
|
||||
buffer.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
var isJson = context.Response.ContentType?.Contains("application/json", StringComparison.OrdinalIgnoreCase) ?? false;
|
||||
|
||||
if (!isJson || context.Response.StatusCode != 200)
|
||||
if (rootNode["DeviceProfile"] is not JsonObject devProfile)
|
||||
{
|
||||
await buffer.CopyToAsync(originalBody).ConfigureAwait(false);
|
||||
return;
|
||||
devProfile = new JsonObject();
|
||||
rootNode["DeviceProfile"] = devProfile;
|
||||
}
|
||||
|
||||
string json;
|
||||
using (var reader = new StreamReader(buffer, Encoding.UTF8, true, 1024, leaveOpen: true))
|
||||
{
|
||||
json = await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||
}
|
||||
devProfile["DirectPlayProfiles"] = new JsonArray();
|
||||
|
||||
try
|
||||
{
|
||||
var root = JsonNode.Parse(json);
|
||||
var mediaSources = root?["MediaSources"]?.AsArray();
|
||||
if (mediaSources != null)
|
||||
{
|
||||
bool modified = false;
|
||||
foreach (var source in mediaSources)
|
||||
{
|
||||
if (source == null) continue;
|
||||
|
||||
var obj = source.AsObject();
|
||||
|
||||
// Pobieramy flagi niezależnie od wielkości liter (Jellyfin 10.9+ może używać camelCase)
|
||||
var dpNode = obj.FirstOrDefault(x => x.Key.Equals("SupportsDirectPlay", StringComparison.OrdinalIgnoreCase)).Value;
|
||||
var dsNode = obj.FirstOrDefault(x => x.Key.Equals("SupportsDirectStream", StringComparison.OrdinalIgnoreCase)).Value;
|
||||
|
||||
var dp = dpNode?.GetValue<bool>() ?? false;
|
||||
var ds = dsNode?.GetValue<bool>() ?? false;
|
||||
var newReqBytes = Encoding.UTF8.GetBytes(rootNode.ToJsonString());
|
||||
var reqMs = new MemoryStream(newReqBytes);
|
||||
context.Request.Body = reqMs;
|
||||
context.Request.ContentLength = newReqBytes.Length;
|
||||
|
||||
if (dp || ds)
|
||||
{
|
||||
// Szukamy oryginalnych kluczy, żeby ich nie zdublować w innym casing-u
|
||||
var dpKey = obj.FirstOrDefault(x => x.Key.Equals("SupportsDirectPlay", StringComparison.OrdinalIgnoreCase)).Key ?? "SupportsDirectPlay";
|
||||
var dsKey = obj.FirstOrDefault(x => x.Key.Equals("SupportsDirectStream", StringComparison.OrdinalIgnoreCase)).Key ?? "SupportsDirectStream";
|
||||
var trKey = obj.FirstOrDefault(x => x.Key.Equals("TranscodingReasons", StringComparison.OrdinalIgnoreCase)).Key ?? "TranscodingReasons";
|
||||
|
||||
obj[dpKey] = false;
|
||||
obj[dsKey] = false;
|
||||
|
||||
var transcodingReasons = obj[trKey]?.GetValue<string>();
|
||||
string customReason = "VideoCodecNotSupported";
|
||||
|
||||
if (transcodingReasons == null)
|
||||
{
|
||||
obj[trKey] = customReason;
|
||||
}
|
||||
else if (!transcodingReasons.Contains(customReason))
|
||||
{
|
||||
obj[trKey] = transcodingReasons + "," + customReason;
|
||||
}
|
||||
|
||||
modified = true;
|
||||
_logger.LogInformation($"[Black Bar Remover DEBUG] Modified PlaybackInfo JSON: forced {dpKey}/{dsKey} to false for MediaSource ID {obj.FirstOrDefault(x => x.Key.Equals("Id", StringComparison.OrdinalIgnoreCase)).Value}");
|
||||
}
|
||||
}
|
||||
|
||||
if (modified)
|
||||
{
|
||||
json = root!.ToJsonString(new JsonSerializerOptions { WriteIndented = false });
|
||||
_logger.LogInformation($"[Black Bar Remover DEBUG] Successfully rewrote PlaybackInfo JSON to enforce transcoding.");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning($"[Black Bar Remover DEBUG] Error modifying PlaybackInfo JSON: {ex.Message}");
|
||||
}
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(json);
|
||||
context.Response.ContentType = "application/json; charset=utf-8";
|
||||
context.Response.ContentLength = bytes.Length;
|
||||
context.Response.Headers.Remove("ETag");
|
||||
context.Response.Headers.Remove("Last-Modified");
|
||||
await originalBody.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
|
||||
await nextMw().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user