feat: playlist implementation

This commit is contained in:
V1ck3s
2026-01-14 23:18:27 +01:00
committed by Vickes
parent 2c5daeefed
commit ebe6e90f39
21 changed files with 2561 additions and 54 deletions

View File

@@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Mvc;
using System.Xml.Linq;
using System.Text.Json;
using octo_fiesta.Models.Domain;
using octo_fiesta.Models.Subsonic;
namespace octo_fiesta.Services.Subsonic;
@@ -137,6 +138,81 @@ public class SubsonicResponseBuilder
);
return new ContentResult { Content = doc.ToString(), ContentType = "application/xml" };
}
/// <summary>
/// Creates a Subsonic response for a playlist represented as an album.
/// Playlists appear as albums with genre "Playlist".
/// </summary>
public IActionResult CreatePlaylistAsAlbumResponse(string format, ExternalPlaylist playlist, List<Song> tracks)
{
var totalDuration = tracks.Sum(s => s.Duration ?? 0);
// Build artist name with emoji and curator
var artistName = $"🎵 {char.ToUpper(playlist.Provider[0])}{playlist.Provider.Substring(1)}";
if (!string.IsNullOrEmpty(playlist.CuratorName))
{
artistName += $" {playlist.CuratorName}";
}
var artistId = $"curator-{playlist.Provider}-{playlist.CuratorName?.ToLowerInvariant().Replace(" ", "-") ?? "unknown"}";
if (format == "json")
{
return CreateJsonResponse(new
{
status = "ok",
version = SubsonicVersion,
album = new
{
id = playlist.Id,
name = playlist.Name,
artist = artistName,
artistId = artistId,
coverArt = playlist.Id,
songCount = tracks.Count,
duration = totalDuration,
year = playlist.CreatedDate?.Year ?? 0,
genre = "Playlist",
isCompilation = false,
created = playlist.CreatedDate?.ToString("yyyy-MM-ddTHH:mm:ss"),
song = tracks.Select(s => ConvertSongToJson(s)).ToList()
}
});
}
var ns = XNamespace.Get(SubsonicNamespace);
var albumElement = new XElement(ns + "album",
new XAttribute("id", playlist.Id),
new XAttribute("name", playlist.Name),
new XAttribute("artist", artistName),
new XAttribute("artistId", artistId),
new XAttribute("songCount", tracks.Count),
new XAttribute("duration", totalDuration),
new XAttribute("genre", "Playlist"),
new XAttribute("coverArt", playlist.Id)
);
if (playlist.CreatedDate.HasValue)
{
albumElement.Add(new XAttribute("year", playlist.CreatedDate.Value.Year));
albumElement.Add(new XAttribute("created", playlist.CreatedDate.Value.ToString("yyyy-MM-ddTHH:mm:ss")));
}
// Add songs
foreach (var song in tracks)
{
albumElement.Add(ConvertSongToXml(song, ns));
}
var doc = new XDocument(
new XElement(ns + "subsonic-response",
new XAttribute("status", "ok"),
new XAttribute("version", SubsonicVersion),
albumElement
)
);
return new ContentResult { Content = doc.ToString(), ContentType = "application/xml" };
}
/// <summary>
/// Creates a Subsonic response containing an artist with albums.