Fix Jellyfin playlists tab: linked Spotify ID, track count, and user/library filters

This commit is contained in:
2026-02-03 15:13:29 -05:00
parent c7f6783fa2
commit 75c7acb745
2 changed files with 183 additions and 20 deletions

View File

@@ -595,10 +595,10 @@ public class AdminController : ControllerBase
}
/// <summary>
/// Get all playlists from Jellyfin
/// Get all Jellyfin users
/// </summary>
[HttpGet("jellyfin/playlists")]
public async Task<IActionResult> GetJellyfinPlaylists()
[HttpGet("jellyfin/users")]
public async Task<IActionResult> GetJellyfinUsers()
{
if (string.IsNullOrEmpty(_jellyfinSettings.Url) || string.IsNullOrEmpty(_jellyfinSettings.ApiKey))
{
@@ -607,8 +607,117 @@ public class AdminController : ControllerBase
try
{
// Call Jellyfin API to get all playlists
var url = $"{_jellyfinSettings.Url}/Items?IncludeItemTypes=Playlist&Recursive=true&Fields=ProviderIds,ChildCount";
var url = $"{_jellyfinSettings.Url}/Users";
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Add("X-Emby-Authorization", GetJellyfinAuthHeader());
var response = await _jellyfinHttpClient.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
var errorBody = await response.Content.ReadAsStringAsync();
_logger.LogError("Failed to fetch Jellyfin users: {StatusCode} - {Body}", response.StatusCode, errorBody);
return StatusCode((int)response.StatusCode, new { error = "Failed to fetch users from Jellyfin" });
}
var json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
var users = new List<object>();
foreach (var user in doc.RootElement.EnumerateArray())
{
var id = user.GetProperty("Id").GetString();
var name = user.GetProperty("Name").GetString();
users.Add(new { id, name });
}
return Ok(new { users });
}
catch (Exception ex)
{
_logger.LogError(ex, "Error fetching Jellyfin users");
return StatusCode(500, new { error = "Failed to fetch users", details = ex.Message });
}
}
/// <summary>
/// Get all Jellyfin libraries (virtual folders)
/// </summary>
[HttpGet("jellyfin/libraries")]
public async Task<IActionResult> GetJellyfinLibraries()
{
if (string.IsNullOrEmpty(_jellyfinSettings.Url) || string.IsNullOrEmpty(_jellyfinSettings.ApiKey))
{
return BadRequest(new { error = "Jellyfin URL or API key not configured" });
}
try
{
var url = $"{_jellyfinSettings.Url}/Library/VirtualFolders";
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Add("X-Emby-Authorization", GetJellyfinAuthHeader());
var response = await _jellyfinHttpClient.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
var errorBody = await response.Content.ReadAsStringAsync();
_logger.LogError("Failed to fetch Jellyfin libraries: {StatusCode} - {Body}", response.StatusCode, errorBody);
return StatusCode((int)response.StatusCode, new { error = "Failed to fetch libraries from Jellyfin" });
}
var json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
var libraries = new List<object>();
foreach (var lib in doc.RootElement.EnumerateArray())
{
var name = lib.GetProperty("Name").GetString();
var itemId = lib.TryGetProperty("ItemId", out var id) ? id.GetString() : null;
var collectionType = lib.TryGetProperty("CollectionType", out var ct) ? ct.GetString() : null;
libraries.Add(new { id = itemId, name, collectionType });
}
return Ok(new { libraries });
}
catch (Exception ex)
{
_logger.LogError(ex, "Error fetching Jellyfin libraries");
return StatusCode(500, new { error = "Failed to fetch libraries", details = ex.Message });
}
}
/// <summary>
/// Get all playlists from Jellyfin
/// </summary>
[HttpGet("jellyfin/playlists")]
public async Task<IActionResult> GetJellyfinPlaylists([FromQuery] string? userId = null, [FromQuery] string? parentId = null)
{
if (string.IsNullOrEmpty(_jellyfinSettings.Url) || string.IsNullOrEmpty(_jellyfinSettings.ApiKey))
{
return BadRequest(new { error = "Jellyfin URL or API key not configured" });
}
try
{
// Build URL with optional userId and parentId (library) filters
var url = $"{_jellyfinSettings.Url}/Items?IncludeItemTypes=Playlist&Recursive=true&Fields=ProviderIds,ChildCount,RecursiveItemCount,SongCount";
if (!string.IsNullOrEmpty(userId))
{
url += $"&UserId={userId}";
}
if (!string.IsNullOrEmpty(parentId))
{
url += $"&ParentId={parentId}";
}
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Add("X-Emby-Authorization", GetJellyfinAuthHeader());
@@ -633,21 +742,21 @@ public class AdminController : ControllerBase
{
var id = item.GetProperty("Id").GetString();
var name = item.GetProperty("Name").GetString();
var childCount = item.TryGetProperty("ChildCount", out var cc) ? cc.GetInt32() : 0;
// Check if this playlist has a linked Spotify ID in ProviderIds
string? linkedSpotifyId = null;
if (item.TryGetProperty("ProviderIds", out var providerIds))
{
if (providerIds.TryGetProperty("Spotify", out var spotifyId))
{
linkedSpotifyId = spotifyId.GetString();
}
}
// Try multiple fields for track count - Jellyfin may use different fields
var childCount = 0;
if (item.TryGetProperty("ChildCount", out var cc) && cc.ValueKind == JsonValueKind.Number)
childCount = cc.GetInt32();
else if (item.TryGetProperty("SongCount", out var sc) && sc.ValueKind == JsonValueKind.Number)
childCount = sc.GetInt32();
else if (item.TryGetProperty("RecursiveItemCount", out var ric) && ric.ValueKind == JsonValueKind.Number)
childCount = ric.GetInt32();
// Check if this playlist is already configured in allstarr
var isConfigured = _spotifyImportSettings.Playlists.Any(p =>
p.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
// Check if this playlist is configured in allstarr and get linked Spotify ID
var configuredPlaylist = _spotifyImportSettings.Playlists
.FirstOrDefault(p => p.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
var isConfigured = configuredPlaylist != null;
var linkedSpotifyId = configuredPlaylist?.Id;
playlists.Add(new
{