feat(uploads): match release groups with MusicBrainz and AcoustID
ci / module boundaries / boundaries (push) Successful in 10s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m30s
ci / api / api (push) Successful in 58s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m1s
ci / db / db (push) Failing after 25s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 1m36s
ci / ts / ts (packages + web) (push) Failing after 6m3s

This commit is contained in:
mrleo1nid committed 2026-10-29 20:44:59 +03:00
1 parent 1e3a149aef
commit 39bf7d4b70
45 files changed
+4792 -417

No files matched your search

+193
View File
@@ -1100,6 +1100,105 @@ paths:
$ref: "#/components/schemas/UploadFile"
default:
$ref: "#/components/responses/Problem"
/api/v1/admin/uploads/batches/{id}/releases:
parameters:
- $ref: "#/components/parameters/EntityId"
get:
operationId: getUploadReleases
parameters:
- name: group_id
in: query
schema: {type: string, format: uuid}
tags: [uploads]
summary: Состояние фонового сопоставления пачки с изданиями MusicBrainz
responses:
"200":
description: Состояние поиска и кандидаты; текущие файлы не изменяются
content:
application/json:
schema: {$ref: "#/components/schemas/UploadReleaseReport"}
default: {$ref: "#/components/responses/Problem"}
post:
operationId: searchUploadReleases
tags: [uploads]
summary: Повторить поиск релиза, при необходимости с уточнённым запросом
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
artist: {type: string, maxLength: 500}
album: {type: string, maxLength: 500}
group_id: {type: string, format: uuid}
release_id: {type: string, format: uuid}
audio:
type: boolean
description: Явно распознать до 50 редактируемых треков через AcoustID; теги и аудио не отправляются. Требуется ключ приложения на сервере.
responses:
"202":
description: Поиск поставлен в фоновую очередь
content:
application/json:
schema: {$ref: "#/components/schemas/UploadReleaseReport"}
default: {$ref: "#/components/responses/Problem"}
/api/v1/admin/uploads/batches/{id}/releases/{release_id}/cover:
parameters:
- $ref: "#/components/parameters/EntityId"
- name: release_id
in: path
required: true
schema: {type: string, format: uuid}
get:
operationId: getUploadReleaseCover
parameters:
- name: group_id
in: query
schema: {type: string, format: uuid}
tags: [uploads]
summary: Предпросмотр обложки выбранного кандидата из Cover Art Archive
responses:
"200":
description: Ограниченное по размеру изображение; не меняет обложку пачки
content:
application/json:
schema:
type: object
required: [image_base64, mime_type, source_url, exact_release]
properties:
image_base64: {type: string}
mime_type: {type: string}
source_url: {type: string}
exact_release: {type: boolean}
default: {$ref: "#/components/responses/Problem"}
/api/v1/admin/uploads/batches/{id}/release:
parameters:
- $ref: "#/components/parameters/EntityId"
post:
operationId: applyUploadRelease
tags: [uploads]
summary: Явно применить метаданные и/или обложку выбранного издания
description: Только к совпавшим редактируемым файлам. Изменившаяся пачка или повторный поиск приводят к 409; одобрение и квота не меняются.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [release_id, apply_metadata, apply_cover]
properties:
group_id: {type: string, format: uuid}
release_id: {type: string, format: uuid}
apply_metadata: {type: boolean}
apply_cover: {type: boolean}
responses:
"200":
description: Применено к текущей пачке
content:
application/json:
schema: {$ref: "#/components/schemas/UploadReleaseReport"}
default: {$ref: "#/components/responses/Problem"}
/api/v1/admin/uploads/review:
post:
operationId: reviewUploads
@@ -2975,6 +3074,100 @@ components:
target_source_id:
type: string
format: uuid
ReleaseTrack:
type: object
required: [recording_id, title, artist, disc, position, duration_ms]
properties:
recording_id: {type: string}
title: {type: string}
artist: {type: string}
disc: {type: integer}
position: {type: integer}
duration_ms: {type: integer}
ReleaseInfo:
type: object
required: [id, group_id, title, artist, type, date, country, comment, has_cover, tracks]
properties:
id: {type: string}
group_id: {type: string}
title: {type: string}
artist: {type: string}
type: {type: string}
date: {type: string}
country: {type: string}
comment: {type: string}
has_cover: {type: boolean}
tracks:
type: array
items: {$ref: "#/components/schemas/ReleaseTrack"}
ReleaseCandidate:
type: object
required: [release, matches, missing, existing, unmatched, matched, total, complete, confidence]
properties:
release: {$ref: "#/components/schemas/ReleaseInfo"}
matches:
type: array
items:
type: object
required: [file_id, track_index, confidence]
properties:
file_id: {type: string, format: uuid}
track_index: {type: integer}
confidence: {type: string}
method: {type: string, enum: [metadata, acoustid]}
missing:
type: array
items: {$ref: "#/components/schemas/ReleaseTrack"}
existing:
type: array
description: Совпадения отсутствующих дорожек с библиотекой по метаданным и длительности; не подтверждают полную загруженную пачку
items: {$ref: "#/components/schemas/ReleaseTrack"}
unmatched:
type: array
items: {type: string, format: uuid}
matched: {type: integer}
total: {type: integer}
complete: {type: boolean}
confidence: {type: string}
UploadReleaseGroup:
type: object
required: [id, artist, album, file_ids, has_cover, state]
properties:
id: {type: string, format: uuid}
artist: {type: string}
album: {type: string}
file_ids:
type: array
items: {type: string, format: uuid}
has_cover: {type: boolean}
state: {type: string}
UploadReleaseReport:
type: object
required: [state, input_hash, candidates, selected_release]
properties:
group_id: {type: string, format: uuid}
groups:
type: array
items: {$ref: "#/components/schemas/UploadReleaseGroup"}
audio_available: {type: boolean}
audio_results:
type: array
items:
type: object
required: [file_id, state]
properties:
file_id: {type: string, format: uuid}
state: {type: string, enum: [matched, ambiguous, not_found, unavailable]}
recording_id: {type: string, format: uuid}
score: {type: number, minimum: 0, maximum: 1}
state:
type: string
enum: [disabled, queued, searching, ready, not_found, needs_input, error]
input_hash: {type: string}
candidates:
type: array
items: {$ref: "#/components/schemas/ReleaseCandidate"}
selected_release: {type: string, nullable: true}
UploadBatchPatch:
type: object
description: Пустые поля не меняют файлы. cover_base64 null снимает обложку пачки.