Files
furumi-ng/docs/PLAYER-API.md
AB-UK 8d70a5133a
All checks were successful
Publish Metadata Agent Image (dev) / build-and-push-image (push) Successful in 1m17s
Publish Web Player Image (dev) / build-and-push-image (push) Successful in 1m14s
Disabled obsolete CI
2026-03-20 00:49:27 +00:00

4.6 KiB

Furumi Web Player API

Base URL: http://<host>:<port>/api

All endpoints require authentication when --token is set (via cookie furumi_token=<token> or query param ?token=<token>).

All entity references use slugs — 12-character hex identifiers (not sequential IDs).

Artists

GET /api/artists

List all artists that have at least one track.

Response:

[
  {
    "slug": "a1b2c3d4e5f6",
    "name": "Pink Floyd",
    "album_count": 5,
    "track_count": 42
  }
]

Sorted alphabetically by name.

GET /api/artists/:slug

Get artist details.

Response:

{
  "slug": "a1b2c3d4e5f6",
  "name": "Pink Floyd"
}

Errors: 404 if not found.

GET /api/artists/:slug/albums

List all albums by an artist.

Response:

[
  {
    "slug": "b2c3d4e5f6a7",
    "name": "Wish You Were Here",
    "year": 1975,
    "track_count": 5,
    "has_cover": true
  }
]

Sorted by year (nulls last), then name.

GET /api/artists/:slug/tracks

List all tracks by an artist across all albums.

Response: same as album tracks (see below).

Sorted by album year, album name, track number, title.

Albums

GET /api/albums/:slug

List all tracks in an album.

Response:

[
  {
    "slug": "c3d4e5f6a7b8",
    "title": "Have a Cigar",
    "track_number": 3,
    "duration_secs": 312.5,
    "artist_name": "Pink Floyd",
    "album_name": "Wish You Were Here",
    "album_slug": "b2c3d4e5f6a7",
    "genre": "Progressive Rock"
  }
]

Sorted by track number (nulls last), then title. Fields album_name, album_slug may be null for tracks without an album.

GET /api/albums/:slug/cover

Serve the album cover image from the album_images table.

Response: Binary image data with appropriate Content-Type (image/jpeg, image/png, etc.) and Cache-Control: public, max-age=86400.

Errors: 404 if no cover exists.

Tracks

GET /api/tracks/:slug

Get full track details.

Response:

{
  "slug": "c3d4e5f6a7b8",
  "title": "Have a Cigar",
  "track_number": 3,
  "duration_secs": 312.5,
  "genre": "Progressive Rock",
  "storage_path": "/music/storage/Pink Floyd/Wish You Were Here/03 - Have a Cigar.flac",
  "artist_name": "Pink Floyd",
  "artist_slug": "a1b2c3d4e5f6",
  "album_name": "Wish You Were Here",
  "album_slug": "b2c3d4e5f6a7",
  "album_year": 1975
}

Errors: 404 if not found.

GET /api/tracks/:slug/cover

Serve cover art for a specific track. Resolution order:

  1. Album cover from album_images table (if the track belongs to an album with a cover)
  2. Embedded cover art extracted from the audio file metadata (ID3/Vorbis/etc. via Symphonia)
  3. 404 if no cover art is available

Response: Binary image data with Content-Type and Cache-Control: public, max-age=86400.

Errors: 404 if no cover art found.

Streaming

GET /api/stream/:slug

Stream the audio file for a track.

Supports HTTP Range requests for seeking:

  • Full response: 200 OK with Content-Length and Accept-Ranges: bytes
  • Partial response: 206 Partial Content with Content-Range
  • Invalid range: 416 Range Not Satisfiable

Content-Type is determined by the file extension (e.g. audio/flac, audio/mpeg).

Errors: 404 if track or file not found.

GET /api/search?q=<query>&limit=<n>

Search across artists, albums, and tracks by name (case-insensitive substring match).

Parameter Required Default Description
q yes Search query
limit no 20 Max results

Response:

[
  {
    "result_type": "artist",
    "slug": "a1b2c3d4e5f6",
    "name": "Pink Floyd",
    "detail": null
  },
  {
    "result_type": "album",
    "slug": "b2c3d4e5f6a7",
    "name": "Wish You Were Here",
    "detail": "Pink Floyd"
  },
  {
    "result_type": "track",
    "slug": "c3d4e5f6a7b8",
    "name": "Have a Cigar",
    "detail": "Pink Floyd"
  }
]

detail contains the artist name for albums and tracks, null for artists.

Sorted by result type (artist → album → track), then by name.

Authentication

When --token / FURUMI_PLAYER_TOKEN is set:

  • Cookie: furumi_token=<token> — set after login
  • Query parameter: ?token=<token> — redirects to player and sets cookie

When token is empty, authentication is disabled and all endpoints are public.

Unauthenticated requests receive 401 Unauthorized with a login form.

Error format

All errors return JSON:

{
  "error": "description of the error"
}

With appropriate HTTP status code (400, 404, 500, etc.).