Blazor WebAssembly app that reads a Traktor collection.nml from Google Drive
and streams the referenced audio files in the browser. Sign in with the Google
account that owns the collection; access is read-only.
| URL | https://traktor-google-drive-194132977379.europe-north1.run.app |
| Alias (legacy URL form) | https://traktor-google-drive-mulgdizrhq-lz.a.run.app |
| Platform | Google Cloud Run (managed) |
| GCP project | traktor-toni-2025 (number 194132977379) |
| Region | europe-north1 |
| Service | traktor-google-drive |
| Container port | 8080 |
| Access | --allow-unauthenticated (public; the app gates itself with Google OAuth) |
| Live revision | traktor-google-drive-00013-kp7, image gcr.io/traktor-toni-2025/traktor-google-drive:1197cf1 |
No custom domain is mapped — the *.run.app URL above is the only address.
The service scales to zero, so the first request after an idle period is a cold start. If it returns
429 Rate exceeded/no available instancefor more than a few minutes, force a new revision — that re-registers it with the serving layer:gcloud run services update traktor-google-drive --region europe-north1 \ --update-env-vars=REDEPLOY_TS=$(date +%Y%m%d%H%M%S)This was needed on 2026-08-16 after the GCP billing account was replaced: the revision reported
Ready: Trueand all quotas were at default, but Cloud Run would not schedule an instance until a new revision was created.
Re-read it at any time with:
gcloud run services list --platform managed --project traktor-toni-2025The container is an ASP.NET host whose only dynamic endpoint is the Drive streaming proxy — everything else is static Blazor WASM:
GET /api/proxy/drive/{fileId}?token={googleAccessToken}
It re-issues GET https://www.googleapis.com/drive/v3/files/{fileId}?alt=media
with Authorization: Bearer {token} and forwards Range / Content-Range.
It exists only because <audio src> cannot attach an Authorization header.
The Cast button in the top row sends playback to a Chromecast-built-in device (Chrome or Edge only — the sender SDK is Chromium-only). Once connected, the per-track play buttons drive the TV instead of this browser, and the casting bar shows what is playing with transport and seek.
The Cast device fetches the audio itself, so it gets the absolute proxy URL and
the proxy must accept it — see
docs/adr-001-tv-playback.md for why
www.gstatic.com is allowed, why the access token is re-issued mid-set, and what
has not been verified against a real television.
Build image and push
dotnet restore
DOCKER_BUILDKIT=1 docker build --secret id=github_token,src=./.github_token -t traktor-google-drive .
docker tag traktor-google-drive tonijuvani/traktor-google-drive:$(date +%Y%m%d)
docker tag traktor-google-drive tonijuvani/traktor-google-drive:latest
docker push tonijuvani/traktor-google-driveIf your nuget.config uses environment variable placeholders for credentials, run this in your project root:
export GITHUB_USERNAME=toini
export GITHUB_TOKEN=$(cat .github_token)
dotnet restore- Your token must be in a file called
.github_tokenin the project root. - You may see warnings like:
Error occurred while getting package vulnerability data: Unable to load the service index for source ...
- These warnings are usually harmless and relate to NuGet's attempt to fetch vulnerability data from the package sources. They do not affect restoring or building your project.
services:
traktor-google-drive:
image: tonijuvani/traktor-google-drive:latest
pull_policy: always
container_name: traktor-google-drive
ports:
- "5000:443"
volumes:
- /home/toni/homeassistant/ssl/fullchain.pem:/etc/letsencrypt/fullchain.pem:ro
- /home/toni/homeassistant/ssl/privkey.pem:/etc/letsencrypt/privkey.pem:roSee README-cloudrun.md for build, push and deploy steps, including the billing/cold-start failure modes this service has actually hit.
End-to-end tests drive the real app in Chromium with the Google Drive API mocked, so they need no Google account and no network:
cd tests/e2e
npm install
npx playwright install chromium
npx playwright test # the suite
npx playwright test screenshots # writes tests/e2e/screenshots/The harness seeds sessionStorage.access_token directly and intercepts every
googleapis.com call plus the audio proxy — see tests/e2e/drive-mock.ts. The
fixture in tests/e2e/fixtures/collection.nml is shaped to the parser's exact
path-normalisation rules; the comment at the top of that file explains why.
node is not on PATH by default on this machine (nvm is loaded with
--no-use), so prefix commands with:
export PATH="$HOME/.nvm/versions/node/$(cat ~/.nvm/alias/default)/bin:$PATH"