From 70968b386c0913495fa90a01a687b91595b05069 Mon Sep 17 00:00:00 2001 From: XShine555 Date: Tue, 18 Aug 2026 14:38:00 +0200 Subject: [PATCH] Changes --- deploy/compose.share.yml | 15 +++ deploy/zitadel/.output-share/admin-sa.pat | 1 + deploy/zitadel/provision.ps1 | 77 +++++++------ web-player/package-lock.json | 103 +++++++++++++++--- web-player/src/lib/albums.ts | 30 +++++ .../lib/components/AlbumContextMenu.svelte | 22 +++- .../src/lib/components/ui/MixBentoTile.svelte | 2 +- web-player/src/lib/player/player.svelte.ts | 18 ++- .../albums/youtube/[albumId]/+page.svelte | 8 ++ .../src/routes/(app)/explore/+page.svelte | 25 +++-- web-player/src/routes/+page.server.ts | 9 +- web-player/src/routes/+page.svelte | 51 ++++++++- .../routes/api/albums/[id]/tracks/+server.ts | 16 +++ .../youtube/[albumId]/tracks/+server.ts | 12 ++ 14 files changed, 325 insertions(+), 64 deletions(-) create mode 100644 deploy/compose.share.yml create mode 100644 deploy/zitadel/.output-share/admin-sa.pat create mode 100644 web-player/src/lib/albums.ts create mode 100644 web-player/src/routes/api/albums/[id]/tracks/+server.ts create mode 100644 web-player/src/routes/api/albums/youtube/[albumId]/tracks/+server.ts diff --git a/deploy/compose.share.yml b/deploy/compose.share.yml new file mode 100644 index 0000000..04e5f0b --- /dev/null +++ b/deploy/compose.share.yml @@ -0,0 +1,15 @@ +name: musify-share + +# Temporary overlay for exposing the stack outside the LAN, on top of +# compose.dev.yml. Only difference from a normal dev run: Zitadel writes its +# init PAT to a separate host directory, so it never overwrites +# zitadel/.output (used by the musify-dev project's own Zitadel instance). +# +# docker compose --env-file deploy/.env.share \ +# -f deploy/compose.yml -f deploy/compose.dev.yml -f deploy/compose.share.yml \ +# --profile apps up -d --build api gateway web-player + +services: + zitadel: + volumes: + - ./zitadel/.output-share:/output diff --git a/deploy/zitadel/.output-share/admin-sa.pat b/deploy/zitadel/.output-share/admin-sa.pat new file mode 100644 index 0000000..bfdaf76 --- /dev/null +++ b/deploy/zitadel/.output-share/admin-sa.pat @@ -0,0 +1 @@ +eyJhbGciOiJBMjU2R0NNS1ciLCJlbmMiOiJBMjU2R0NNIiwiaXYiOiJ5Yk1hUFdVUmNqcU44WDNGIiwia2lkIjoib2lkY0tleSIsInRhZyI6Ikw2NjYwN3FRVzZNYVJLaVl3Q0w1N3cifQ.BE_7qqA1Auer5yEoGnyN0v35JlZlK3XgCo0s_CQtG7s.cOf9oudMHRnVTOGj.0DRh9kj7oahl4imvMjxqRLUOZo6NhYlQyslf9_x4XF_TKi1dnQ.Uu36qEL5J1VKSIYcvWlbAg diff --git a/deploy/zitadel/provision.ps1 b/deploy/zitadel/provision.ps1 index 434f4d8..b9855b6 100644 --- a/deploy/zitadel/provision.ps1 +++ b/deploy/zitadel/provision.ps1 @@ -15,7 +15,14 @@ param( [string]$ProjectName = "Musify", [string]$ApiAppName = "Musify API (Scalar)", - [string]$WebAppName = "Musify Web" + [string]$WebAppName = "Musify Web", + # Alternate .env to read/write (e.g. deploy/.env.share). Defaults to deploy/.env. + [string]$EnvFile, + # Skip writing Musify.Api user-secrets and web-player/.env: use when the apps + # only ever run in Docker for this environment and get their config from compose. + [switch]$SkipHostWiring, + # Matches the zitadel volume mount for this environment (see compose.share.yml). + [string]$OutputDir = ".output" ) $ErrorActionPreference = "Stop" @@ -23,8 +30,8 @@ $deployDir = Split-Path $PSScriptRoot -Parent $repoDir = Split-Path $deployDir -Parent $apiProject = Join-Path $repoDir "backend\Musify.Api" $webEnvFile = Join-Path $repoDir "web-player\.env" -$envFile = Join-Path $deployDir ".env" -$patFile = Join-Path $PSScriptRoot ".output\admin-sa.pat" +$envFile = if ($EnvFile) { $EnvFile } else { Join-Path $deployDir ".env" } +$patFile = Join-Path $PSScriptRoot "$OutputDir\admin-sa.pat" . (Join-Path $deployDir "scripts\common.ps1") @@ -148,35 +155,39 @@ Set-DotEnvValue $envFile "AUTH_CLIENT_ID" $apiClientId Set-DotEnvValue $envFile "WEB_CLIENT_ID" $webClientId Set-DotEnvValue $envFile "WEB_CLIENT_SECRET" $webSecret -# Musify.Api reads these from user-secrets when it runs outside Docker. -@{ - "Authentication:ClientId" = $apiClientId - "Authentication:AudienceAddress" = $apiClientId - "Authentication:IssuerAddress" = $issuer - "Authentication:MetadataAddress" = "$issuer/.well-known/openid-configuration" - "Authentication:AuthorizationEndpoint" = "$issuer/oauth/v2/authorize?prompt=login" - "Authentication:TokenEndpoint" = "$issuer/oauth/v2/token" - "Authentication:ScalarRedirectUri" = "$apiUrl/scalar/" - "Authentication:RequireHttpsMetadata" = "false" -}.GetEnumerator() | ForEach-Object { - dotnet user-secrets set $_.Key $_.Value --project $apiProject | Out-Null -} +if ($SkipHostWiring) { + Write-Host " wired into $envFile only (-SkipHostWiring: apps run in Docker)" +} else { + # Musify.Api reads these from user-secrets when it runs outside Docker. + @{ + "Authentication:ClientId" = $apiClientId + "Authentication:AudienceAddress" = $apiClientId + "Authentication:IssuerAddress" = $issuer + "Authentication:MetadataAddress" = "$issuer/.well-known/openid-configuration" + "Authentication:AuthorizationEndpoint" = "$issuer/oauth/v2/authorize?prompt=login" + "Authentication:TokenEndpoint" = "$issuer/oauth/v2/token" + "Authentication:ScalarRedirectUri" = "$apiUrl/scalar/" + "Authentication:RequireHttpsMetadata" = "false" + }.GetEnumerator() | ForEach-Object { + dotnet user-secrets set $_.Key $_.Value --project $apiProject | Out-Null + } -# web-player/.env is what `npm run dev` reads (the container gets its config -# from compose instead). -if (-not (Test-Path $webEnvFile)) { - Copy-Item (Join-Path $repoDir "web-player\.env.example") $webEnvFile -} -@{ - ZITADEL_ISSUER = $issuer - ZITADEL_CLIENT_ID = $webClientId - ZITADEL_CLIENT_SECRET = $webSecret - AUTH_REDIRECT_URI = "$viteUrl/auth/callback" - AUTH_POST_LOGOUT_URI = "$viteUrl/" - SESSION_SECRET = $cfg['SESSION_SECRET'] - API_BASE_URL = $apiUrl -}.GetEnumerator() | ForEach-Object { - Set-DotEnvValue $webEnvFile $_.Key $_.Value -} + # web-player/.env is what `npm run dev` reads (the container gets its config + # from compose instead). + if (-not (Test-Path $webEnvFile)) { + Copy-Item (Join-Path $repoDir "web-player\.env.example") $webEnvFile + } + @{ + ZITADEL_ISSUER = $issuer + ZITADEL_CLIENT_ID = $webClientId + ZITADEL_CLIENT_SECRET = $webSecret + AUTH_REDIRECT_URI = "$viteUrl/auth/callback" + AUTH_POST_LOGOUT_URI = "$viteUrl/" + SESSION_SECRET = $cfg['SESSION_SECRET'] + API_BASE_URL = $apiUrl + }.GetEnumerator() | ForEach-Object { + Set-DotEnvValue $webEnvFile $_.Key $_.Value + } -Write-Host " wired into deploy/.env, Musify.Api user-secrets and web-player/.env" + Write-Host " wired into deploy/.env, Musify.Api user-secrets and web-player/.env" +} diff --git a/web-player/package-lock.json b/web-player/package-lock.json index 46cc1df..67c7d44 100644 --- a/web-player/package-lock.json +++ b/web-player/package-lock.json @@ -37,6 +37,29 @@ "vite": "^8.0.16" } }, + "node_modules/@emnapi/core": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz", + "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", + "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", @@ -1098,7 +1121,6 @@ "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.69.1.tgz", "integrity": "sha512-+nz8Fx/cElzb2ZPHC+6Ll3y3NEVIe4Na75PeplLlyTmd1dBXAjz2KR14y1ZgNjb2ThfAYzulu+PFy1UE3RCUzA==", "license": "MIT", - "peer": true, "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.9", @@ -1150,7 +1172,6 @@ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-7.1.2.tgz", "integrity": "sha512-DrUBA2UXRfDmUX/ZTiEopd3X40yavsJF1FX2RygcuIScHL7o5YX1fMvoYnDhjeJQC4weCOklirpNWlcb2NiSeA==", "license": "MIT", - "peer": true, "dependencies": { "deepmerge": "^4.3.1", "magic-string": "^0.30.21", @@ -1388,6 +1409,72 @@ "node": ">=14.0.0" } }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.1", + "dev": true, + "inBundle": true, + "license": "0BSD", + "optional": true + }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.2.tgz", @@ -1479,7 +1566,6 @@ "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -1541,7 +1627,6 @@ "integrity": "sha512-KA0OshtlcCCXmbfqyZkM5pV3/WNraJf7DkJRLpyrmwPtud57H5BDX7C3k0LPSPxpprfRL+cJDGabF10mvNCoCw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.64.0", "@typescript-eslint/types": "8.64.0", @@ -1649,7 +1734,6 @@ "integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==", "devOptional": true, "license": "MIT", - "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -1733,7 +1817,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1968,7 +2051,6 @@ "integrity": "sha512-GVTD7s1vdIl6UYvAfriOPeY1Df8LIZjfofLvHwde+erDHGGuHyuM6xoxRxmHiebhYuD2p1vN4wWh0XzPARSGDQ==", "dev": true, "license": "MIT", - "peer": true, "workspaces": [ "packages/*" ], @@ -3047,7 +3129,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", @@ -3181,7 +3262,6 @@ "integrity": "sha512-/FVl766LpUfB5vXgCYOYa0MeV/441Ia99AeICQIQFTY/Nw0roZwULcXpku5i1/m5kt/baz+s4Zogspd839HSMg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -3198,7 +3278,6 @@ "integrity": "sha512-wXvbXMjSvb4C9ENWTHXyd+ihakKCsJ6rJhLP6/8HFNj4GkZr48jqL9PoKsl2sk7SyCZRTnJ7O2TTowUpOxP/KA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=20" }, @@ -3369,7 +3448,6 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==", "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.9" }, @@ -3504,7 +3582,6 @@ "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.56.4.tgz", "integrity": "sha512-/d0QHehmRuJW8gVz395MTkPcPozxzdjBMBE8oEYGz8O3b9KTMzzQ9ZHJQLuFKOHOPQbU6kx/X4iid/EBBzH7iw==", "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", @@ -3716,7 +3793,6 @@ "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3778,7 +3854,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.3.tgz", "integrity": "sha512-Ds+gBRbj0lwRO2Y5hwnUBdxSwlAve9LeRyU4sNnAr0ewW0gWF0n5bgXgUzbgZ49MV9BVUAQUFYVcDUcilUExMA==", "license": "MIT", - "peer": true, "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", diff --git a/web-player/src/lib/albums.ts b/web-player/src/lib/albums.ts new file mode 100644 index 0000000..51c60a1 --- /dev/null +++ b/web-player/src/lib/albums.ts @@ -0,0 +1,30 @@ +import { toQueueItems, type ApiTrackLike, type QueueItem } from '$lib/player/player.svelte'; + +interface YouTubeAlbumTracksResponse { + album: { artist: string; thumbnailUrl: string }; + tracks: { videoId: string; title: string; isExplicit: boolean }[]; +} + +export async function fetchAlbumQueueItems( + kind: 'local' | 'youtube', + albumId: string +): Promise { + if (kind === 'local') { + const res = await fetch(`/api/albums/${albumId}/tracks`); + if (!res.ok) return []; + const data = (await res.json()) as { items: ApiTrackLike[] }; + return toQueueItems(data.items ?? []); + } + + const res = await fetch(`/api/albums/youtube/${albumId}/tracks`); + if (!res.ok) return []; + const detail = (await res.json()) as YouTubeAlbumTracksResponse; + return detail.tracks.map((track) => ({ + id: track.videoId, + title: track.title, + artist: detail.album.artist, + source: 'youtube' as const, + coverUrl: detail.album.thumbnailUrl, + explicit: track.isExplicit + })); +} diff --git a/web-player/src/lib/components/AlbumContextMenu.svelte b/web-player/src/lib/components/AlbumContextMenu.svelte index 5d84412..79c0115 100644 --- a/web-player/src/lib/components/AlbumContextMenu.svelte +++ b/web-player/src/lib/components/AlbumContextMenu.svelte @@ -25,15 +25,19 @@