tidal-ui (lossless-ui)

repository·main·Indexed 23 days ago

https://github.com/binimum/tidal-ui

A high-fidelity music streaming user interface built with SvelteKit and Tailwind, capable of downloading FLAC files up to 24-bit/192kHz. The project includes the LosslessAPI client for searching tracks, artists, albums, and playlists, retrieving DASH manifests for high-resolution audio, and downloading tracks with optional FFmpeg WASM metadata embedding.

Tokens
9.3K
Snippets
18
Records
74
Agent score
78%

What's inside tidal-ui

  1. Understand lossless-ui caching and proxying behavior

    main

    When developing with or extending lossless-ui, be aware of the following architectural behaviors:

    API Proxying

    To avoid CORS errors, some requests are proxied through the first-party SvelteKit route at /api/proxy. This allows the browser to communicate with the API directly through the application server.

    Caching Rules

    The application implements a caching mechanism for safe GET requests. However, certain conditions prevent a response from being cached:

    • Headers: Requests containing Authorization, Cookie, or Range headers are not cached.
    • Payload Size: Responses larger than the REDIS_CACHE_MAX_BODY_BYTES limit are not cached.
    • Content Type: Non-text/JSON payloads are never cached.
    • Status Codes: Any 4xx or 5xx status codes are not cached.
    • Cache Control: Responses with Cache-Control: no-store or Cache-Control: private are never cached.
  2. Run lossless-ui with Docker Compose

    main

    To run the lossless-ui high-fidelity music streaming UI locally using Docker Compose, follow these steps:

    1. Prepare Environment: Create a .env file by copying the existing .env.example. Note that Redis is deprecated and should not be used.
    2. Build and Start: Run the production container using the following command:
      docker compose up --build
    3. Access the UI: Once the container has finished booting, visit http://localhost:5000.

    Configuration & Management:

    • Environment Variables: You can pass optional configuration (e.g., TITLE) using additional -e flags with the docker command.
    • Port: The SvelteKit server is configured to bind to PORT=5000 via Docker Compose.
    • Stopping: Use docker compose down to stop the stack.
    docker compose up --build
  3. Configure ESLint for Svelte and TypeScript projects

    main

    The project uses a flat configuration format for ESLint. It integrates recommended rules from JavaScript, TypeScript, and Svelte, while ensuring compatibility with Prettier.

    Key configuration details:

    • Ignore Files: Uses .gitignore to determine which files to ignore via @eslint/compat.
    • Globals: Configures both browser and node environments.
    • TypeScript/Svelte Integration: For .svelte, .svelte.ts, and .svelte.js files, it uses typescript-eslint's parser with projectService: true and passes the project's svelte.config.js to the parser options.
    • Rule Overrides: The no-undef rule is disabled ('off') to prevent conflicts with TypeScript's own type checking.
    import prettier from 'eslint-config-prettier';
    import { fileURLToPath } from 'node:url';
    import { includeIgnoreFile } from '@eslint/compat';
    import js from '@eslint/js';
    import svelte from 'eslint-plugin-svelte';
    import { defineConfig } from 'eslint/config';
    import globals from 'globals';
    import ts from 'typescript-eslint';
    import svelteConfig from './svelte.config.js';
    
    const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url));
    
    export default defineConfig(
    	includeIgnoreFile(gitignorePath),
    	js.configs.recommended,
    	...ts.configs.recommended,
    	...svelte.configs.recommended,
    	prettier,
    	...svelte.configs.prettier,
    	{
    		languageOptions: {
    			globals: { ...globals.browser, ...globals.node }
    		},
    		rules: {
    			'no-undef': 'off'
    		}
    	},
    	{
    		files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'],
    		languageOptions: {
    			parserOptions: {
    				projectService: true,
    				extraFileExtensions: ['.svelte'],
    				parser: ts.parser,
    				svelteConfig
    			}
    		}
    	}
    );
  4. Configure environment variables for svelte-prod service

    main

    The svelte-prod service uses several environment variables for configuration, primarily for setting the application title and configuring Redis for caching. These variables should be provided in your environment or a .env file used by Docker Compose.

    Application Settings

    • TITLE: The title of the application.
    • NODE_ENV: Set to production by default.
    • PORT: The port the application listens on (default 5000).

    Redis Configuration

    Use these variables to connect to a Redis instance and tune caching behavior:

    • REDIS_URL: The full connection URL for Redis.
    • REDIS_HOST: The hostname of the Redis server.
    • REDIS_PORT: The port of the Redis server.
    • REDIS_USERNAME: The username for Redis authentication.
    • REDIS_PASSWORD: The password for Redis authentication.
    • REDIS_TLS: Whether to use TLS for the Redis connection.

    Cache TTL and Limits

    • REDIS_CACHE_TTL_SECONDS: General cache Time-To-Live in seconds.
    • REDIS_CACHE_TTL_SEARCH_SECONDS: TTL specifically for search results.
    • REDIS_CACHE_TTL_TRACK_SECONDS: TTL specifically for tracking data.
    • REDIS_CACHE_MAX_BODY_BYTES: Maximum size in bytes for cached bodies.
    environment:
      - TITLE=${TITLE}
      - REDIS_URL=${REDIS_URL}
      - REDIS_HOST=${REDIS_HOST}
      - REDIS_PORT=${REDIS_PORT}
      - REDIS_USERNAME=${REDIS_USERNAME}
      - REDIS_PASSWORD=${REDIS_PASSWORD}
      - REDIS_TLS=${REDIS_TLS}
      - REDIS_CACHE_TTL_SECONDS=${REDIS_CACHE_TTL_SECONDS}
      - REDIS_CACHE_TTL_SEARCH_SECONDS=${REDIS_CACHE_TTL_SEARCH_SECONDS}
      - REDIS_CACHE_TTL_TRACK_SECONDS=${REDIS_CACHE_TTL_TRACK_SECONDS}
      - REDIS_CACHE_MAX_BODY_BYTES=${REDIS_CACHE_MAX_BODY_BYTES}
  5. Download track options configuration

    main

    When using downloadTrack(), you can pass a DownloadTrackOptions object to customize the download process.

    Supported options:

    • signal: An AbortSignal to cancel the download.
    • convertAacToMp3: Boolean. If true, converts compatible formats to MP3.
    • downloadCoverSeperately: Boolean. If true, fetches the album cover as a separate file instead of embedding it.
    • onProgress: Callback for overall download/embedding progress.
    • onFfmpegStart / onFfmpegComplete / onFfmpegError: Lifecycle hooks for the FFmpeg metadata embedding process.
    • onFfmpegProgress: Callback specifically for FFmpeg processing progress.
    • onFfmpegCountdown: Callback for estimating FFmpeg WASM download size.
    • ffmpegAutoTriggered: Boolean. Whether to automatically trigger FFmpeg loading.
  6. Configure API cluster settings via API_CONFIG

    main

    The API_CONFIG object controls the global behavior of the API client.

    Available Keys:

    • targets: An array of ApiClusterTarget currently being used for load distribution.
    • baseUrl: The default base URL used if no specific target is resolved.
    • useProxy: A boolean indicating whether the application should attempt to use the CORS proxy.
    • proxyUrl: The endpoint path used for proxying requests (e.g., '/api/proxy').
  7. Use Player Stores for audio state

    main

    The library provides several Svelte stores to track the state of the audio player. You can subscribe to these stores to build reactive UI elements that respond to playback changes.

    Available player stores:

    • playerStore: The main player state store.
    • currentTrack: The track currently being played.
    • isPlaying: Boolean indicating if audio is currently playing.
    • currentTime: The current playback position in seconds.
    • duration: The total duration of the current track in seconds.
    • volume: The current volume level.
    • progress: The playback progress (typically a value between 0 and 1).
  8. Get artist details and discography with `getArtist()`

    main

    Fetch a comprehensive overview of an artist using their ID. This method performs a deep scan of the artist's data to build a normalized view of their discography.

    It automatically resolves and links:

    • Artist: The primary artist details.
    • Albums: A sorted list of albums (by release date or popularity).
    • Tracks: A list of top tracks (up to 100) sorted by popularity.

    The method handles complex nested data structures and ensures that tracks are correctly linked to their respective album and artist objects.

    async getArtist(id: number): Promise<ArtistDetails>
  9. Download a track with `downloadTrack()`

    main

    Fetches the audio stream for a track and triggers a browser download for the specified filename.

    Parameters:

    • trackId: The numeric ID of the track.
    • quality: The desired AudioQuality (defaults to 'LOSSLESS').
    • filename: The name to be used for the downloaded file.
    • options (optional): DownloadTrackOptions to configure behavior like metadata embedding or separate cover downloads.

    Key features:

    • Metadata Embedding: If configured, uses FFmpeg WASM to embed title, artist, album, track number, disc number, release year, ISRC, and ReplayGain into the file.
    • Format Conversion: Can convert AAC/M4A files to MP3 if convertAacToMp3 is set in options.
    • Progress Tracking: Provides hooks for download and embedding progress via options.onProgress and options.onFfmpegProgress.
    async downloadTrack(
    	trackId: number,
    	quality: AudioQuality = 'LOSSLESS',
    	filename: string,
    	options?: DownloadTrackOptions
    ): Promise<void>
  10. Retrieve DASH manifests for high-resolution audio

    main

    To access high-resolution streams, use getDashManifest or getDashManifestWithMetadata. These methods return the manifest required for DASH playback.

    getDashManifest returns a DashManifestResult:

    • { kind: 'dash', manifest: string, contentType: string | null } for segmented DASH.
    • { kind: 'flac', manifestText: string, urls: string[], contentType: string | null } for direct FLAC URLs.

    getDashManifestWithMetadata provides additional audio properties like sampleRate, bitDepth, and replayGain.