Files
MusicPlayer/server/openapi.json
T
SansanandClaude Opus 5.5 c86dacb890
Build / test (pull_request) Successful in 2m3s
Build / build (pull_request) Skipped
Add a soundtrack by picking or dropping a folder (#50)
"Add soundtrack…" in the statistics toolbar, or a folder dropped anywhere on
the window, opens a popup that lists the folder's songs and uploads them to
the server, which adds the soundtrack at once (Sansan/MusicServer#33).

- The name comes from the folder and can be changed; characters the server
  refuses are replaced.
- Only MP3s are uploaded, as the server takes no other; other song files
  are listed greyed out as left out. Hidden files are skipped.
- Disc folders are flattened with the disc's number first ("1-01 Title.mp3"),
  as the server wants one folder. With one disc folder, names stay as
  they are.
- Songs are sent one at a time with a progress bar, reading one file at a
  time. A failed song is sent again twice, then the upload is cancelled
  and the server's reason shown. Closing the popup cancels it too.
- Not offered while the server syncs, offline, or without MP3s.
- Afterwards search, the inspiration list and the statistics reload.

The API types come from the server's spec (Upload, UploadFile,
UploadRequest).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 21:43:59 +02:00

1 line
66 KiB
JSON

{"components":{"schemas":{"APIError":{"additionalProperties":false,"properties":{"error":{"description":"What went wrong","examples":["Not Found"],"type":"string"}},"required":["error"],"type":"object"},"AlternativeNames":{"additionalProperties":false,"properties":{"names":{"description":"The new list of alternative names. Replaces the old list; send [] to remove them all.","items":{"type":"string"},"maxItems":50,"type":"array"}},"required":["names"],"type":"object"},"CharacterDetails":{"additionalProperties":false,"properties":{"modified":{"description":"When the image was last changed, in Unix seconds","format":"int64","type":"integer"},"name":{"description":"File name of the character image","type":"string"}},"required":["name","modified"],"type":"object"},"CoverSource":{"additionalProperties":false,"properties":{"id":{"description":"The game's ID on SteamGridDB, or the anime's ID on AniList","maxLength":50,"minLength":1,"type":"string"},"source":{"description":"Where the cover comes from","enum":["steamgriddb","anilist"],"type":"string"}},"required":["source","id"],"type":"object"},"ProgressResponse":{"additionalProperties":false,"properties":{"progress":{"type":"string"},"time_spent":{"type":"string"}},"required":["progress","time_spent"],"type":"object"},"Song":{"additionalProperties":false,"properties":{"file_name":{"type":"string"},"name":{"type":"string"},"soundtrack_name":{"type":"string"},"soundtrack_uuid":{"type":"string"},"times_played":{"format":"int32","type":"integer"},"uuid":{"type":"string"}},"required":["uuid","name","times_played","soundtrack_uuid","soundtrack_name"],"type":"object"},"SongInfo":{"additionalProperties":false,"properties":{"CurrentlyPlaying":{"type":"boolean"},"Song":{"type":"string"},"SongNo":{"format":"int64","type":"integer"},"SongPlayed":{"format":"int32","type":"integer"},"SongUuid":{"type":"string"},"Soundtrack":{"type":"string"},"SoundtrackPlayed":{"format":"int32","type":"integer"},"SoundtrackUuid":{"type":"string"}},"required":["SoundtrackUuid","Soundtrack","SoundtrackPlayed","SongUuid","Song","SongPlayed","CurrentlyPlaying","SongNo"],"type":"object"},"SongInfoForStats":{"additionalProperties":false,"properties":{"file_name":{"type":"string"},"path":{"type":"string"},"song_name":{"type":"string"},"song_uuid":{"type":"string"},"soundtrack_id":{"format":"int32","type":"integer"},"soundtrack_name":{"type":"string"},"soundtrack_uuid":{"type":"string"},"times_played":{"format":"int32","type":"integer"}},"required":["soundtrack_id","soundtrack_uuid","song_uuid","soundtrack_name","song_name","path","times_played"],"type":"object"},"Soundtrack":{"additionalProperties":false,"properties":{"added":{"format":"date-time","type":"string"},"alternative_names":{"description":"Other names the soundtrack is known by","items":{"type":"string"},"type":"array"},"cover_modified":{"description":"When the cover last changed, in Unix seconds. Missing when there is no cover.","format":"int64","type":"integer"},"last_played":{"format":"date-time","type":"string"},"name":{"type":"string"},"times_played":{"format":"int32","type":"integer"},"uuid":{"type":"string"}},"required":["uuid","name","alternative_names","times_played","added"],"type":"object"},"SoundtrackWithSongList":{"additionalProperties":false,"properties":{"added":{"format":"date-time","type":"string"},"alternative_names":{"description":"Other names the soundtrack is known by","items":{"type":"string"},"type":"array"},"cover_modified":{"description":"When the cover last changed, in Unix seconds. Missing when there is no cover.","format":"int64","type":"integer"},"last_played":{"format":"date-time","type":"string"},"name":{"type":"string"},"songs":{"items":{"$ref":"#/components/schemas/Song"},"type":"array"},"times_played":{"format":"int32","type":"integer"},"uuid":{"type":"string"}},"required":["songs","uuid","name","alternative_names","times_played","added"],"type":"object"},"SoundtrackWithSongs":{"additionalProperties":false,"properties":{"alternative_names":{"description":"Other names the soundtrack is known by","items":{"type":"string"},"type":"array"},"cover_modified":{"description":"When the cover last changed, in Unix seconds. Missing when there is no cover.","format":"int64","type":"integer"},"songs":{"items":{"$ref":"#/components/schemas/SongInfoForStats"},"type":"array"},"soundtrack_id":{"format":"int32","type":"integer"},"soundtrack_last_played":{"format":"date-time","type":"string"},"soundtrack_name":{"type":"string"},"soundtrack_played":{"format":"int32","type":"integer"},"soundtrack_uuid":{"type":"string"}},"required":["soundtrack_id","soundtrack_uuid","soundtrack_name","alternative_names","soundtrack_played","songs"],"type":"object"},"StatisticsSummary":{"additionalProperties":false,"properties":{"avg_soundtrack_plays":{"format":"double","type":"number"},"max_soundtrack_plays":{"format":"int64","type":"integer"},"min_soundtrack_plays":{"format":"int64","type":"integer"},"never_played_soundtracks":{"format":"int64","type":"integer"},"played_soundtracks":{"format":"int64","type":"integer"},"total_soundtrack_plays":{"format":"int64","type":"integer"},"total_soundtracks":{"format":"int64","type":"integer"}},"required":["total_soundtracks","played_soundtracks","never_played_soundtracks","total_soundtrack_plays","avg_soundtrack_plays","max_soundtrack_plays","min_soundtrack_plays"],"type":"object"},"StatusMessage":{"additionalProperties":false,"properties":{"status":{"type":"string"}},"required":["status"],"type":"object"},"SyncResponse":{"additionalProperties":false,"properties":{"catched_errors":{"items":{"type":"string"},"type":"array"},"soundtracks_added":{"items":{"type":"string"},"type":"array"},"soundtracks_changed_content":{"items":{"type":"string"},"type":"array"},"soundtracks_changed_title":{"additionalProperties":{"type":"string"},"type":"object"},"soundtracks_re_added":{"items":{"type":"string"},"type":"array"},"soundtracks_removed":{"items":{"type":"string"},"type":"array"},"total_time":{"type":"string"}},"required":["soundtracks_added","soundtracks_re_added","soundtracks_changed_title","soundtracks_changed_content","soundtracks_removed","catched_errors","total_time"],"type":"object"},"TokenRequest":{"additionalProperties":false,"properties":{"client_type":{"default":"web","description":"Kind of client, for example web, godot or api","type":"string"},"password":{"description":"API password, required when the server has one configured","type":"string"}},"type":"object"},"TokenResponse":{"additionalProperties":false,"properties":{"client_type":{"type":"string"},"expires_at":{"format":"date-time","type":"string"},"token":{"description":"Send as Authorization: Bearer \u003ctoken\u003e","type":"string"}},"required":["token","expires_at","client_type"],"type":"object"},"Upload":{"additionalProperties":false,"properties":{"files":{"description":"The songs received so far, sorted by name","items":{"$ref":"#/components/schemas/UploadFile"},"type":"array"},"id":{"type":"string"},"name":{"description":"Name of the new soundtrack, also its folder name","type":"string"},"started":{"format":"date-time","type":"string"},"total_size":{"description":"Size of all songs received so far, in bytes","format":"int64","type":"integer"}},"required":["id","name","started","files","total_size"],"type":"object"},"UploadFile":{"additionalProperties":false,"properties":{"name":{"type":"string"},"size":{"format":"int64","type":"integer"}},"required":["name","size"],"type":"object"},"UploadRequest":{"additionalProperties":false,"properties":{"name":{"description":"Name of the new soundtrack, also its folder name in the music folder","maxLength":200,"minLength":1,"type":"string"}},"required":["name"],"type":"object"},"VersionData":{"additionalProperties":false,"properties":{"changelog":{"examples":[["Initial release","Bug fixes"]],"items":{"type":"string"},"type":"array"},"version":{"examples":["1.0.0"],"type":"string"}},"required":["version","changelog"],"type":"object"}},"securitySchemes":{"bearer":{"description":"Token from POST /api/v1/token","scheme":"bearer","type":"http"}}},"info":{"description":"API for the MusicServer application.\n\nGet a token with `POST /api/v1/token` and send it as `Authorization: Bearer \u003ctoken\u003e`.\n\nEvery client has its own play queue. The music endpoints return its ID in the `X-Queue-Id` header; send it back in the same header to keep using that queue. Without it, or when the queue has ended (after a reset or a sync), a new queue is started.","title":"MusicServer API","version":"5.0.0"},"openapi":"3.1.0","paths":{"/api/v1/character":{"get":{"description":"Returns a character image.","operationId":"get-character","parameters":[{"description":"File name of the character image, from the character list","explode":false,"in":"query","name":"name","required":true,"schema":{"description":"File name of the character image, from the character list","type":"string"}}],"responses":{"200":{"content":{"image/jpeg":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}},"image/png":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}}},"description":"The image"},"206":{"content":{"image/jpeg":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}},"image/png":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}}},"description":"Part of the file, for a Range request"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Character image","tags":["characters"]}},"/api/v1/character/details":{"get":{"description":"Returns every character image with the time it was last changed, so clients can cache images and download only the changed ones.","operationId":"list-character-details","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/CharacterDetails"},"type":"array"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"List characters with change times","tags":["characters"]}},"/api/v1/character/list":{"get":{"description":"Returns the file names of all character images.","operationId":"list-characters","responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"string"},"type":"array"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"List characters","tags":["characters"]}},"/api/v1/download":{"get":{"description":"Returns the name of the latest MusicPlayer release.","operationId":"get-latest-client-version","responses":{"200":{"content":{"application/json":{"schema":{"type":"string"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Bad Gateway"}},"security":[{"bearer":[]}],"summary":"Latest client version","tags":["download"]}},"/api/v1/download/linux":{"get":{"description":"Redirects to the download URL of the latest MusicPlayer release for Linux.","operationId":"download-client-linux","responses":{"302":{"description":"Found","headers":{"Location":{"schema":{"description":"Download URL of the asset","type":"string"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Bad Gateway"}},"security":[{"bearer":[]}],"summary":"Download the latest client for Linux","tags":["download"]}},"/api/v1/download/linux-package":{"get":{"description":"Redirects to the download URL of the latest MusicPlayer release for Linux (package with installer).","operationId":"download-client-linux-package","responses":{"302":{"description":"Found","headers":{"Location":{"schema":{"description":"Download URL of the asset","type":"string"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Bad Gateway"}},"security":[{"bearer":[]}],"summary":"Download the latest client for Linux (package with installer)","tags":["download"]}},"/api/v1/download/list":{"get":{"description":"Returns the file names of the latest MusicPlayer release.","operationId":"list-client-assets","responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"string"},"type":"array"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Bad Gateway"}},"security":[{"bearer":[]}],"summary":"Latest client assets","tags":["download"]}},"/api/v1/download/windows":{"get":{"description":"Redirects to the download URL of the latest MusicPlayer release for Windows.","operationId":"download-client-windows","responses":{"302":{"description":"Found","headers":{"Location":{"schema":{"description":"Download URL of the asset","type":"string"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Bad Gateway"}},"security":[{"bearer":[]}],"summary":"Download the latest client for Windows","tags":["download"]}},"/api/v1/health":{"get":{"description":"Returns {\"status\": \"up\"} when the server and database work, otherwise status \"down\" and an error.","operationId":"get-health","responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object"}}},"description":"OK"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Error"}},"summary":"Health check","tags":["server"]}},"/api/v1/music":{"get":{"description":"Jumps to the song at the given queue position (clamped to the queue) and returns it.","operationId":"get-queue-song","parameters":[{"description":"ID of the client's play queue, from an earlier response. Omit it to start a new queue.","in":"header","name":"X-Queue-Id","schema":{"format":"uuid","type":"string"}},{"description":"Position of the song in the queue (SongNo)","explode":false,"in":"query","name":"song","required":true,"schema":{"description":"Position of the song in the queue (SongNo)","format":"int64","type":"integer"}}],"responses":{"200":{"content":{"audio/mpeg":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}}},"description":"The song (MP3)","headers":{"X-Queue-Id":{"description":"ID of the client's play queue. Send it with the following requests.","schema":{"format":"uuid","type":"string"}}}},"206":{"content":{"audio/mpeg":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}}},"description":"Part of the file, for a Range request","headers":{"X-Queue-Id":{"description":"ID of the client's play queue. Send it with the following requests.","schema":{"format":"uuid","type":"string"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unprocessable Entity"},"423":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Locked"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Play a song from the queue","tags":["music"]}},"/api/v1/music/addPlayed":{"get":{"description":"Increments the play counters of the current song and its soundtrack.","operationId":"mark-current-played","parameters":[{"description":"ID of the client's play queue, from an earlier response. Omit it to start a new queue.","in":"header","name":"X-Queue-Id","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"OK","headers":{"X-Queue-Id":{"description":"ID of the client's play queue. Send it with the following requests.","schema":{"format":"uuid","type":"string"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"423":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Locked"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Mark the current song as played","tags":["music"]}},"/api/v1/music/addQue":{"get":{"description":"Adds the song returned by the last random song request to the end of the queue.","operationId":"add-latest-to-queue","parameters":[{"description":"ID of the client's play queue, from an earlier response. Omit it to start a new queue.","in":"header","name":"X-Queue-Id","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"OK","headers":{"X-Queue-Id":{"description":"ID of the client's play queue. Send it with the following requests.","schema":{"format":"uuid","type":"string"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"423":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Locked"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Add the last random song to the queue","tags":["music"]}},"/api/v1/music/all":{"get":{"description":"Returns the names of all soundtracks in random order.","operationId":"get-soundtrack-names-random","responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"string"},"type":"array"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"423":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Locked"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Soundtrack names, random order","tags":["music"]}},"/api/v1/music/all/order":{"get":{"description":"Returns the names of all soundtracks in alphabetical order.","operationId":"get-soundtrack-names","responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"string"},"type":"array"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"423":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Locked"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Soundtrack names, sorted","tags":["music"]}},"/api/v1/music/all/random":{"get":{"description":"Same as /music/all.","operationId":"get-soundtrack-names-random-alias","responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"string"},"type":"array"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"423":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Locked"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Soundtrack names, random order","tags":["music"]}},"/api/v1/music/info":{"get":{"description":"Returns the current song in the queue.","operationId":"get-song-info","parameters":[{"description":"ID of the client's play queue, from an earlier response. Omit it to start a new queue.","in":"header","name":"X-Queue-Id","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SongInfo"}}},"description":"OK","headers":{"X-Queue-Id":{"description":"ID of the client's play queue. Send it with the following requests.","schema":{"format":"uuid","type":"string"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Current song","tags":["music"]}},"/api/v1/music/list":{"get":{"description":"Returns all songs in the queue.","operationId":"get-played-songs","parameters":[{"description":"ID of the client's play queue, from an earlier response. Omit it to start a new queue.","in":"header","name":"X-Queue-Id","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SongInfo"},"type":"array"}}},"description":"OK","headers":{"X-Queue-Id":{"description":"ID of the client's play queue. Send it with the following requests.","schema":{"format":"uuid","type":"string"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Queue","tags":["music"]}},"/api/v1/music/next":{"get":{"description":"Moves to the next song in the queue and returns it. Stays on the last song at the end.","operationId":"get-next-song","parameters":[{"description":"ID of the client's play queue, from an earlier response. Omit it to start a new queue.","in":"header","name":"X-Queue-Id","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"audio/mpeg":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}}},"description":"The song (MP3)","headers":{"X-Queue-Id":{"description":"ID of the client's play queue. Send it with the following requests.","schema":{"format":"uuid","type":"string"}}}},"206":{"content":{"audio/mpeg":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}}},"description":"Part of the file, for a Range request","headers":{"X-Queue-Id":{"description":"ID of the client's play queue. Send it with the following requests.","schema":{"format":"uuid","type":"string"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Not Found"},"423":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Locked"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Next song in the queue","tags":["music"]}},"/api/v1/music/played":{"put":{"description":"Increments the play counters of the song at the given queue position. Positions outside the queue are ignored.","operationId":"mark-song-played","parameters":[{"description":"ID of the client's play queue, from an earlier response. Omit it to start a new queue.","in":"header","name":"X-Queue-Id","schema":{"format":"uuid","type":"string"}},{"description":"Position of the song in the queue (SongNo)","explode":false,"in":"query","name":"song","required":true,"schema":{"description":"Position of the song in the queue (SongNo)","format":"int64","type":"integer"}}],"responses":{"200":{"description":"OK","headers":{"X-Queue-Id":{"description":"ID of the client's play queue. Send it with the following requests.","schema":{"format":"uuid","type":"string"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unprocessable Entity"},"423":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Locked"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Mark a queued song as played","tags":["music"]}},"/api/v1/music/previous":{"get":{"description":"Moves to the previous song in the queue and returns it. Stays on the first song at the start.","operationId":"get-previous-song","parameters":[{"description":"ID of the client's play queue, from an earlier response. Omit it to start a new queue.","in":"header","name":"X-Queue-Id","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"audio/mpeg":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}}},"description":"The song (MP3)","headers":{"X-Queue-Id":{"description":"ID of the client's play queue. Send it with the following requests.","schema":{"format":"uuid","type":"string"}}}},"206":{"content":{"audio/mpeg":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}}},"description":"Part of the file, for a Range request","headers":{"X-Queue-Id":{"description":"ID of the client's play queue. Send it with the following requests.","schema":{"format":"uuid","type":"string"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Not Found"},"423":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Locked"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Previous song in the queue","tags":["music"]}},"/api/v1/music/rand":{"get":{"description":"Picks a random song from a random soundtrack. Add it to the queue with /music/addQue.","operationId":"get-random-song","parameters":[{"description":"ID of the client's play queue, from an earlier response. Omit it to start a new queue.","in":"header","name":"X-Queue-Id","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"audio/mpeg":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}}},"description":"The song (MP3)","headers":{"X-Queue-Id":{"description":"ID of the client's play queue. Send it with the following requests.","schema":{"format":"uuid","type":"string"}}}},"206":{"content":{"audio/mpeg":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}}},"description":"Part of the file, for a Range request","headers":{"X-Queue-Id":{"description":"ID of the client's play queue. Send it with the following requests.","schema":{"format":"uuid","type":"string"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Not Found"},"423":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Locked"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Random song","tags":["music"]}},"/api/v1/music/rand/classic":{"get":{"description":"Picks a random song where every song has the same chance, regardless of soundtrack.","operationId":"get-random-song-classic","parameters":[{"description":"ID of the client's play queue, from an earlier response. Omit it to start a new queue.","in":"header","name":"X-Queue-Id","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"audio/mpeg":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}}},"description":"The song (MP3)","headers":{"X-Queue-Id":{"description":"ID of the client's play queue. Send it with the following requests.","schema":{"format":"uuid","type":"string"}}}},"206":{"content":{"audio/mpeg":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}}},"description":"Part of the file, for a Range request","headers":{"X-Queue-Id":{"description":"ID of the client's play queue. Send it with the following requests.","schema":{"format":"uuid","type":"string"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Not Found"},"423":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Locked"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Random song, every song equal","tags":["music"]}},"/api/v1/music/rand/low":{"get":{"description":"Picks a random song, favouring soundtracks that have been played less than average.","operationId":"get-random-song-low-chance","parameters":[{"description":"ID of the client's play queue, from an earlier response. Omit it to start a new queue.","in":"header","name":"X-Queue-Id","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"audio/mpeg":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}}},"description":"The song (MP3)","headers":{"X-Queue-Id":{"description":"ID of the client's play queue. Send it with the following requests.","schema":{"format":"uuid","type":"string"}}}},"206":{"content":{"audio/mpeg":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}}},"description":"Part of the file, for a Range request","headers":{"X-Queue-Id":{"description":"ID of the client's play queue. Send it with the following requests.","schema":{"format":"uuid","type":"string"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Not Found"},"423":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Locked"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Random song, favouring rarely played","tags":["music"]}},"/api/v1/music/reset":{"get":{"description":"Ends the queue, starts a new one and reloads the soundtrack list. The new queue's ID is returned in the X-Queue-Id header; send it with the following requests.","operationId":"reset-queue","parameters":[{"description":"ID of the client's play queue, from an earlier response. Omit it to start a new queue.","in":"header","name":"X-Queue-Id","schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"description":"OK","headers":{"X-Queue-Id":{"schema":{"description":"ID of the new queue","type":"string"}}}},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"423":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Locked"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Reset the queue","tags":["music"]}},"/api/v1/music/soundTest":{"get":{"description":"Returns a random song for checking the volume.","operationId":"get-sound-test-song","responses":{"200":{"content":{"audio/mpeg":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}}},"description":"The song (MP3)"},"206":{"content":{"audio/mpeg":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}}},"description":"Part of the file, for a Range request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Not Found"},"423":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Locked"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Sound check song","tags":["music"]}},"/api/v1/songs/{uuid}":{"get":{"description":"Returns information about a song.","operationId":"get-song","parameters":[{"description":"UUID of the soundtrack or song","in":"path","name":"uuid","required":true,"schema":{"description":"UUID of the soundtrack or song","format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Song"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Get song","tags":["library"]}},"/api/v1/songs/{uuid}/file":{"get":{"description":"Returns the audio file of a song.","operationId":"get-song-file","parameters":[{"description":"UUID of the soundtrack or song","in":"path","name":"uuid","required":true,"schema":{"description":"UUID of the soundtrack or song","format":"uuid","type":"string"}}],"responses":{"200":{"content":{"audio/mpeg":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}}},"description":"The song (MP3)"},"206":{"content":{"audio/mpeg":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}}},"description":"Part of the file, for a Range request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unprocessable Entity"},"423":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Locked"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Stream song","tags":["library"]}},"/api/v1/soundtracks":{"get":{"description":"Returns all soundtracks, sorted by name.","operationId":"list-soundtracks","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Soundtrack"},"type":"array"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"List soundtracks","tags":["library"]}},"/api/v1/soundtracks/uploads":{"post":{"description":"Starts adding a new soundtrack. Send its songs with PUT /soundtracks/uploads/{id}/files/{file}, then POST /soundtracks/uploads/{id}/finish. The name must be usable as a folder name, and no soundtrack, folder or other upload may have it, ignoring case. Unfinished uploads are removed after a day.","operationId":"start-upload","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Upload"}}},"description":"Created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unprocessable Entity"},"423":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Locked"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Service Unavailable"}},"security":[{"bearer":[]}],"summary":"Start uploading a soundtrack","tags":["uploads"]}},"/api/v1/soundtracks/uploads/{id}":{"delete":{"description":"Deletes an unfinished upload and its songs.","operationId":"cancel-upload","parameters":[{"description":"ID of the upload, from POST /soundtracks/uploads","in":"path","name":"id","required":true,"schema":{"description":"ID of the upload, from POST /soundtracks/uploads","format":"uuid","type":"string"}}],"responses":{"204":{"description":"No Content"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unprocessable Entity"},"423":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Locked"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Cancel an upload","tags":["uploads"]},"get":{"description":"Returns an unfinished upload with the songs received so far, for example to resume it.","operationId":"get-upload","parameters":[{"description":"ID of the upload, from POST /soundtracks/uploads","in":"path","name":"id","required":true,"schema":{"description":"ID of the upload, from POST /soundtracks/uploads","format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Upload"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unprocessable Entity"},"423":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Locked"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Get an upload","tags":["uploads"]}},"/api/v1/soundtracks/uploads/{id}/files/{file}":{"put":{"description":"Stores one song of the upload, replacing a song of the same name, so a failed song can be sent again. Only MP3 files are accepted. The size limits are set on the server with UPLOAD_MAX_FILE_MB and UPLOAD_MAX_TOTAL_MB.","operationId":"upload-song","parameters":[{"description":"ID of the upload, from POST /soundtracks/uploads","in":"path","name":"id","required":true,"schema":{"description":"ID of the upload, from POST /soundtracks/uploads","format":"uuid","type":"string"}},{"description":"File name of the song, e.g. \"01 Title.mp3\". Plain names only: put songs from disc folders in one folder, e.g. \"1-01 Title.mp3\".","in":"path","name":"file","required":true,"schema":{"description":"File name of the song, e.g. \"01 Title.mp3\". Plain names only: put songs from disc folders in one folder, e.g. \"1-01 Title.mp3\".","type":"string"}}],"requestBody":{"content":{"audio/mpeg":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadFile"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Not Found"},"413":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Request Entity Too Large"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unprocessable Entity"},"423":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Locked"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Upload a song","tags":["uploads"]}},"/api/v1/soundtracks/uploads/{id}/finish":{"post":{"description":"Moves the songs into a new folder in the music folder, adds the soundtrack to the library and returns it. Refused when a soundtrack with the same songs exists.","operationId":"finish-upload","parameters":[{"description":"ID of the upload, from POST /soundtracks/uploads","in":"path","name":"id","required":true,"schema":{"description":"ID of the upload, from POST /soundtracks/uploads","format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SoundtrackWithSongList"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Not Found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unprocessable Entity"},"423":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Locked"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Finish an upload","tags":["uploads"]}},"/api/v1/soundtracks/{uuid}":{"get":{"description":"Returns a soundtrack with its songs, sorted by name.","operationId":"get-soundtrack","parameters":[{"description":"UUID of the soundtrack or song","in":"path","name":"uuid","required":true,"schema":{"description":"UUID of the soundtrack or song","format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SoundtrackWithSongList"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Get soundtrack","tags":["library"]}},"/api/v1/soundtracks/{uuid}/alternative-names":{"put":{"description":"Replaces the soundtrack's alternative names, which search and the cover search also use. Names are trimmed, and empty names, duplicates and the soundtrack's own name (ignoring case) are dropped.","operationId":"set-alternative-names","parameters":[{"description":"UUID of the soundtrack","in":"path","name":"uuid","required":true,"schema":{"description":"UUID of the soundtrack","format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlternativeNames"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Soundtrack"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Set alternative names","tags":["library"]}},"/api/v1/soundtracks/{uuid}/cover":{"delete":{"description":"Deletes the soundtrack's cover, including one set by hand, so the automatic search looks for a new one.","operationId":"delete-soundtrack-cover","parameters":[{"description":"UUID of the soundtrack or song","in":"path","name":"uuid","required":true,"schema":{"description":"UUID of the soundtrack or song","format":"uuid","type":"string"}}],"responses":{"204":{"description":"No Content"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Service Unavailable"}},"security":[{"bearer":[]}],"summary":"Remove the cover","tags":["covers"]},"get":{"description":"Returns the soundtrack's cover image. cover_modified in the soundtrack responses tells when it last changed.","operationId":"get-soundtrack-cover","parameters":[{"description":"UUID of the soundtrack or song","in":"path","name":"uuid","required":true,"schema":{"description":"UUID of the soundtrack or song","format":"uuid","type":"string"}}],"responses":{"200":{"content":{"image/jpeg":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}},"image/png":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}},"image/webp":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}}},"description":"The cover image"},"206":{"content":{"image/jpeg":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}},"image/png":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}},"image/webp":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}}},"description":"Part of the file, for a Range request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Soundtrack cover","tags":["covers"]},"put":{"description":"Stores the image as the soundtrack's cover. The automatic search never replaces it; delete it to go back to automatic.","operationId":"upload-soundtrack-cover","parameters":[{"description":"UUID of the soundtrack","in":"path","name":"uuid","required":true,"schema":{"description":"UUID of the soundtrack","format":"uuid","type":"string"}}],"requestBody":{"content":{"image/*":{"schema":{"contentMediaType":"application/octet-stream","format":"binary","type":"string"}}},"required":true},"responses":{"204":{"description":"No Content"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Service Unavailable"}},"security":[{"bearer":[]}],"summary":"Upload a cover","tags":["covers"]}},"/api/v1/soundtracks/{uuid}/cover/source":{"put":{"description":"Downloads the cover of the given SteamGridDB game or AniList anime and stores it as the soundtrack's cover. The automatic search never replaces it. SteamGridDB needs STEAMGRIDDB_API_KEY on the server.","operationId":"set-soundtrack-cover-source","parameters":[{"description":"UUID of the soundtrack","in":"path","name":"uuid","required":true,"schema":{"description":"UUID of the soundtrack","format":"uuid","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CoverSource"}}},"required":true},"responses":{"204":{"description":"No Content"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Not Found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Bad Gateway"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Service Unavailable"}},"security":[{"bearer":[]}],"summary":"Pick a cover","tags":["covers"]}},"/api/v1/statistics/songs/least-played":{"get":{"description":"Returns the least played songs with their soundtrack.","operationId":"get-least-played-songs","parameters":[{"description":"Number of results. Values above 100 are capped to 100.","explode":false,"in":"query","name":"limit","schema":{"default":10,"description":"Number of results. Values above 100 are capped to 100.","format":"int64","minimum":1,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SongInfoForStats"},"type":"array"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Least played songs","tags":["statistics"]}},"/api/v1/statistics/songs/most-played":{"get":{"description":"Returns the most played songs with their soundtrack.","operationId":"get-most-played-songs","parameters":[{"description":"Number of results. Values above 100 are capped to 100.","explode":false,"in":"query","name":"limit","schema":{"default":10,"description":"Number of results. Values above 100 are capped to 100.","format":"int64","minimum":1,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SongInfoForStats"},"type":"array"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Most played songs","tags":["statistics"]}},"/api/v1/statistics/soundtracks":{"get":{"description":"Returns every soundtrack with its songs and play counts, sorted by name.","operationId":"get-all-soundtrack-statistics","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SoundtrackWithSongs"},"type":"array"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"All soundtracks with statistics","tags":["statistics"]}},"/api/v1/statistics/soundtracks/last-played":{"get":{"description":"Returns the most recently played soundtracks with their songs.","operationId":"get-last-played-soundtracks","parameters":[{"description":"Number of results. Values above 100 are capped to 100.","explode":false,"in":"query","name":"limit","schema":{"default":10,"description":"Number of results. Values above 100 are capped to 100.","format":"int64","minimum":1,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SoundtrackWithSongs"},"type":"array"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Recently played soundtracks","tags":["statistics"]}},"/api/v1/statistics/soundtracks/least-played":{"get":{"description":"Returns the least played soundtracks with their songs.","operationId":"get-least-played-soundtracks","parameters":[{"description":"Number of results. Values above 100 are capped to 100.","explode":false,"in":"query","name":"limit","schema":{"default":10,"description":"Number of results. Values above 100 are capped to 100.","format":"int64","minimum":1,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SoundtrackWithSongs"},"type":"array"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Least played soundtracks","tags":["statistics"]}},"/api/v1/statistics/soundtracks/most-played":{"get":{"description":"Returns the most played soundtracks with their songs.","operationId":"get-most-played-soundtracks","parameters":[{"description":"Number of results. Values above 100 are capped to 100.","explode":false,"in":"query","name":"limit","schema":{"default":10,"description":"Number of results. Values above 100 are capped to 100.","format":"int64","minimum":1,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SoundtrackWithSongs"},"type":"array"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Most played soundtracks","tags":["statistics"]}},"/api/v1/statistics/soundtracks/never-played":{"get":{"description":"Returns all soundtracks that have never been played, with their songs.","operationId":"get-never-played-soundtracks","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SoundtrackWithSongs"},"type":"array"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Never played soundtracks","tags":["statistics"]}},"/api/v1/statistics/soundtracks/oldest-played":{"get":{"description":"Returns the soundtracks played longest ago (played at least once) with their songs.","operationId":"get-oldest-played-soundtracks","parameters":[{"description":"Number of results. Values above 100 are capped to 100.","explode":false,"in":"query","name":"limit","schema":{"default":10,"description":"Number of results. Values above 100 are capped to 100.","format":"int64","minimum":1,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SoundtrackWithSongs"},"type":"array"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Least recently played soundtracks","tags":["statistics"]}},"/api/v1/statistics/summary":{"get":{"description":"Returns overall play statistics.","operationId":"get-statistics-summary","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatisticsSummary"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Statistics summary","tags":["statistics"]}},"/api/v1/sync":{"post":{"description":"Starts syncing the music folder in the background, skipping soundtracks that have not changed. Poll /sync/progress for the result.","operationId":"start-sync","responses":{"200":{"content":{"application/json":{"schema":{"type":"string"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"423":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Locked"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Sync changed soundtracks","tags":["sync"]}},"/api/v1/sync/full":{"post":{"description":"Starts a full sync in the background that re-reads every soundtrack and removes songs whose files are gone. Poll /sync/progress for the result.","operationId":"start-full-sync","responses":{"200":{"content":{"application/json":{"schema":{"type":"string"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"423":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Locked"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Sync all soundtracks","tags":["sync"]}},"/api/v1/sync/progress":{"get":{"description":"While a sync is running, returns its progress. Otherwise returns the result of the last sync.","operationId":"get-sync-progress","responses":{"200":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProgressResponse"},{"$ref":"#/components/schemas/SyncResponse"}]}}},"description":"Progress of the running sync, or the result of the last sync"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Sync progress or result","tags":["sync"]}},"/api/v1/token":{"delete":{"description":"Deletes the token used for this request.","operationId":"delete-token","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatusMessage"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Invalidate session token","tags":["auth"]},"post":{"description":"Returns a token for the other /api/v1 endpoints, valid for 24 hours. Requires the API password when the server has one. Limited to 10 requests per minute per client.","operationId":"create-token","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unprocessable Entity"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Too Many Requests"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"summary":"Create session token","tags":["auth"]}},"/api/v1/token/cleanup":{"post":{"description":"Deletes all expired session tokens.","operationId":"cleanup-tokens","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatusMessage"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Remove expired tokens","tags":["auth"]}},"/api/v1/version":{"get":{"description":"Returns the server version and its changelog.","operationId":"get-version","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VersionData"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Latest server version","tags":["server"]}},"/api/v1/version/history":{"get":{"description":"Returns all server versions with their changelogs, newest first.","operationId":"get-version-history","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/VersionData"},"type":"array"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIError"}}},"description":"Internal Server Error"}},"security":[{"bearer":[]}],"summary":"Version history","tags":["server"]}}}}