hifi-api

repository·main·Indexed 43 days ago

https://github.com/binimum/hifi-api

A proxy and wrapper for the Tidal API designed for high-fidelity music streaming and metadata access, commonly used in homelab environments. It provides endpoints for track manifests, Widevine DRM license proxying, catalog searches, and playback request queuing to manage concurrent account limits.

Tokens
8K
Snippets
30
Records
39
Agent score
56%

What's inside hifi-api

  1. Decode and use the video manifest

    main

    The manifest returned by GET /video/ is a base64-encoded JSON string. To use it, you must decode it to obtain the HLS stream URLs.

    Decoded Manifest Structure:

    {
      "mimeType": "application/vnd.apple.mpegurl",
      "urls": [
        "https://.../manifest.m3u8?token=..."
      ]
    }

    These URLs are standard HLS (.m3u8) manifests that can be played in compatible media players.

    {
      "mimeType":"application/vnd.apple.mpegurl",
      "urls":[
        "https://im-fa.manifest.tidal.com/1/manifests/CAESCDQ4MjA0MTA2IhZXRWp2UUJ0Z0xhX1hjc0c1NGdra3ZBIhY2MDVpTzFpODBOVnlFenZUdFN5NkpBIhY4dXlxeUZjbDNkOUlrMnpSZ3d2RFhnIhZETU5RdEN2clNqaEdBWnNwNUVnaHpnIhY0NmJ1Z3hkWEpOY0I4ZmRjYmRkYjRRKAEwAlAB.m3u8?token=1772574630~MjUyODQzZjFjNjBlYjRhNjJlYWU0MTEzNzMwMDQ2ZmMzNzExZmY0OA=="
      ]
    }
  2. How playback request queuing works

    main

    When all available playback accounts are currently occupied, the API does not hold the HTTP request indefinitely. Instead, it returns a 202 Accepted status immediately with a payload indicating the request is pending.

    Handling Queued Requests

    When you receive a 202 Accepted response, it includes a requestId and a queuePosition. The response also provides headers: Location, Retry-After, X-Playback-Queue-Position, and X-Playback-Request-Id.

    To manage the queued request, the client can:

    • Poll for status: Use GET /playback/requests/{requestId}. A completed poll will return the original playback response.
    • Cancel the request: Use DELETE /playback/requests/{requestId} to remove the request from the queue.

    Note: Completed, failed, and cancelled request records expire after five minutes.

    Example Pending Response

    {
      "status": "pending",
      "requestId": "f1b5...",
      "queuePosition": 2,
      "statusUrl": "/playback/requests/f1b5...",
      "cancelUrl": "/playback/requests/f1b5...",
      "playbackAccounts": 2,
      "activePlaybackRequests": 2
    }
  3. Handle Dolby Atmos playback constraints

    main

    The API returns Dolby Atmos content by default if available. Because most web browsers cannot play Atmos, you should explicitly specify all desired formats except Atmos formats in your request to avoid playback issues.

    Example of an Atmos response (contains EAC3_JOC):

    {
      "version": "2.7",
      "data": {
        "data": {
          "id": "434493254",
          "type": "trackManifests",
          "attributes": {
            "trackPresentation": "FULL",
            "uri": "https://im-fa.manifest.tidal.com/...",
            "hash": "P9JIfU1+7xwdasgo84AJa...",
            "formats": [
              "EAC3_JOC"
            ]
          }
        }
      }
    }
  4. Identify Dolby Atmos tracks

    main
    Tracks that support Dolby Atmos typically include DOLBY_ATMOS within the mediaMetadata.tags field. To specifically request Dolby Atmos tracks, use the trackManifests endpoint (refer to its specific schema for details).
  5. Setup hifi-api

    main

    To set up the hifi-api, you must first authenticate with Tidal to generate credentials, then install the project dependencies.

    1. Authenticate with Tidal:
      • Navigate to the tidal_auth/ directory.
      • Install requirements: pip install -r requirements.txt.
      • Run the authentication script: python3 tidal_auth.py and follow the on-screen instructions. This creates or updates a token.json file.
    2. Install API Dependencies:
      • Navigate to the project root.
      • Install the main dependencies: pip install -r requirements.txt.
    3. Run the Server:
      • Execute python3 main.py. By default, the server listens on 0.0.0.0:8000.
    # In tidal_auth/ directory
    pip install -r requirements.txt
    python3 tidal_auth.py
    
    # In project root
    pip install -r requirements.txt
    python3 main.py
  6. Configure proxy settings

    main

    You can manage proxy usage via environment variables or an .env file to mitigate account ban risks or handle network restrictions.

    • USE_PROXIES (default: False): Enables proxy support.
    • PROXIES_FILE (default: proxies.txt): Path to a text file containing proxies, one per line in the format http(s)://user:pass@hostname:port.
    • ROTATE_PROXIES_ON_REFRESH (default: False): If True, rotates to a new proxy when refreshing the Tidal token.
    • MAX_RETRIES (default: 2): Number of retry attempts with a new proxy if the current one fails.
    • FALLBACK_TO_DIRECT_CONNECTION (default: False): WARNING: If True, the application will use the host's direct IP if all proxies fail or if proxies.txt is misconfigured.
  7. Configure playback and catalog accounts

    main

    The API manages Tidal accounts for playback and metadata (catalog) traffic.

    Playback Accounts

    Every playback credential in token.json is limited to one in-flight playback request. If you have two playback accounts, you can run at most two concurrent playback requests.

    Catalog Accounts

    To reserve an account specifically for metadata traffic (search, album, artist, playlist, etc.) and exclude it from playback rotation, you can:

    1. Add "role": "catalog" to the account's entry in token.json.
    2. Or, configure CATALOG_CLIENT_ID, CATALOG_CLIENT_SECRET, and CATALOG_REFRESH_TOKEN in your .env file.

    Note: A catalog account does not require an active subscription. CATALOG_TOKEN can also accept a static bearer token, but it will not refresh automatically.

    Example token.json structure

    [
      {
        "client_ID": "...",
        "client_secret": "...",
        "refresh_token": "paid-account-1-refresh-token",
        "userID": "..."
      },
      {
        "client_ID": "...",
        "client_secret": "...",
        "refresh_token": "paid-account-2-refresh-token",
        "userID": "..."
      },
      {
        "role": "catalog",
        "client_ID": "...",
        "client_secret": "...",
        "refresh_token": "catalog-account-refresh-token",
        "userID": "..."
      }
    ]
  8. Deploy hifi-api using Docker Compose

    main

    You can deploy the hifi-api service using Docker Compose. The service is configured to run on port 8000 and requires a .env file for environment variables and a token.json file for authentication.

    Key Configuration Details:

    • Port Mapping: The service binds to 0.0.0.0:8000 on the host.
    • Authentication: The file ./token.json must exist in the same directory as the docker-compose.yml and will be mounted as a read-only (ro) volume at /app/token.json inside the container.
    • Environment Variables: Configuration is loaded from a .env file.
    • Restart Policy: The service is set to unless-stopped.
    services:
      web:
        build: .
        env_file:
          - .env
        ports:
          - "0.0.0.0:8000:8000"
        volumes:
          - ./token.json:/app/token.json:ro
        restart: unless-stopped
    
    networks:
      default:
        driver: bridge
  9. Get similar albums with GET /album/similar/

    main

    Retrieve a list of albums similar to a specific album. This endpoint supports pagination via a cursor.

    Parameters:

    • id (int, required): The ID of the album.
    • cursor (int | str, optional): A cursor for paginated results.
    {
      "version": "2.6",
      "albums": [
        {
          "title": "The College Dropout",
          "barcodeId": "00602567922186",
          "numberOfVolumes": 1,
          "numberOfItems": 21,
          "duration": "PT1H16M24S",
          "explicit": true,
          "releaseDate": "2004-02-10",
          "copyright": {
            "text": "© 2004 UMG Recordings, Inc."
          },
          "popularity": 0.849979392325809,
          "accessType": "PUBLIC",
          "availability": [
            "STREAM",
            "DJ"
          ],
          "mediaTags": [
            "LOSSLESS"
          ],
          "externalLinks": [
            {
              "href": "https://tidal.com/browse/album/92099357",
              "meta": {
                "type": "TIDAL_SHARING"
              }
            }
          ],
          "type": "ALBUM",
          "albumType": "ALBUM",
          "createdAt": "2018-07-16T21:57:06Z",
          "id": 92099357,
          "cover": "cc77193c-a074-4538-a8b5-307b2a44169e",
          "artists": [
            {
              "id": 25022,
              "name": "Kanye West"
            }
          ],
          "url": "http://www.tidal.com/album/92099357"
        }
      ]
    }
  10. Get a mix via GET /mix/

    main

    Retrieve a mix (similar to recommendations, typically containing ~60 tracks) using its unique ID.

    Parameters:

    • id (string, required): The unique ID of the mix.

    Response: Returns a 200 OK status with a JSON object containing:

    • mix: Metadata about the mix (ID, title, subtitle, graphic info, image URLs, etc.).
    • items: An array of track objects included in the mix.
    // Example Response Structure
    {
      "version": "2.6",
      "mix": {
        "id": "0019a6f811ccf59fd0346967a1dea7",
        "title": "Subterranean Homesick Alien",
        "subTitle": "Radiohead",
        "mixType": "TRACK_MIX",
        "images": { ... }
      },
      "items": [
        {
          "id": 58990513,
          "title": "Subterranean Homesick Alien",
          "duration": 267,
          "artists": [{ "id": 64518, "name": "Radiohead" }],
          "audioQuality": "LOSSLESS"
        }
      ]
    }
  11. Search for covers with GET /cover/

    main

    Retrieve cover images for a specific track or via a search query.

    Parameters:

    • id (int, optional): The track ID.
    • q (str, optional): A track search query.
    {
      "version": "2.6",
      "covers": [
        {
          "id": 311222965,
          "name": "Broke with Expensive Taste",
          "1280": "https://resources.tidal.com/images/c50c9146/3c7a/44e6/a1bc/55b20516eb29/1280x1280.jpg",
          "640": "https://resources.tidal.com/images/c50c9146/3c7a/44e6/a1bc/55b20516eb29/640x640.jpg",
          "80": "https://resources.tidal.com/images/c50c9146/3c7a/44e6/a1bc/55b20516eb29/80x80.jpg"
        }
      ]
    }
  12. Get album details

    main

    The GET /album/ endpoint retrieves information about a specific album.

    Parameters:

    • id (int, required): The album ID.
    • limit (int, optional, default 100, min 1, max 500): Number of items to return.
    • offset (int, optional, default 0, min 0): Item offset for pagination.

    Response: 200 OK.

    GET /album/?id={album_id}&limit=100