mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-04-27 03:53:10 -04:00
fix(jellyfin): return cached search responses as raw json
CI / build-and-test (push) Has been cancelled
CI / build-and-test (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,38 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using allstarr.Controllers;
|
||||||
|
|
||||||
|
namespace allstarr.Tests;
|
||||||
|
|
||||||
|
public class JellyfinSearchResponseSerializationTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void SerializeSearchResponseJson_PreservesPascalCaseShape()
|
||||||
|
{
|
||||||
|
var payload = new
|
||||||
|
{
|
||||||
|
Items = new[]
|
||||||
|
{
|
||||||
|
new Dictionary<string, object?>
|
||||||
|
{
|
||||||
|
["Name"] = "BTS",
|
||||||
|
["Type"] = "MusicAlbum"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
TotalRecordCount = 1,
|
||||||
|
StartIndex = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
var method = typeof(JellyfinController).GetMethod(
|
||||||
|
"SerializeSearchResponseJson",
|
||||||
|
BindingFlags.NonPublic | BindingFlags.Static);
|
||||||
|
|
||||||
|
Assert.NotNull(method);
|
||||||
|
|
||||||
|
var closedMethod = method!.MakeGenericMethod(payload.GetType());
|
||||||
|
var json = (string)closedMethod.Invoke(null, new object?[] { payload })!;
|
||||||
|
|
||||||
|
Assert.Equal(
|
||||||
|
"{\"Items\":[{\"Name\":\"BTS\",\"Type\":\"MusicAlbum\"}],\"TotalRecordCount\":1,\"StartIndex\":0}",
|
||||||
|
json);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@ public partial class JellyfinController
|
|||||||
{
|
{
|
||||||
var boundSearchTerm = searchTerm;
|
var boundSearchTerm = searchTerm;
|
||||||
searchTerm = GetEffectiveSearchTerm(searchTerm, Request.QueryString.Value);
|
searchTerm = GetEffectiveSearchTerm(searchTerm, Request.QueryString.Value);
|
||||||
|
string? searchCacheKey = null;
|
||||||
|
|
||||||
// AlbumArtistIds takes precedence over ArtistIds if both are provided
|
// AlbumArtistIds takes precedence over ArtistIds if both are provided
|
||||||
var effectiveArtistIds = albumArtistIds ?? artistIds;
|
var effectiveArtistIds = albumArtistIds ?? artistIds;
|
||||||
@@ -181,7 +182,7 @@ public partial class JellyfinController
|
|||||||
// Check cache for search results (only cache pure searches, not filtered searches)
|
// Check cache for search results (only cache pure searches, not filtered searches)
|
||||||
if (string.IsNullOrWhiteSpace(effectiveArtistIds) && string.IsNullOrWhiteSpace(albumIds))
|
if (string.IsNullOrWhiteSpace(effectiveArtistIds) && string.IsNullOrWhiteSpace(albumIds))
|
||||||
{
|
{
|
||||||
var cacheKey = CacheKeyBuilder.BuildSearchKey(
|
searchCacheKey = CacheKeyBuilder.BuildSearchKey(
|
||||||
searchTerm,
|
searchTerm,
|
||||||
includeItemTypes,
|
includeItemTypes,
|
||||||
limit,
|
limit,
|
||||||
@@ -192,12 +193,12 @@ public partial class JellyfinController
|
|||||||
recursive,
|
recursive,
|
||||||
userId,
|
userId,
|
||||||
Request.Query["IsFavorite"].ToString());
|
Request.Query["IsFavorite"].ToString());
|
||||||
var cachedResult = await _cache.GetAsync<object>(cacheKey);
|
var cachedResult = await _cache.GetStringAsync(searchCacheKey);
|
||||||
|
|
||||||
if (cachedResult != null)
|
if (!string.IsNullOrWhiteSpace(cachedResult))
|
||||||
{
|
{
|
||||||
_logger.LogInformation("SEARCH TRACE: cache hit for key '{CacheKey}'", cacheKey);
|
_logger.LogInformation("SEARCH TRACE: cache hit for key '{CacheKey}'", searchCacheKey);
|
||||||
return new JsonResult(cachedResult);
|
return Content(cachedResult, "application/json");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -538,24 +539,16 @@ public partial class JellyfinController
|
|||||||
TotalRecordCount = items.Count,
|
TotalRecordCount = items.Count,
|
||||||
StartIndex = startIndex
|
StartIndex = startIndex
|
||||||
};
|
};
|
||||||
|
var json = SerializeSearchResponseJson(response);
|
||||||
|
|
||||||
// Cache search results in Redis using the configured search TTL.
|
// Cache search results in Redis using the configured search TTL.
|
||||||
if (!string.IsNullOrWhiteSpace(searchTerm) && string.IsNullOrWhiteSpace(effectiveArtistIds))
|
if (!string.IsNullOrWhiteSpace(searchTerm) &&
|
||||||
|
string.IsNullOrWhiteSpace(effectiveArtistIds) &&
|
||||||
|
!string.IsNullOrWhiteSpace(searchCacheKey))
|
||||||
{
|
{
|
||||||
if (externalHasRequestedTypeResults)
|
if (externalHasRequestedTypeResults)
|
||||||
{
|
{
|
||||||
var cacheKey = CacheKeyBuilder.BuildSearchKey(
|
await _cache.SetStringAsync(searchCacheKey, json, CacheExtensions.SearchResultsTTL);
|
||||||
searchTerm,
|
|
||||||
includeItemTypes,
|
|
||||||
limit,
|
|
||||||
startIndex,
|
|
||||||
parentId,
|
|
||||||
sortBy,
|
|
||||||
Request.Query["SortOrder"].ToString(),
|
|
||||||
recursive,
|
|
||||||
userId,
|
|
||||||
Request.Query["IsFavorite"].ToString());
|
|
||||||
await _cache.SetAsync(cacheKey, response, CacheExtensions.SearchResultsTTL);
|
|
||||||
_logger.LogDebug("💾 Cached search results for '{SearchTerm}' ({Minutes} min TTL)", searchTerm,
|
_logger.LogDebug("💾 Cached search results for '{SearchTerm}' ({Minutes} min TTL)", searchTerm,
|
||||||
CacheExtensions.SearchResultsTTL.TotalMinutes);
|
CacheExtensions.SearchResultsTTL.TotalMinutes);
|
||||||
}
|
}
|
||||||
@@ -570,12 +563,6 @@ public partial class JellyfinController
|
|||||||
|
|
||||||
_logger.LogDebug("About to serialize response...");
|
_logger.LogDebug("About to serialize response...");
|
||||||
|
|
||||||
var json = System.Text.Json.JsonSerializer.Serialize(response, new System.Text.Json.JsonSerializerOptions
|
|
||||||
{
|
|
||||||
PropertyNamingPolicy = null,
|
|
||||||
DictionaryKeyPolicy = null
|
|
||||||
});
|
|
||||||
|
|
||||||
if (_logger.IsEnabled(LogLevel.Debug))
|
if (_logger.IsEnabled(LogLevel.Debug))
|
||||||
{
|
{
|
||||||
var preview = json.Length > 200 ? json[..200] : json;
|
var preview = json.Length > 200 ? json[..200] : json;
|
||||||
@@ -591,6 +578,15 @@ public partial class JellyfinController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string SerializeSearchResponseJson<T>(T response) where T : class
|
||||||
|
{
|
||||||
|
return JsonSerializer.Serialize(response, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNamingPolicy = null,
|
||||||
|
DictionaryKeyPolicy = null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets child items of a parent (tracks in album, albums for artist).
|
/// Gets child items of a parent (tracks in album, albums for artist).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user