Add anonymous listening support and redesign web-player UI

Backend:
- Expose GET /config/playback so the frontend can detect whether
  anonymous listening is enabled without requiring auth
- Wire anonymous access through track streaming and YouTube resolve flows

Frontend:
- Allow browsing/listening without a session when the backend enables it
- Redesign Inicio, Sidebar and top bar with a new dark theme, typography,
  and layout (top bar with live quick-search, sticky/blurred on scroll)
- Populate Top música, Popular and Recomendados with real Musify +
  YouTube Music data instead of placeholders
- Various shape, spacing, color and animation refinements across the
  redesigned components
This commit is contained in:
2026-09-11 01:29:37 +02:00
parent 10766bbb47
commit 389b3b9244
40 changed files with 1172 additions and 452 deletions
@@ -0,0 +1,25 @@
using System.Net;
using System.Net.Http.Json;
using Musify.Api.Tests.TestSupport;
using Musify.Application.Configuration.Responses;
using Xunit;
namespace Musify.Api.Tests.Endpoints
{
[Collection(ApiCollection.Name)]
public sealed class ConfigEndpointsTests(ApiTestFixture fixture)
{
[Fact]
public async Task GetPlaybackConfig_WithoutAuthentication_ReturnsConfiguredValues()
{
var client = fixture.CreateAnonymousClient();
var response = await client.GetAsync("/config/playback", TestContext.Current.CancellationToken);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadFromJsonAsync<PlaybackPublicConfigResponse>(TestContext.Current.CancellationToken);
Assert.NotNull(body);
Assert.False(body.AllowAnonymousListening);
}
}
}
@@ -0,0 +1,31 @@
using Musify.Application.Configuration;
using Musify.Application.Tests.TestSupport;
using Xunit;
namespace Musify.Application.Tests.Configuration
{
public sealed class GetPlaybackPublicConfigTests
{
[Fact]
public async Task Handle_ReflectsPlaybackConfiguration()
{
var playback = TestConfigurations.Playback(allowAnonymousListening: true, anonymousFragmentSeconds: 30);
var handler = new GetPlaybackPublicConfigQueryHandler(playback);
var response = await handler.Handle(new GetPlaybackPublicConfigQuery(), TestContext.Current.CancellationToken);
Assert.True(response.AllowAnonymousListening);
Assert.Equal(30, response.AnonymousFragmentSeconds);
}
[Fact]
public async Task Handle_DefaultsToDisabled()
{
var handler = new GetPlaybackPublicConfigQueryHandler(TestConfigurations.Playback());
var response = await handler.Handle(new GetPlaybackPublicConfigQuery(), TestContext.Current.CancellationToken);
Assert.False(response.AllowAnonymousListening);
}
}
}