mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-10 07:58:39 -05:00
feat: add admin UI improvements and forwarded headers support
Enhanced admin configuration UI with missing fields, required indicators, and sp_dc warning. Added Spotify playlist selector for linking with auto-filtering of already-linked playlists. Configured forwarded headers to pass real client IPs from nginx to Jellyfin. Improved track view modal error handling.
This commit is contained in:
@@ -1379,6 +1379,12 @@ public class AdminController : ControllerBase
|
||||
{
|
||||
return Ok(new
|
||||
{
|
||||
backendType = _configuration.GetValue<string>("Backend:Type") ?? "Jellyfin",
|
||||
musicService = _configuration.GetValue<string>("MusicService") ?? "SquidWTF",
|
||||
explicitFilter = _configuration.GetValue<string>("ExplicitFilter") ?? "All",
|
||||
enableExternalPlaylists = _configuration.GetValue<bool>("EnableExternalPlaylists", false),
|
||||
playlistsDirectory = _configuration.GetValue<string>("PlaylistsDirectory") ?? "(not set)",
|
||||
redisEnabled = _configuration.GetValue<bool>("Redis:Enabled", false),
|
||||
spotifyApi = new
|
||||
{
|
||||
enabled = _spotifyApiSettings.Enabled,
|
||||
@@ -1392,6 +1398,9 @@ public class AdminController : ControllerBase
|
||||
{
|
||||
enabled = _spotifyImportSettings.Enabled,
|
||||
matchingIntervalHours = _spotifyImportSettings.MatchingIntervalHours,
|
||||
syncStartHour = _spotifyImportSettings.SyncStartHour,
|
||||
syncStartMinute = _spotifyImportSettings.SyncStartMinute,
|
||||
syncWindowHours = _spotifyImportSettings.SyncWindowHours,
|
||||
playlists = _spotifyImportSettings.Playlists.Select(p => new
|
||||
{
|
||||
name = p.Name,
|
||||
@@ -1919,6 +1928,111 @@ public class AdminController : ControllerBase
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all playlists from the user's Spotify account
|
||||
/// </summary>
|
||||
[HttpGet("spotify/user-playlists")]
|
||||
public async Task<IActionResult> GetSpotifyUserPlaylists()
|
||||
{
|
||||
if (!_spotifyApiSettings.Enabled || string.IsNullOrEmpty(_spotifyApiSettings.SessionCookie))
|
||||
{
|
||||
return BadRequest(new { error = "Spotify API not configured. Please set sp_dc session cookie." });
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var token = await _spotifyClient.GetWebAccessTokenAsync();
|
||||
if (string.IsNullOrEmpty(token))
|
||||
{
|
||||
return StatusCode(401, new { error = "Failed to authenticate with Spotify. Check your sp_dc cookie." });
|
||||
}
|
||||
|
||||
// Get list of already-configured Spotify playlist IDs
|
||||
var configuredPlaylists = await ReadPlaylistsFromEnvFile();
|
||||
var linkedSpotifyIds = new HashSet<string>(
|
||||
configuredPlaylists.Select(p => p.Id),
|
||||
StringComparer.OrdinalIgnoreCase
|
||||
);
|
||||
|
||||
var playlists = new List<object>();
|
||||
var offset = 0;
|
||||
const int limit = 50;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var url = $"https://api.spotify.com/v1/me/playlists?offset={offset}&limit={limit}";
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
|
||||
|
||||
var response = await _jellyfinHttpClient.SendAsync(request);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
_logger.LogWarning("Failed to fetch Spotify playlists: {StatusCode}", response.StatusCode);
|
||||
break;
|
||||
}
|
||||
|
||||
var json = await response.Content.ReadAsStringAsync();
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var root = doc.RootElement;
|
||||
|
||||
if (!root.TryGetProperty("items", out var items) || items.GetArrayLength() == 0)
|
||||
break;
|
||||
|
||||
foreach (var item in items.EnumerateArray())
|
||||
{
|
||||
var id = item.TryGetProperty("id", out var itemId) ? itemId.GetString() : null;
|
||||
var name = item.TryGetProperty("name", out var n) ? n.GetString() : null;
|
||||
var trackCount = 0;
|
||||
|
||||
if (item.TryGetProperty("tracks", out var tracks) &&
|
||||
tracks.TryGetProperty("total", out var total))
|
||||
{
|
||||
trackCount = total.GetInt32();
|
||||
}
|
||||
|
||||
var owner = "";
|
||||
if (item.TryGetProperty("owner", out var ownerObj) &&
|
||||
ownerObj.TryGetProperty("display_name", out var displayName))
|
||||
{
|
||||
owner = displayName.GetString() ?? "";
|
||||
}
|
||||
|
||||
var isPublic = item.TryGetProperty("public", out var pub) && pub.GetBoolean();
|
||||
|
||||
// Check if this playlist is already linked
|
||||
var isLinked = !string.IsNullOrEmpty(id) && linkedSpotifyIds.Contains(id);
|
||||
|
||||
playlists.Add(new
|
||||
{
|
||||
id,
|
||||
name,
|
||||
trackCount,
|
||||
owner,
|
||||
isPublic,
|
||||
isLinked
|
||||
});
|
||||
}
|
||||
|
||||
if (items.GetArrayLength() < limit) break;
|
||||
offset += limit;
|
||||
|
||||
// Rate limiting
|
||||
if (_spotifyApiSettings.RateLimitDelayMs > 0)
|
||||
{
|
||||
await Task.Delay(_spotifyApiSettings.RateLimitDelayMs);
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(new { playlists });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error fetching Spotify user playlists");
|
||||
return StatusCode(500, new { error = "Failed to fetch Spotify playlists", details = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all playlists from Jellyfin
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user