mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
Fix external mapping deserialization and suppress cache MISS logs
- Fixed RuntimeBinderException when processing external mappings by replacing dynamic with JsonDocument parsing - Suppressed cache MISS logs for manual/external mappings (they're expected to be missing most of the time) - Only log manual/external mapping HITs at INFO level, other cache operations at DEBUG level - Applied fix to SpotifyTrackMatchingService (2 locations) and AdminController (2 locations)
This commit is contained in:
@@ -531,14 +531,27 @@ public class AdminController : ControllerBase
|
|||||||
{
|
{
|
||||||
// Check for external manual mapping
|
// Check for external manual mapping
|
||||||
var externalMappingKey = $"spotify:external-map:{decodedName}:{track.SpotifyId}";
|
var externalMappingKey = $"spotify:external-map:{decodedName}:{track.SpotifyId}";
|
||||||
var externalMapping = await _cache.GetAsync<dynamic>(externalMappingKey);
|
var externalMappingJson = await _cache.GetStringAsync(externalMappingKey);
|
||||||
|
|
||||||
if (HasValue(externalMapping))
|
if (!string.IsNullOrEmpty(externalMappingJson))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var provider = externalMapping?.provider?.ToString();
|
using var doc = JsonDocument.Parse(externalMappingJson);
|
||||||
var externalId = externalMapping?.id?.ToString();
|
var root = doc.RootElement;
|
||||||
|
|
||||||
|
string? provider = null;
|
||||||
|
string? externalId = null;
|
||||||
|
|
||||||
|
if (root.TryGetProperty("provider", out var providerEl))
|
||||||
|
{
|
||||||
|
provider = providerEl.GetString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root.TryGetProperty("id", out var idEl))
|
||||||
|
{
|
||||||
|
externalId = idEl.GetString();
|
||||||
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(provider) && !string.IsNullOrEmpty(externalId))
|
if (!string.IsNullOrEmpty(provider) && !string.IsNullOrEmpty(externalId))
|
||||||
{
|
{
|
||||||
@@ -546,7 +559,7 @@ public class AdminController : ControllerBase
|
|||||||
isLocal = false;
|
isLocal = false;
|
||||||
externalProvider = provider;
|
externalProvider = provider;
|
||||||
_logger.LogDebug("✓ Manual external mapping found for {Title}: {Provider} {ExternalId}",
|
_logger.LogDebug("✓ Manual external mapping found for {Title}: {Provider} {ExternalId}",
|
||||||
track.Title, (object?)provider, (object?)externalId);
|
track.Title, provider, externalId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -655,13 +668,22 @@ public class AdminController : ControllerBase
|
|||||||
{
|
{
|
||||||
// Check for external manual mapping
|
// Check for external manual mapping
|
||||||
var externalMappingKey = $"spotify:external-map:{decodedName}:{track.SpotifyId}";
|
var externalMappingKey = $"spotify:external-map:{decodedName}:{track.SpotifyId}";
|
||||||
var externalMapping = await _cache.GetAsync<dynamic>(externalMappingKey);
|
var externalMappingJson = await _cache.GetStringAsync(externalMappingKey);
|
||||||
|
|
||||||
if (HasValue(externalMapping))
|
if (!string.IsNullOrEmpty(externalMappingJson))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var provider = externalMapping?.provider?.ToString();
|
using var doc = JsonDocument.Parse(externalMappingJson);
|
||||||
|
var root = doc.RootElement;
|
||||||
|
|
||||||
|
string? provider = null;
|
||||||
|
|
||||||
|
if (root.TryGetProperty("provider", out var providerEl))
|
||||||
|
{
|
||||||
|
provider = providerEl.GetString();
|
||||||
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(provider))
|
if (!string.IsNullOrEmpty(provider))
|
||||||
{
|
{
|
||||||
isLocal = false;
|
isLocal = false;
|
||||||
|
|||||||
@@ -57,13 +57,28 @@ public class RedisCacheService
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var value = await _db!.StringGetAsync(key);
|
var value = await _db!.StringGetAsync(key);
|
||||||
|
|
||||||
|
// Only log manual/external mapping HITs (not MISSes - they're expected)
|
||||||
|
var isManualMapping = key.Contains(":manual-map:") || key.Contains(":external-map:");
|
||||||
|
|
||||||
if (value.HasValue)
|
if (value.HasValue)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Redis cache HIT: {Key}", key);
|
if (isManualMapping)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Redis cache HIT: {Key}", key);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogDebug("Redis cache HIT: {Key}", key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Redis cache MISS: {Key}", key);
|
// Don't log MISS for manual/external mappings - they're expected to be missing most of the time
|
||||||
|
if (!isManualMapping)
|
||||||
|
{
|
||||||
|
_logger.LogDebug("Redis cache MISS: {Key}", key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@@ -105,7 +120,16 @@ public class RedisCacheService
|
|||||||
var result = await _db!.StringSetAsync(key, value, expiry);
|
var result = await _db!.StringSetAsync(key, value, expiry);
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Redis cache SET: {Key} (TTL: {Expiry})", key, expiry?.ToString() ?? "none");
|
// Log manual/external mappings at INFO level, others at DEBUG
|
||||||
|
var isManualMapping = key.Contains(":manual-map:") || key.Contains(":external-map:");
|
||||||
|
if (isManualMapping)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Redis cache SET: {Key} (TTL: {Expiry})", key, expiry?.ToString() ?? "none");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogDebug("Redis cache SET: {Key} (TTL: {Expiry})", key, expiry?.ToString() ?? "none");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -339,9 +339,9 @@ public class SpotifyTrackMatchingService : BackgroundService
|
|||||||
var manualMapping = await _cache.GetAsync<string>(manualMappingKey);
|
var manualMapping = await _cache.GetAsync<string>(manualMappingKey);
|
||||||
|
|
||||||
var externalMappingKey = $"spotify:external-map:{playlistName}:{track.SpotifyId}";
|
var externalMappingKey = $"spotify:external-map:{playlistName}:{track.SpotifyId}";
|
||||||
var externalMapping = await _cache.GetAsync<dynamic>(externalMappingKey);
|
var externalMappingJson = await _cache.GetStringAsync(externalMappingKey);
|
||||||
|
|
||||||
var hasManualMapping = !string.IsNullOrEmpty(manualMapping) || HasValue(externalMapping);
|
var hasManualMapping = !string.IsNullOrEmpty(manualMapping) || !string.IsNullOrEmpty(externalMappingJson);
|
||||||
var isInCache = existingMatched.Any(m => m.SpotifyId == track.SpotifyId);
|
var isInCache = existingMatched.Any(m => m.SpotifyId == track.SpotifyId);
|
||||||
|
|
||||||
// If track has manual mapping but isn't in cache, we need to rebuild
|
// If track has manual mapping but isn't in cache, we need to rebuild
|
||||||
@@ -847,14 +847,27 @@ public class SpotifyTrackMatchingService : BackgroundService
|
|||||||
if (!matchedJellyfinItem.HasValue)
|
if (!matchedJellyfinItem.HasValue)
|
||||||
{
|
{
|
||||||
var externalMappingKey = $"spotify:external-map:{playlistName}:{spotifyTrack.SpotifyId}";
|
var externalMappingKey = $"spotify:external-map:{playlistName}:{spotifyTrack.SpotifyId}";
|
||||||
var externalMapping = await _cache.GetAsync<dynamic>(externalMappingKey);
|
var externalMappingJson = await _cache.GetStringAsync(externalMappingKey);
|
||||||
|
|
||||||
if (HasValue(externalMapping))
|
if (!string.IsNullOrEmpty(externalMappingJson))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var provider = externalMapping?.provider?.ToString();
|
using var doc = JsonDocument.Parse(externalMappingJson);
|
||||||
var externalId = externalMapping?.id?.ToString();
|
var root = doc.RootElement;
|
||||||
|
|
||||||
|
string? provider = null;
|
||||||
|
string? externalId = null;
|
||||||
|
|
||||||
|
if (root.TryGetProperty("provider", out var providerEl))
|
||||||
|
{
|
||||||
|
provider = providerEl.GetString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root.TryGetProperty("id", out var idEl))
|
||||||
|
{
|
||||||
|
externalId = idEl.GetString();
|
||||||
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(provider) && !string.IsNullOrEmpty(externalId))
|
if (!string.IsNullOrEmpty(provider) && !string.IsNullOrEmpty(externalId))
|
||||||
{
|
{
|
||||||
@@ -879,7 +892,7 @@ public class SpotifyTrackMatchingService : BackgroundService
|
|||||||
});
|
});
|
||||||
|
|
||||||
_logger.LogInformation("✓ Using manual external mapping for {Title}: {Provider} {ExternalId}",
|
_logger.LogInformation("✓ Using manual external mapping for {Title}: {Provider} {ExternalId}",
|
||||||
spotifyTrack.Title, (object?)provider, (object?)externalId);
|
spotifyTrack.Title, provider, externalId);
|
||||||
continue; // Skip to next track
|
continue; // Skip to next track
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user