mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
Fix multiple Jellyfin proxy issues and improvements
- Fix playback progress reporting by wrapping POST bodies in required field names
- Fix cache cleanup by updating last access time when streaming files
- Fix Artists/{id}/Similar endpoint proxying to correct Jellyfin endpoint
- Add ' - SW' suffix to external albums and artists for better identification
- Register SquidWTFSettings configuration to enable quality settings
- Remove unused code and improve debugging logs
This commit is contained in:
@@ -811,6 +811,16 @@ public class JellyfinController : ControllerBase
|
||||
|
||||
if (localPath != null && System.IO.File.Exists(localPath))
|
||||
{
|
||||
// Update last access time for cache cleanup
|
||||
try
|
||||
{
|
||||
System.IO.File.SetLastAccessTimeUtc(localPath, DateTime.UtcNow);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to update last access time for {Path}", localPath);
|
||||
}
|
||||
|
||||
var stream = System.IO.File.OpenRead(localPath);
|
||||
return File(stream, GetContentType(localPath), enableRangeProcessing: true);
|
||||
}
|
||||
@@ -1202,7 +1212,7 @@ public class JellyfinController : ControllerBase
|
||||
/// </summary>
|
||||
[HttpGet("Items/{itemId}/Similar")]
|
||||
[HttpGet("Songs/{itemId}/Similar")]
|
||||
[HttpGet("Artists/{artistId}/Similar")]
|
||||
[HttpGet("Artists/{itemId}/Similar")]
|
||||
public async Task<IActionResult> GetSimilarItems(
|
||||
string itemId,
|
||||
[FromQuery] int limit = 50,
|
||||
@@ -1266,7 +1276,11 @@ public class JellyfinController : ControllerBase
|
||||
}
|
||||
}
|
||||
|
||||
// For local items, proxy to Jellyfin
|
||||
// For local items, determine the correct endpoint based on the request path
|
||||
var endpoint = Request.Path.Value?.Contains("/Artists/", StringComparison.OrdinalIgnoreCase) == true
|
||||
? $"Artists/{itemId}/Similar"
|
||||
: $"Items/{itemId}/Similar";
|
||||
|
||||
var queryParams = new Dictionary<string, string>
|
||||
{
|
||||
["limit"] = limit.ToString()
|
||||
@@ -1282,7 +1296,7 @@ public class JellyfinController : ControllerBase
|
||||
queryParams["userId"] = userId;
|
||||
}
|
||||
|
||||
var result = await _proxyService.GetJsonAsync($"Items/{itemId}/Similar", queryParams, Request.Headers);
|
||||
var result = await _proxyService.GetJsonAsync(endpoint, queryParams, Request.Headers);
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
@@ -1532,28 +1546,32 @@ public class JellyfinController : ControllerBase
|
||||
|
||||
// Read body using StreamReader with proper encoding
|
||||
string body;
|
||||
using (var reader = new StreamReader(Request.Body, System.Text.Encoding.UTF8, leaveOpen: true))
|
||||
using (var reader = new StreamReader(Request.Body, System.Text.Encoding.UTF8, detectEncodingFromByteOrderMarks: false, bufferSize: 1024, leaveOpen: true))
|
||||
{
|
||||
body = await reader.ReadToEndAsync();
|
||||
}
|
||||
|
||||
// Reset stream position after reading
|
||||
// Reset stream position after reading so it can be read again if needed
|
||||
Request.Body.Position = 0;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(body))
|
||||
{
|
||||
_logger.LogWarning("Empty POST body for {Path}, ContentLength={ContentLength}, ContentType={ContentType}",
|
||||
_logger.LogWarning("Empty POST body received from client for {Path}, ContentLength={ContentLength}, ContentType={ContentType}",
|
||||
fullPath, Request.ContentLength, Request.ContentType);
|
||||
|
||||
// Log all headers to debug
|
||||
_logger.LogWarning("Request headers: {Headers}",
|
||||
string.Join(", ", Request.Headers.Select(h => $"{h.Key}={h.Value}")));
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("POST body for {Path}: {BodyLength} bytes, ContentType={ContentType}",
|
||||
_logger.LogInformation("POST body received from client for {Path}: {BodyLength} bytes, ContentType={ContentType}",
|
||||
fullPath, body.Length, Request.ContentType);
|
||||
|
||||
// Always log body content for playback endpoints to debug the issue
|
||||
if (fullPath.Contains("Playing", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_logger.LogInformation("POST body content: {Body}", body);
|
||||
_logger.LogInformation("POST body content from client: {Body}", body);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -142,6 +142,16 @@ public class SubsonicController : ControllerBase
|
||||
|
||||
if (localPath != null && System.IO.File.Exists(localPath))
|
||||
{
|
||||
// Update last access time for cache cleanup
|
||||
try
|
||||
{
|
||||
System.IO.File.SetLastAccessTimeUtc(localPath, DateTime.UtcNow);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to update last access time for {Path}", localPath);
|
||||
}
|
||||
|
||||
var stream = System.IO.File.OpenRead(localPath);
|
||||
return File(stream, GetContentType(localPath), enableRangeProcessing: true);
|
||||
}
|
||||
|
||||
@@ -71,6 +71,8 @@ builder.Services.Configure<DeezerSettings>(
|
||||
builder.Configuration.GetSection("Deezer"));
|
||||
builder.Services.Configure<QobuzSettings>(
|
||||
builder.Configuration.GetSection("Qobuz"));
|
||||
builder.Services.Configure<SquidWTFSettings>(
|
||||
builder.Configuration.GetSection("SquidWTF"));
|
||||
builder.Services.Configure<RedisSettings>(
|
||||
builder.Configuration.GetSection("Redis"));
|
||||
|
||||
|
||||
@@ -256,17 +256,40 @@ public class JellyfinProxyService
|
||||
|
||||
using var request = new HttpRequestMessage(HttpMethod.Post, url);
|
||||
|
||||
// Create content from body string
|
||||
if (!string.IsNullOrEmpty(body))
|
||||
// Handle special case for playback endpoints - Jellyfin expects wrapped body
|
||||
var bodyToSend = body;
|
||||
if (!string.IsNullOrWhiteSpace(body))
|
||||
{
|
||||
request.Content = new StringContent(body, System.Text.Encoding.UTF8, "application/json");
|
||||
_logger.LogDebug("POST body length: {Length} bytes", body.Length);
|
||||
// Check if this is a playback progress endpoint
|
||||
if (endpoint.Contains("Sessions/Playing/Progress", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Wrap the body in playbackProgressInfo field
|
||||
bodyToSend = $"{{\"playbackProgressInfo\":{body}}}";
|
||||
_logger.LogDebug("Wrapped body for playback progress endpoint");
|
||||
}
|
||||
else if (endpoint.Contains("Sessions/Playing/Stopped", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Wrap the body in playbackStopInfo field
|
||||
bodyToSend = $"{{\"playbackStopInfo\":{body}}}";
|
||||
_logger.LogDebug("Wrapped body for playback stopped endpoint");
|
||||
}
|
||||
else if (endpoint.Contains("Sessions/Playing", StringComparison.OrdinalIgnoreCase) &&
|
||||
!endpoint.Contains("Progress", StringComparison.OrdinalIgnoreCase) &&
|
||||
!endpoint.Contains("Stopped", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Wrap the body in playbackStartInfo field for /Sessions/Playing
|
||||
bodyToSend = $"{{\"playbackStartInfo\":{body}}}";
|
||||
_logger.LogDebug("Wrapped body for playback start endpoint");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("POST body is empty for {Url}", url);
|
||||
bodyToSend = "{}";
|
||||
_logger.LogWarning("POST body was empty for {Url}, sending empty JSON object", url);
|
||||
}
|
||||
|
||||
request.Content = new StringContent(bodyToSend, System.Text.Encoding.UTF8, "application/json");
|
||||
|
||||
bool authHeaderAdded = false;
|
||||
|
||||
// Forward authentication headers from client (case-insensitive)
|
||||
@@ -312,12 +335,12 @@ public class JellyfinProxyService
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("POST to Jellyfin: {Url}, body length: {Length} bytes", url, body.Length);
|
||||
_logger.LogInformation("POST to Jellyfin: {Url}, body length: {Length} bytes", url, bodyToSend.Length);
|
||||
|
||||
// Log body content for playback endpoints to debug
|
||||
if (endpoint.Contains("Playing", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_logger.LogInformation("Sending body to Jellyfin: {Body}", body);
|
||||
_logger.LogInformation("Sending body to Jellyfin: {Body}", bodyToSend);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -304,10 +304,17 @@ public class JellyfinResponseBuilder
|
||||
/// </summary>
|
||||
public Dictionary<string, object?> ConvertAlbumToJellyfinItem(Album album)
|
||||
{
|
||||
// Add " - SW" suffix to external album names
|
||||
var albumName = album.Title;
|
||||
if (!album.IsLocal)
|
||||
{
|
||||
albumName = $"{album.Title} - SW";
|
||||
}
|
||||
|
||||
var item = new Dictionary<string, object?>
|
||||
{
|
||||
["Id"] = album.Id,
|
||||
["Name"] = album.Title,
|
||||
["Name"] = albumName,
|
||||
["ServerId"] = "allstarr",
|
||||
["Type"] = "MusicAlbum",
|
||||
["IsFolder"] = true,
|
||||
@@ -328,10 +335,10 @@ public class JellyfinResponseBuilder
|
||||
},
|
||||
["BackdropImageTags"] = new string[0],
|
||||
["ImageBlurHashes"] = new Dictionary<string, object>(),
|
||||
["LocationType"] = "FileSystem", // External content appears as local files to clients
|
||||
["MediaType"] = (object?)null, // Match Jellyfin structure
|
||||
["ChannelId"] = (object?)null, // Match Jellyfin structure
|
||||
["CollectionType"] = (object?)null, // Match Jellyfin structure
|
||||
["LocationType"] = "FileSystem",
|
||||
["MediaType"] = (object?)null,
|
||||
["ChannelId"] = (object?)null,
|
||||
["CollectionType"] = (object?)null,
|
||||
["UserData"] = new Dictionary<string, object>
|
||||
{
|
||||
["PlaybackPositionTicks"] = 0,
|
||||
@@ -364,10 +371,17 @@ public class JellyfinResponseBuilder
|
||||
/// </summary>
|
||||
public Dictionary<string, object?> ConvertArtistToJellyfinItem(Artist artist)
|
||||
{
|
||||
// Add " - SW" suffix to external artist names
|
||||
var artistName = artist.Name;
|
||||
if (!artist.IsLocal)
|
||||
{
|
||||
artistName = $"{artist.Name} - SW";
|
||||
}
|
||||
|
||||
var item = new Dictionary<string, object?>
|
||||
{
|
||||
["Id"] = artist.Id,
|
||||
["Name"] = artist.Name,
|
||||
["Name"] = artistName,
|
||||
["ServerId"] = "allstarr",
|
||||
["Type"] = "MusicArtist",
|
||||
["IsFolder"] = true,
|
||||
|
||||
@@ -21,7 +21,6 @@ public class SquidWTFDownloadService : BaseDownloadService
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly SemaphoreSlim _requestLock = new(1, 1);
|
||||
private readonly string? _preferredQuality;
|
||||
private readonly SquidWTFSettings _squidwtfSettings;
|
||||
|
||||
private DateTime _lastRequestTime = DateTime.MinValue;
|
||||
|
||||
Reference in New Issue
Block a user