Files
SansanandClaude Opus 5.5 0862d160e5
Build / test (push) Successful in 1m55s
Build / build (push) Successful in 1m12s
Give the sync progress bar colour and animation
The bar in the Sync popup is now an AnimatedProgressBar: taller, with a
darker track and the theme's accent fill (orange, frost blue in Nord, green
with a gold shine for Christmas). It shows Godot's busy animation until the
server reports progress, glides to each new value, has a soft highlight
sweeping along the filled part inside its rounded corners, and fills and
pops before the result shows. With Reduce animations it only jumps to its
values.

The styles are SyncProgressBar, SyncProgressBarFill and SyncProgressBarText
in every theme. Anim.stop stops a keyed animation without starting an empty
one.

The server now sends sync times as "MM:SS"; the spec is updated from
MusicServer develop (also the linux-package route), and the test data and
screenshots use the new format.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 11:13:43 +02:00

4353 lines
116 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": {
"description": "How long the sync has been running, as minutes and seconds, e.g. \"02:15\"",
"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": {
"description": "How long the sync took, as minutes and seconds, e.g. \"02:15\"",
"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 <token>",
"type": "string"
}
},
"required": [
"token",
"expires_at",
"client_type"
],
"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 <token>`.\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/{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"
]
}
}
}
}