kv-music/.opencode/plans/tidal-web-backend.md

4.4 KiB

Replace Tidal OAuth2 Backend with Public Web API + Self-Hosted Proxy

Goal

Replace Monochrome's Tidal OAuth2 backend (which is blocked in your country) with the SpotiFLAC tidal-web approach that uses:

  1. Tidal public web endpoints (tidal.com/v1/) with a public token for metadata/search
  2. Self-hosted proxy (SpotiFLAC Go backend) for audio streaming/downloads

Changes

1. js/HiFi.ts - Complete rewrite

  • Remove OAuth2 token flow (auth.tidal.com/v1/oauth2/token)
  • Use public web token (x-tidal-token header, default: 49YxDN9a2aFV6RTG)
  • Change API base from api.tidal.com/v1/ to tidal.com/v1/
  • Keep all TypeScript type interfaces (TidalTrack, TidalAlbum, etc.)
  • Keep TidalResponse class
  • Update query() to use tidal.com/v1/ endpoints
  • Methods updated: getInfo, getTrack, getAlbum, getPlaylist, getPlaylistItems, getArtist, search, getVideo, getLyrics, getSimilarArtists, getSimilarAlbums
  • Constructor accepts: publicToken, countryCode, locale, deviceType
  • Remove: #fetchAppToken, #fetchAuthenticated, #fetchJson (OAuth2-based), fetchToken, getTrackManifest

2. js/api.js - Rewrite LosslessAPI

  • fetchWithRetry() - Remove HiFiClient OAuth2 fallback, remove proxy instance iteration for metadata (use HiFiClient directly). Keep proxy logic only for streaming if needed.
  • search(), searchTracks(), searchArtists(), searchAlbums(), searchPlaylists(), searchVideos() - Update to use new HiFiClient.search()
  • getAlbum() - Update to handle pages API response structure (pages/album returns page modules with ALBUM_HEADER and ALBUM_ITEMS)
  • getPlaylist() - Update to use new HiFiClient methods
  • getArtist() - Update to handle pages API response (pages/artist returns ARTIST_HEADER module)
  • getTrack() - Route through self-hosted proxy instead of Tidal OpenAPI
  • getStreamUrl() - Route through self-hosted proxy
  • getVideo() - Update for new endpoints
  • downloadTrack() - Stream URLs come from self-hosted proxy
  • enrichTrack() - Update to use proxy for playback info
  • normalizeTrackManifestResponse() - Adapt to proxy response format
  • Keep: getCoverUrl(), getCoverSrcset(), getArtistPictureUrl(), getArtistPictureSrcset(), getVideoCoverUrl(), cache methods, prepare methods

3. js/storage.js - Add tidalWebSettings

export const tidalWebSettings = {
    STORAGE_KEY: 'tidal-web-settings',
    DEFAULT_PROXY_URL: '',  // User must set their self-hosted proxy
    DEFAULT_PUBLIC_TOKEN: '49YxDN9a2aFV6RTG',
    DEFAULT_COUNTRY_CODE: 'US',
    getProxyUrl() { ... },
    setProxyUrl(url) { ... },
    getPublicToken() { ... },
    setPublicToken(token) { ... },
    getCountryCode() { ... },
    setCountryCode(code) { ... },
};

4. js/proxy-utils.js - Simplify

  • Remove tidal CDN URL proxying (no longer needed - streaming goes through self-hosted proxy)
  • Keep CORS proxy list for any remaining direct stream URLs

5. js/music-api.js - Minimal changes

  • getTrack(), getStreamUrl(), downloadTrack() already delegate to LosslessAPI
  • No structural changes needed

6. js/app.js - Update initialization

  • Change HiFiClient.initialize() to pass publicToken, countryCode from tidalWebSettings
  • Remove OAuth2 token/tokenExpiry storage references

Self-Hosted Proxy API Contract

The self-hosted SpotiFLAC Go backend must expose:

POST {proxyUrl}/v1/dl/tid2
Body: { "id": "123456", "quality": "HI_RES_LOSSLESS" }

Response:
{
  "data": {
    "manifest": "base64-encoded-manifest",
    "manifestMimeType": "application/dash+xml",
    "audioQuality": "HI_RES_LOSSLESS",
    "bitDepth": 24,
    "sampleRate": 96000,
    "trackPresentation": "FULL"
  }
}

Alternative response format (from mirror proxies):

[{ "OriginalTrackUrl": "https://direct-stream-url.flac" }]

Deployment Notes

The SpotiFLAC Go backend is at go_backend/ in the SpotiFLAC-Mobile repo:

  • Build: cd go_backend && go build
  • Run: ./go_backend (listens on port)
  • Configure with Tidal credentials or public token

Files Modified

  1. js/HiFi.ts - Full rewrite
  2. js/api.js - Major rewrite (LosslessAPI class)
  3. js/storage.js - Add tidalWebSettings
  4. js/proxy-utils.js - Simplify
  5. js/music-api.js - Minimal changes
  6. js/app.js - Update initialization

Files NOT Modified

  • Server-side functions (Cloudflare Workers for SEO) - continue using OAuth2
  • Frontend UI components
  • Player, downloads, metadata embedding logic (unchanged interfaces)