Support both favorite endpoint formats
Some checks failed
CI / build-and-test (push) Has been cancelled

- Add /UserFavoriteItems/{itemId} route (official Jellyfin API)
- Keep /Users/{userId}/FavoriteItems/{itemId} for compatibility
- Support userId in query string or path
- Add route logging to debug which endpoint is being called
This commit is contained in:
2026-02-01 18:29:31 -05:00
parent 1326f1b3ab
commit 8e6eb5cc4a

View File

@@ -1130,11 +1130,20 @@ public class JellyfinController : ControllerBase
/// <summary>
/// Marks an item as favorite. For playlists, triggers a full download.
/// Supports both /Users/{userId}/FavoriteItems/{itemId} and /UserFavoriteItems/{itemId}?userId=xxx
/// </summary>
[HttpPost("Users/{userId}/FavoriteItems/{itemId}")]
public async Task<IActionResult> MarkFavorite(string userId, string itemId)
[HttpPost("UserFavoriteItems/{itemId}")]
public async Task<IActionResult> MarkFavorite(string itemId, string? userId = null)
{
_logger.LogInformation("MarkFavorite called: userId={UserId}, itemId={ItemId}", userId, itemId);
// Get userId from query string if not in path
if (string.IsNullOrEmpty(userId))
{
userId = Request.Query["userId"].ToString();
}
_logger.LogInformation("MarkFavorite called: userId={UserId}, itemId={ItemId}, route={Route}",
userId, itemId, Request.Path);
// Check if this is an external playlist - trigger download
if (PlaylistIdHelper.IsExternalPlaylist(itemId))
@@ -1195,7 +1204,13 @@ public class JellyfinController : ControllerBase
}
// For local Jellyfin items, proxy the request through
var endpoint = $"Users/{userId}/FavoriteItems/{itemId}";
// Use the official Jellyfin endpoint format
var endpoint = $"UserFavoriteItems/{itemId}";
if (!string.IsNullOrEmpty(userId))
{
endpoint = $"{endpoint}?userId={userId}";
}
_logger.LogInformation("Proxying favorite request to Jellyfin: {Endpoint}", endpoint);
var result = await _proxyService.PostJsonAsync(endpoint, "{}", Request.Headers);
@@ -1211,11 +1226,20 @@ public class JellyfinController : ControllerBase
/// <summary>
/// Removes an item from favorites.
/// Supports both /Users/{userId}/FavoriteItems/{itemId} and /UserFavoriteItems/{itemId}?userId=xxx
/// </summary>
[HttpDelete("Users/{userId}/FavoriteItems/{itemId}")]
public async Task<IActionResult> UnmarkFavorite(string userId, string itemId)
[HttpDelete("UserFavoriteItems/{itemId}")]
public async Task<IActionResult> UnmarkFavorite(string itemId, string? userId = null)
{
_logger.LogInformation("UnmarkFavorite called: userId={UserId}, itemId={ItemId}", userId, itemId);
// Get userId from query string if not in path
if (string.IsNullOrEmpty(userId))
{
userId = Request.Query["userId"].ToString();
}
_logger.LogInformation("UnmarkFavorite called: userId={UserId}, itemId={ItemId}, route={Route}",
userId, itemId, Request.Path);
// External items can't be unfavorited (they're not really favorited in Jellyfin)
var (isExternal, _, _) = _localLibraryService.ParseSongId(itemId);
@@ -1230,7 +1254,13 @@ public class JellyfinController : ControllerBase
}
// Proxy to Jellyfin to unfavorite
var endpoint = $"Users/{userId}/FavoriteItems/{itemId}";
// Use the official Jellyfin endpoint format
var endpoint = $"UserFavoriteItems/{itemId}";
if (!string.IsNullOrEmpty(userId))
{
endpoint = $"{endpoint}?userId={userId}";
}
_logger.LogInformation("Proxying unfavorite request to Jellyfin: {Endpoint}", endpoint);
var result = await _proxyService.DeleteAsync(endpoint, Request.Headers);