Import missing artist portraits from uploaded album folders
ci / module boundaries / boundaries (push) Successful in 6s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 2m1s
ci / ts / ts (packages + web) (push) Failing after 5m13s
ci / api / api (push) Successful in 41s
ci / db / db (push) Failing after 1m33s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 2m25s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 2m19s

This commit is contained in:
mrleo1nid committed 2026-10-30 10:46:07 +03:00
1 parent fd17c06397
commit 82bf015283
23 files changed
+829 -317

No files matched your search

+4
View File
@@ -2848,6 +2848,10 @@ components:
type: object type: object
required: [files] required: [files]
properties: properties:
artist_image_base64:
type: string
maxLength: 11184812
description: Фото исполнителя JPEG, PNG или WebP, до 8 МБ. После одобрения применяется к исполнителю альбома только при отсутствии аватарки.
cover_base64: cover_base64:
type: string type: string
maxLength: 11184812 maxLength: 11184812
+62
View File
@@ -0,0 +1,62 @@
import { mkdir, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { expect, test } from '@playwright/test';
test('an artist directory uploads each album as a separate batch with a shared portrait', async ({ page }, info) => {
const root = info.outputPath('творожный бизнес');
const albums = ['(2026) творог грехов - творожный бизнес', '(2025) молчанка - творожный бизнес'];
for (const album of albums) {
await mkdir(join(root, album), { recursive: true });
await writeFile(join(root, album, 'song.flac'), Buffer.alloc(128));
}
const portrait = Buffer.from('portrait');
await writeFile(join(root, 'artist.jpg'), portrait);
await writeFile(join(root, albums[0]!, 'cover.jpg'), Buffer.from('album cover'));
type Batch = { artist_image_base64?: string; cover_base64?: string; files: { client_id: string; rel_path: string }[] };
const batches: Batch[] = [];
await page.exposeFunction('recordArtistBatch', (batch: Batch) => { batches.push(batch); });
await page.addInitScript(() => localStorage.setItem('pipu.locale', 'ru'));
await page.goto('/login');
await page.getByLabel('Логин', { exact: true }).fill('mobile-admin');
await page.getByRole('textbox', { name: 'Пароль', exact: true }).fill('mobile-admin-password');
await page.getByRole('button', { name: 'Войти', exact: true }).click();
await expect(page.getByRole('button', { name: 'Меню профиля' })).toBeVisible();
await page.evaluate(() => {
const original = window.fetch;
window.fetch = async (input, init) => {
const req = new Request(input, init);
const path = new URL(req.url).pathname;
const json = (data: unknown) => new Response(JSON.stringify(data), { headers: { 'Content-Type': 'application/json' } });
if (path === '/api/v1/uploads/precheck') {
const body = await req.json();
return json({ files: body.files.map((f: { client_id: string }) => ({ client_id: f.client_id, result: 'accepted' })) });
}
if (path === '/api/v1/uploads' && req.method === 'POST') {
const body = await req.json();
await (window as unknown as { recordArtistBatch: (body: unknown) => Promise<void> }).recordArtistBatch(body);
return json({ batch_id: crypto.randomUUID(), files: body.files.map((f: { client_id: string }) => ({ client_id: f.client_id, upload_file_id: f.client_id, tus_url: `/api/v1/uploads/files/${f.client_id}` })) });
}
return original(input, init);
};
history.pushState({}, '', '/uploads/new');
window.dispatchEvent(new PopStateEvent('popstate'));
});
await page.route('**/api/v1/uploads/files/*', async (route) => {
await route.fulfill({ status: route.request().method() === 'HEAD' ? 200 : 204,
headers: { 'Upload-Length': '128', 'Upload-Offset': route.request().method() === 'HEAD' ? '0' : '128', 'Tus-Resumable': '1.0.0' } });
});
await page.locator('input[webkitdirectory]').setInputFiles(root);
const send = page.getByRole('button', { name: /^Отправить/ });
await expect(send).toBeEnabled();
await expect(page.getByText(/Пропущен/)).toHaveCount(0);
await send.click();
await expect(page.getByText(/Готово: 2 файла/)).toBeVisible();
expect(batches).toHaveLength(2);
expect(batches.map((batch) => batch.files[0]!.rel_path).sort()).toEqual(albums.map((album) => `творожный бизнес/${album}/song.flac`).sort());
for (const batch of batches) {
expect(batch.files).toHaveLength(1);
expect(batch.artist_image_base64).toBe(portrait.toString('base64'));
}
expect(batches.find((batch) => batch.files[0]!.rel_path.includes(albums[0]!))!.cover_base64).toBe(Buffer.from('album cover').toString('base64'));
expect(batches.find((batch) => batch.files[0]!.rel_path.includes(albums[1]!))!.cover_base64).toBeUndefined();
});
+14 -1
View File
@@ -1,6 +1,19 @@
import { describe, expect, it } from 'vitest'; import { describe, expect, it } from 'vitest';
import { needsFormatAttention } from './files'; import { artistImageFor, isArtistImage, needsFormatAttention } from './files';
it('inherits the nearest artist portrait without crossing sibling folders', () => {
const root = new File(['portrait'], 'artist.jpg');
const local = new File(['local'], 'ARTIST.PNG');
const images = new Map([['творожный бизнес', root], ['творожный бизнес/Альбом', local]]);
expect(isArtistImage(root.name)).toBe(true);
expect(isArtistImage(local.name)).toBe(true);
expect(isArtistImage('cover.jpg')).toBe(false);
expect(artistImageFor('творожный бизнес/(2026) творог грехов', images)).toBe(root);
expect(artistImageFor('творожный бизнес/Альбом/CD1', images)).toBe(local);
expect(artistImageFor('другой исполнитель/Альбом', images)).toBeUndefined();
expect(artistImageFor('творожный бизнес 2/Альбом', images)).toBeUndefined();
});
describe('needsFormatAttention', () => { describe('needsFormatAttention', () => {
it('marks everything except lossless formats', () => { it('marks everything except lossless formats', () => {
+14
View File
@@ -61,6 +61,20 @@ export function isAlbumCover(name: string): boolean {
return /^(cover|folder|front)\.(jpe?g|png|webp)$/i.test(name.split(/[/\\]/).pop() ?? name); return /^(cover|folder|front)\.(jpe?g|png|webp)$/i.test(name.split(/[/\\]/).pop() ?? name);
} }
export function isArtistImage(name: string): boolean {
return /^artist\.(jpe?g|png|webp)$/i.test(name.split(/[/\\]/).pop() ?? name);
}
/** The nearest ancestor wins; sibling artist folders must never share portraits. */
export function artistImageFor(folder: string, images: ReadonlyMap<string, File>): File | undefined {
for (;;) {
const image = images.get(folder);
if (image) return image;
if (!folder) return undefined;
folder = albumFolder(folder);
}
}
export function albumFolder(relPath: string): string { export function albumFolder(relPath: string): string {
const path = relPath.replaceAll('\\', '/'); const path = relPath.replaceAll('\\', '/');
const at = path.lastIndexOf('/'); const at = path.lastIndexOf('/');
@@ -52,3 +52,50 @@ it('keeps external and embedded album covers in separate batches without skippin
expect(batches[1]!.files.map((f) => f.rel_path)).toEqual(['B/01.flac', 'B/02.flac']); expect(batches[1]!.files.map((f) => f.rel_path)).toEqual(['B/01.flac', 'B/02.flac']);
expect(uploader.getState().items.every((f) => f.stage === 'done')).toBe(true); expect(uploader.getState().items.every((f) => f.stage === 'done')).toBe(true);
}); });
it('splits an artist discography into album batches and inherits only that artist portrait', async () => {
vi.stubGlobal('Worker', class extends EventTarget {
postMessage({ id }: { id: string }) {
queueMicrotask(() => this.dispatchEvent(new MessageEvent('message', { data: { id, type: 'done', sha256: id.padEnd(64, '0') } })));
}
});
const batches: { artist_image_base64?: string; cover_base64?: string; files: { client_id: string; rel_path: string }[] }[] = [];
const session = new AuthSession({ baseUrl: 'http://localhost', clientType: 'web', fetch: async (input) => {
const req = input as Request;
const body = await req.json();
const data = req.url.endsWith('/precheck')
? { files: body.files.map((f: { client_id: string }) => ({ client_id: f.client_id, result: 'accepted' })) }
: (batches.push(body), { batch_id: String(batches.length), files: body.files.map((f: { client_id: string }) => ({ client_id: f.client_id, upload_file_id: f.client_id, tus_url: `/files/${f.client_id}` })) });
return new Response(JSON.stringify(data), { headers: { 'Content-Type': 'application/json' } });
} });
const uploader = new Uploader(session);
const first = 'творожный бизнес/(2026) творог грехов - творожный бизнес';
const second = 'творожный бизнес/(2025) молчанка - творожный бизнес';
uploader.add([
{ file: new File(['portrait'], 'artist.jpg'), relPath: 'творожный бизнес/artist.jpg' },
{ file: new File(['cover'], 'cover.jpg'), relPath: `${first}/cover.jpg` },
{ file: new File(['a'], '02.flac'), relPath: `${first}/02.flac` },
{ file: new File(['b'], '03.flac'), relPath: `${first}/03.flac` },
{ file: new File(['c'], '02.flac'), relPath: `${second}/02.flac` },
{ file: new File(['d'], '02.flac'), relPath: 'Другой исполнитель/Альбом/02.flac' },
]);
await waitFor(() => expect(uploader.ready).toHaveLength(4));
expect(uploader.getState().skipped).toBe(0);
await uploader.start('Дискография');
expect(batches).toHaveLength(3);
const a = batches.find((batch) => batch.files[0]!.rel_path.startsWith(first))!;
const b = batches.find((batch) => batch.files[0]!.rel_path.startsWith(second))!;
const other = batches.find((batch) => batch.files[0]!.rel_path.startsWith('Другой'))!;
expect(a.files).toHaveLength(2);
expect(b.files).toHaveLength(1);
expect(a.artist_image_base64).toBe(btoa('portrait'));
expect(b.artist_image_base64).toBe(btoa('portrait'));
expect(a.cover_base64).toBe(btoa('cover'));
expect(b.cover_base64).toBeUndefined();
expect(other.artist_image_base64).toBeUndefined();
uploader.clear();
uploader.add([{ file: new File(['new'], '02.flac'), relPath: `${second}/02.flac` }]);
await waitFor(() => expect(uploader.ready).toHaveLength(1));
await uploader.start('New selection');
expect(batches[3]!.artist_image_base64).toBeUndefined();
});
+7 -2
View File
@@ -1,7 +1,7 @@
import { unwrap, type AuthSession } from '@pipu/core'; import { unwrap, type AuthSession } from '@pipu/core';
import * as tus from 'tus-js-client'; import * as tus from 'tus-js-client';
import { albumFolder, coverBase64, isAlbumCover, isAudio, type PickedFile } from './files'; import { albumFolder, artistImageFor, coverBase64, isAlbumCover, isArtistImage, isAudio, type PickedFile } from './files';
import type { HashRequest, HashResponse } from './hash.worker'; import type { HashRequest, HashResponse } from './hash.worker';
// Загрузка пачки в очередь на добавление (docs/uploads.md#5): SHA-256 в воркере → предпроверка дублей // Загрузка пачки в очередь на добавление (docs/uploads.md#5): SHA-256 в воркере → предпроверка дублей
@@ -74,6 +74,7 @@ export class Uploader {
private transferDone: (() => void) | null = null; private transferDone: (() => void) | null = null;
private refreshing: Promise<unknown> | null = null; private refreshing: Promise<unknown> | null = null;
private folderCovers = new Map<string, File>(); private folderCovers = new Map<string, File>();
private artistImages = new Map<string, File>();
constructor(private readonly session: AuthSession) { constructor(private readonly session: AuthSession) {
this.restore(); this.restore();
@@ -104,12 +105,13 @@ export class Uploader {
if (this.state.busy) return; if (this.state.busy) return;
for (const f of files) { for (const f of files) {
if (isAlbumCover(f.file.name)) this.folderCovers.set(albumFolder(f.relPath), f.file); if (isAlbumCover(f.file.name)) this.folderCovers.set(albumFolder(f.relPath), f.file);
if (isArtistImage(f.file.name)) this.artistImages.set(albumFolder(f.relPath), f.file);
} }
const fresh: UploadItem[] = []; const fresh: UploadItem[] = [];
let skipped = 0; let skipped = 0;
for (const f of files) { for (const f of files) {
if (!isAudio(f.file.name)) { if (!isAudio(f.file.name)) {
if (!isAlbumCover(f.file.name)) skipped++; if (!isAlbumCover(f.file.name) && !isArtistImage(f.file.name)) skipped++;
continue; continue;
} }
// Тот же путь и размер уже в списке — не добавляем второй раз. // Тот же путь и размер уже в списке — не добавляем второй раз.
@@ -134,6 +136,7 @@ export class Uploader {
if (this.state.busy) return; if (this.state.busy) return;
this.hashQueue = []; this.hashQueue = [];
this.folderCovers.clear(); this.folderCovers.clear();
this.artistImages.clear();
this.set({ items: [], skipped: 0, batchId: null, error: null }); this.set({ items: [], skipped: 0, batchId: null, error: null });
this.persist(); this.persist();
} }
@@ -236,11 +239,13 @@ export class Uploader {
for (const [folder, group] of groups) { for (const [folder, group] of groups) {
try { try {
const cover = this.folderCovers.get(folder); const cover = this.folderCovers.get(folder);
const artistImage = artistImageFor(folder, this.artistImages);
const res = unwrap( const res = unwrap(
await this.session.client.POST('/api/v1/uploads', { await this.session.client.POST('/api/v1/uploads', {
body: { body: {
comment: comment.trim() || undefined, comment: comment.trim() || undefined,
...(cover ? { cover_base64: await coverBase64(cover) } : {}), ...(cover ? { cover_base64: await coverBase64(cover) } : {}),
...(artistImage ? { artist_image_base64: await coverBase64(artistImage) } : {}),
files: group.map((it) => ({ files: group.map((it) => ({
client_id: it.id, client_id: it.id,
name: it.file?.name ?? it.relPath, name: it.file?.name ?? it.relPath,
+2 -1
View File
@@ -24,7 +24,8 @@ server {
real_ip_recursive on; real_ip_recursive on;
root /usr/share/nginx/html; root /usr/share/nginx/html;
client_max_body_size 16m; # Two images (album cover + artist portrait), each up to 8 MiB before base64.
client_max_body_size 32m;
include /etc/nginx/snippets/security-headers.conf; include /etc/nginx/snippets/security-headers.conf;
+10
View File
@@ -94,6 +94,16 @@
Обложки больше 8 МБ клиент автоматически уменьшает до JPEG с длинной стороной не более 2048 пикселей Обложки больше 8 МБ клиент автоматически уменьшает до JPEG с длинной стороной не более 2048 пикселей
перед base64-кодированием; это относится и к отдельным файлам, и к встроенной обложке, и к правке пачки. перед base64-кодированием; это относится и к отдельным файлам, и к встроенной обложке, и к правке пачки.
Меньшие картинки остаются без изменений. Код HTTP 413 от прокси показывается как превышение размера запроса. Меньшие картинки остаются без изменений. Код HTTP 413 от прокси показывается как превышение размера запроса.
- `artist.jpg` (также `artist.jpeg`, `artist.png`, `artist.webp`, регистр не важен) в папке исполнителя
передаётся отдельно от обложки. Ближайшая такая картинка наследуется вложенными папками альбомов;
соседние исполнители не смешиваются. Например `Исполнитель/artist.jpg` и две папки альбомов
дают две отдельные пачки с одной фотографией исполнителя и своими обложками.
После одобрения и импорта трека фотография сохраняется в кеш WebP и ставится исполнителю альбома,
только если у него нет ни локальной аватарки, ни изображения Genius. Гостям она не назначается.
Существующая аватарка сохраняется, в том числе при одновременном импорте нескольких альбомов.
До одобрения каталог не меняется. Лимит фотографии — 8 МБ после уменьшения, как у обложки;
nginx принимает до 32 МБ, чтобы вместить обе картинки в base64. Временная фотография удаляется
после завершения всех файлов пачки, а готовая аватарка остаётся в кеше.
## 6. Обработка (`processing`) ## 6. Обработка (`processing`)
@@ -2295,6 +2295,8 @@ export interface components {
}[]; }[];
}; };
UploadBatchRequest: { UploadBatchRequest: {
/** @description Фото исполнителя JPEG, PNG или WebP, до 8 МБ. После одобрения применяется к исполнителю альбома только при отсутствии аватарки. */
artist_image_base64?: string;
/** @description Обложка альбома JPEG, PNG или WebP, до 8 МБ после декодирования. Применяется ко всем файлам пачки. */ /** @description Обложка альбома JPEG, PNG или WebP, до 8 МБ после декодирования. Применяется ко всем файлам пачки. */
cover_base64?: string; cover_base64?: string;
comment?: string; comment?: string;
+1
View File
@@ -172,6 +172,7 @@ func newApp(ctx context.Context, cfg config.API, pool *pgxpool.Pool, rdb *redis.
upSvc := uploads.NewService(upStore, libStore, libSvc, scanner, agent, prober, upSvc := uploads.NewService(upStore, libStore, libSvc, scanner, agent, prober,
library.FPCalc{Bin: cfg.FPCalcPath, Timeout: time.Minute}, library.FPCalc{Bin: cfg.FPCalcPath, Timeout: time.Minute},
uploads.Config{StagingDir: cfg.UploadsDir, FFmpeg: cfg.FFmpegPath}, log) uploads.Config{StagingDir: cfg.UploadsDir, FFmpeg: cfg.FFmpegPath}, log)
upSvc.SetArtistImages(library.NewArtistImages(libStore, covers))
if cfg.MusicBrainzEnabled { if cfg.MusicBrainzEnabled {
upSvc.SetReleaseCatalogue(musicbrainz.New(upStore, "PipuMusic/"+buildinfo.Version+" (https://gitea.hsrv.site/mrleo1nid/pipu-music)")) upSvc.SetReleaseCatalogue(musicbrainz.New(upStore, "PipuMusic/"+buildinfo.Version+" (https://gitea.hsrv.site/mrleo1nid/pipu-music)"))
if strings.TrimSpace(cfg.AcoustIDAPIKey) != "" { if strings.TrimSpace(cfg.AcoustIDAPIKey) != "" {
+306 -302
View File
@@ -1762,7 +1762,9 @@ type UploadBatchPatch struct {
// UploadBatchRequest defines model for UploadBatchRequest. // UploadBatchRequest defines model for UploadBatchRequest.
type UploadBatchRequest struct { type UploadBatchRequest struct {
Comment *string `json:"comment,omitempty"` // ArtistImageBase64 Фото исполнителя JPEG, PNG или WebP, до 8 МБ. После одобрения применяется к исполнителю альбома только при отсутствии аватарки.
ArtistImageBase64 *string `json:"artist_image_base64,omitempty"`
Comment *string `json:"comment,omitempty"`
// CoverBase64 Обложка альбома JPEG, PNG или WebP, до 8 МБ после декодирования. Применяется ко всем файлам пачки. // CoverBase64 Обложка альбома JPEG, PNG или WebP, до 8 МБ после декодирования. Применяется ко всем файлам пачки.
CoverBase64 *string `json:"cover_base64,omitempty"` CoverBase64 *string `json:"cover_base64,omitempty"`
@@ -15071,45 +15073,45 @@ func (sh *strictHandler) GetReadyz(w http.ResponseWriter, r *http.Request) {
// Base64 encoded, gzipped, json marshaled Swagger object // Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{ var swaggerSpec = []string{
"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",
@@ -15135,17 +15137,17 @@ var swaggerSpec = []string{
"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",
@@ -15174,7 +15176,7 @@ var swaggerSpec = []string{
"SPxbPAtV5YHje79kfxgrgfHZ5k01FBpBYsCkFmfY4UNWOjhtVfn+Tep2XCPi2JuchT0akw0v9hLqkjgk", "SPxbPAtV5YHje79kfxgrgfHZ5k01FBpBYsCkFmfY4UNWOjhtVfn+Tep2XCPi2JuchT0akw0v9hLqkjgk",
"a06kQosVWHq+Hhwth5wBbtGWrXOoYDVtlSqJ2/yc5ssUvKRdvQICwO53YsLAaigIpi65fMAIwJRJkXIP", "a06kQosVWHq+Hhwth5wBbtGWrXOoYDVtlSqJ2/yc5ssUvKRdvQICwO53YsLAaigIpi65fMAIwJRJkXIP",
"9QUk+1U6Sp+lz7OHbZKOULfVDTOoWgA8OkiuT58yjTk9lEjbe4ZkeZP9sAxJP4ivVwlcMYUGTW1jFnfj", "9QUk+1U6Sp+lz7OHbZKOULfVDTOoWgA8OkiuT58yjTk9lEjbe4ZkeZP9sAxJP4ivVwlcMYUGTW1jFnfj",
"Nl3ltO0oAjS8DjaFLVP4H3IvFZiyolyDmRmYwwnJR8zK2E/32gQKEaBaAWyQcbaV3Qe/LKP2AdYqMGuZ", "Nl3ltO0oAjS8DjaFLVP473MvFZiyolyDmRmYwwnJR8zK2E/32gQKEaBaAWyQcbaV3Qe/LKP2AdYqMGuZ",
"J4aCxXEIHsTnYIh80UT6HcshYnbXfQrWyz7jkKP0MbQ+fsJb28BCRqojmltp6TM8AU3RSaowRuyAHY3R", "J4aCxXEIHsTnYIh80UT6HcshYnbXfQrWyz7jkKP0MbQ+fsJb28BCRqojmltp6TM8AU3RSaowRuyAHY3R",
"N4zWwCwCpVUAIJzEZZaqdyst08D9sWLDNq7pngAGVeSEKCHgCw1SaqvQSs1kjTZodAUgxwyxnyCJvEnA", "N4zWwCwCpVUAIJzEZZaqdyst08D9sWLDNq7pngAGVeSEKCHgCw1SaqvQSs1kjTZodAUgxwyxnyCJvEnA",
"Z5S3WfHoIwr3SKcqvd8YLrQsWUxSC+SVBqlbvKXvA/dodzYcf2jx3Kyt0S7TG83fBhuNhI8sNSgDDVCz", "Z5S3WfHoIwr3SKcqvd8YLrQsWUxSC+SVBqlbvKXvA/dodzYcf2jx3Kyt0S7TG83fBhuNhI8sNSgDDVCz",
@@ -15182,11 +15184,11 @@ var swaggerSpec = []string{
"1234khmOt4Lv2i0YotIVXH/L1MTsJAfzsezLsLgTBl7H2JNrw9iQa1XA/2vH0ahA7WDgbgevagUqHTuS", "1234khmOt4Lv2i0YotIVXH/L1MTsJAfzsezLsLgTBl7H2JNrw9iQa1XA/2vH0ahA7WDgbgevagUqHTuS",
"wD1/T5Rv7GWfZ1/xmxu9zXvZ3ewzpjbJzGORjKwVLCrRNlTUCp3lLNWJfS/ocJz9TrGVUCkUCXNOD3gZ", "wD1/T5Rv7GWfZ1/xmxu9zXvZ3ewzpjbJzGORjKwVLCrRNlTUCp3lLNWJfS/ocJz9TrGVUCkUCXNOD3gZ",
"GQYk+Y+uhC4l3KN8lscfn3NnKrhe2xC65As9kq5QduuDI3QsK3LU2lbwK/GdOLe4+PrrxnZvPHUi37OB", "GQYk+Y+uhC4l3KN8lscfn3NnKrhe2xC65As9kq5QduuDI3QsK3LU2lbwK/GdOLe4+PrrxnZvPHUi37OB",
"Nxie7Q9jr2ss7BdtzTpY2hhrP2b6O5wVxkLNa/QU3rG1toMsMLVul5xZpv2/pdGcuszW4vy5+cXa4B/P", "Nxie7Q9jr2ss7BdtzTpY2hhrP2b6O5wVxkLNa/QU3rG1toMsMLVul5xZpv2/odGcuszW4vy5+cXa4B/P",
"yMj7OakMbFpjxV4b2dl+gP46XDXYELrhbbnra+Fg0MwRrkGzBeqH6zZwWcW6NGLPajI5t9PiYbdLKRpa", "yMj7OakMbFpjxV4b2dl+gP4qXDXYELrhbbnra+Fg0MwRrkGzBeqH6zZwWcW6NGLPajI5t9PiYbdLKRpa",
"vEHiDWs7nIbONA7igqNW2oD8tbgwO9mX5aViSjWv62lhSo0z5tDxMlMt2e7CTyfJErI1VFOXYQJAnRSx", "vEHiDWs7nIbONA7igqNW2oD8tbgwO9mX5aViSjWv62lhSo0z5tDxMlMt2e7CTyfJErI1VFOXYQJAnRSx",
"T6VKIR2vRtcWQ9lniQlIdsiGnhMEmMkvY7VOQsHNlx/CvuMFRkaitweoxXfDft9LlAqh1sXWf/475+za", "T6VKIR2vRtcWQ9lniQlIdsiGnhMEmMkvY7VOQsHNlx/CvuMFRkaitweoxXfDft9LlAqh1sXWf/lb5+za",
"4tnXb3xyYbH92oU7f9Gq66Q7QcM4IyHQXynWVEeU2N45Jl9O2e/OH6nqZfcLFCzNUr5yWWQG/cCV2VFK", "4tnXb3xyYbH92oU7f96q66Q7QcM4IyHQXynWVEeU2N45Jl9O2e/OH6nqZfcLFCzNUr5yWWQG/cCV2VFK",
"T8puJT87gvdVkIA/UUUBFH+dkqiSLr6WpCQT+T0K/bKGAXRlhn+pT8bUp918wBv2xqITFaRbDpPysvJ2", "T8puJT87gvdVkIA/UUUBFH+dkqiSLr6WpCQT+T0K/bKGAXRlhn+pT8bUp918wBv2xqITFaRbDpPysvJ2",
"4+YWtqVdZJwSjYpkLVDIzKpxbIYQOC7Mip4nU7zdIU5+AAXL3BkzUpOxK32bYitNwes9AL1AzWmcPtXV", "4+YWtqVdZJwSjYpkLVDIzKpxbIYQOC7Mip4nU7zdIU5+AAXL3BkzUpOxK32bYitNwes9AL1AzWmcPtXV",
"p2dMk4Dsp3SUHmrpTkY1r4AUMzXAEydOOsP4xOml6vR0GJhC+qg2YE6/Cj6YRqa0YKnjZ0gvh13P8ZtD", "p2dMk4Dsp3SUHmrpTkY1r4AUMzXAEydOOsP4xOml6vR0GJhC+qg2YE6/Cj6YRqa0YKnjZ0gvh13P8ZtD",
@@ -15201,276 +15203,278 @@ var swaggerSpec = []string{
"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",
"AAAA//+rgjPrSAYCAA==",
} }
// GetSwagger returns the content of the embedded swagger specification file // GetSwagger returns the content of the embedded swagger specification file
@@ -0,0 +1,77 @@
//go:build integration
package dbtest
import (
"context"
"log/slog"
"os"
"path/filepath"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"pipu/api/internal/library"
"pipu/api/internal/library/meta"
"pipu/api/internal/platform/db"
)
type portraitProber struct{ artist string }
func (p portraitProber) Probe(context.Context, string) (library.Probe, error) {
return library.Probe{Format: "flac", Codec: "flac", DurationMs: 180000,
Tags: meta.Fields{Artist: p.artist + ", Guest " + p.artist, AlbumArtist: p.artist, Album: "Portrait album", Title: "Song", Track: "1"}}, nil
}
func TestArtistImagesPreserveLocalExternalAndConcurrentPortraits(t *testing.T) {
pool := connect(t)
ctx := context.Background()
require.NoError(t, db.Migrate(ctx, pool))
store := library.NewStore(pool)
source, err := store.SaveSource(ctx, library.Source{ID: uuid.New(), Name: "portraits", Type: library.SourceLocal, Status: "mounted", TagsFirst: true, NFSOptions: "ro"})
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, store.DeleteSource(ctx, source.ID))
require.NoError(t, store.CleanupOrphans(ctx))
})
root := t.TempDir()
for _, scenario := range []string{"empty", "local", "external", "concurrent"} {
t.Run(scenario, func(t *testing.T) {
artist := "Portrait " + uuid.NewString()
scanner := library.NewScanner(store, portraitProber{artist}, library.NewProgressBoard(), slog.New(slog.DiscardHandler))
rel := scenario + ".flac"
require.NoError(t, os.WriteFile(filepath.Join(root, rel), []byte(scenario), 0o644))
file, err := scanner.ScanFile(ctx, source, root, rel)
require.NoError(t, err)
id, err := store.ArtistWithoutImageForFile(ctx, file)
require.NoError(t, err)
if scenario == "local" {
require.NoError(t, store.SetArtistImageIfMissing(ctx, id, "existing-local"))
}
if scenario == "external" || scenario == "concurrent" {
_, err = pool.Exec(ctx, `UPDATE artists SET genius_image_url='https://example.org/portrait.jpg' WHERE id=$1`, id)
require.NoError(t, err)
}
if scenario != "empty" {
_, err = store.ArtistWithoutImageForFile(ctx, file)
require.ErrorIs(t, err, library.ErrNotFound)
}
// Even a caller that looked up the artist before another writer cannot overwrite it.
require.NoError(t, store.SetArtistImageIfMissing(ctx, id, "uploaded-portrait"))
a, err := store.Artist(ctx, id)
require.NoError(t, err)
switch scenario {
case "empty":
require.Equal(t, "uploaded-portrait", *a.ImagePath)
case "local":
require.Equal(t, "existing-local", *a.ImagePath)
default:
require.Nil(t, a.ImagePath)
require.Equal(t, "https://example.org/portrait.jpg", *a.ImageURL)
}
var guestImage *string
require.NoError(t, pool.QueryRow(ctx, `SELECT image_path FROM artists WHERE name=$1`, "Guest "+artist).Scan(&guestImage))
require.Nil(t, guestImage, "a parent folder portrait belongs to the album artist, not guests")
})
}
}
@@ -0,0 +1,74 @@
package library
import (
"context"
"crypto/sha256"
"encoding/hex"
"errors"
"os"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
)
type artistImageStore interface {
ArtistWithoutImageForFile(context.Context, uuid.UUID) (uuid.UUID, error)
SetArtistImageIfMissing(context.Context, uuid.UUID, string) error
}
type imageRenderer interface {
StoreImage(context.Context, []byte) (string, error)
}
// ArtistImages applies approved portraits to the album artist, never guests.
type ArtistImages struct {
store artistImageStore
images imageRenderer
}
func NewArtistImages(store *Store, covers *Covers) *ArtistImages {
return &ArtistImages{store: store, images: covers}
}
func (a *ArtistImages) ApplyArtistImage(ctx context.Context, file uuid.UUID, data []byte) error {
id, err := a.store.ArtistWithoutImageForFile(ctx, file)
if errors.Is(err, ErrNotFound) {
return nil
}
if err != nil {
return err
}
key, err := a.images.StoreImage(ctx, data)
if err != nil {
return err
}
return a.store.SetArtistImageIfMissing(ctx, id, key)
}
func (c *Covers) StoreImage(ctx context.Context, data []byte) (string, error) {
if err := os.MkdirAll(c.Dir, 0o755); err != nil {
return "", err
}
sum := sha256.Sum256(data)
key := hex.EncodeToString(sum[:8])
_, err := c.render(ctx, data, key)
return key, err
}
func (st *Store) ArtistWithoutImageForFile(ctx context.Context, file uuid.UUID) (uuid.UUID, error) {
var id uuid.UUID
err := st.pool.QueryRow(ctx, `SELECT a.id FROM media_files f JOIN tracks t ON t.id=f.track_id
JOIN albums al ON al.id=t.album_id JOIN artists a ON a.id=al.album_artist_id
WHERE f.id=$1 AND a.image_path IS NULL AND a.genius_image_url IS NULL`, file).Scan(&id)
if errors.Is(err, pgx.ErrNoRows) {
return id, ErrNotFound
}
return id, err
}
func (st *Store) SetArtistImageIfMissing(ctx context.Context, id uuid.UUID, key string) error {
// Another import or Genius may have supplied a portrait during rendering.
_, err := st.pool.Exec(ctx, `UPDATE artists SET image_path=$2 WHERE id=$1
AND image_path IS NULL AND genius_image_url IS NULL`, id, key)
return err
}
@@ -0,0 +1,69 @@
package library
import (
"context"
"errors"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
)
type portraitStore struct {
id uuid.UUID
err error
saved uuid.UUID
key string
}
func (s *portraitStore) ArtistWithoutImageForFile(context.Context, uuid.UUID) (uuid.UUID, error) {
return s.id, s.err
}
func (s *portraitStore) SetArtistImageIfMissing(_ context.Context, id uuid.UUID, key string) error {
s.saved, s.key = id, key
return nil
}
type portraitRenderer struct {
data []byte
err error
}
func (r *portraitRenderer) StoreImage(_ context.Context, data []byte) (string, error) {
r.data = data
return "portrait-key", r.err
}
func TestArtistPortraitOnlyRendersForAnArtistWithoutImage(t *testing.T) {
for _, scenario := range []string{"new portrait", "already has image", "lookup failure", "render failure"} {
t.Run(scenario, func(t *testing.T) {
st := &portraitStore{id: uuid.New()}
renderer := &portraitRenderer{}
switch scenario {
case "already has image":
st.err = ErrNotFound
case "lookup failure":
st.err = errors.New("database unavailable")
case "render failure":
renderer.err = errors.New("invalid image")
}
images := &ArtistImages{store: st, images: renderer}
err := images.ApplyArtistImage(context.Background(), uuid.New(), []byte("portrait"))
if scenario == "lookup failure" || scenario == "render failure" {
require.Error(t, err)
} else {
require.NoError(t, err)
}
if scenario == "new portrait" {
require.Equal(t, st.id, st.saved)
require.Equal(t, "portrait-key", st.key)
require.Equal(t, []byte("portrait"), renderer.data)
} else {
require.Equal(t, uuid.Nil, st.saved)
}
if scenario == "already has image" || scenario == "lookup failure" {
require.Nil(t, renderer.data)
}
})
}
}
+3 -2
View File
@@ -49,6 +49,7 @@ type TrackRow struct {
// ArtistRow — исполнитель со счётчиками. // ArtistRow — исполнитель со счётчиками.
type ArtistRow struct { type ArtistRow struct {
ImageURL *string ImageURL *string
ImagePath *string
ID uuid.UUID ID uuid.UUID
Name string Name string
AlbumCount int AlbumCount int
@@ -196,10 +197,10 @@ func (st *Store) ArtistAlbums(ctx context.Context, artistID uuid.UUID) ([]AlbumR
func (st *Store) Artist(ctx context.Context, id uuid.UUID) (ArtistRow, error) { func (st *Store) Artist(ctx context.Context, id uuid.UUID) (ArtistRow, error) {
var a ArtistRow var a ArtistRow
err := st.pool.QueryRow(ctx, ` err := st.pool.QueryRow(ctx, `
SELECT a.id, a.name, a.genius_image_url, SELECT a.id, a.name, a.genius_image_url, a.image_path,
(SELECT count(*) FROM albums al WHERE al.album_artist_id = a.id), (SELECT count(*) FROM albums al WHERE al.album_artist_id = a.id),
(SELECT count(DISTINCT ta.track_id) FROM track_artists ta WHERE ta.artist_id = a.id) (SELECT count(DISTINCT ta.track_id) FROM track_artists ta WHERE ta.artist_id = a.id)
FROM artists a WHERE a.id = $1`, id).Scan(&a.ID, &a.Name, &a.ImageURL, &a.AlbumCount, &a.TrackCount) FROM artists a WHERE a.id = $1`, id).Scan(&a.ID, &a.Name, &a.ImageURL, &a.ImagePath, &a.AlbumCount, &a.TrackCount)
if errors.Is(err, pgx.ErrNoRows) { if errors.Is(err, pgx.ErrNoRows) {
return ArtistRow{}, ErrNotFound return ArtistRow{}, ErrNotFound
} }
+5 -1
View File
@@ -485,7 +485,11 @@ func (h *Handler) GetArtist(ctx context.Context, req gen.GetArtistRequestObject)
if err != nil { if err != nil {
return nil, catalogErr(err) return nil, catalogErr(err)
} }
return gen.GetArtist200JSONResponse{Id: a.ID, Name: a.Name, ImageUrl: a.ImageURL, AlbumCount: a.AlbumCount, TrackCount: a.TrackCount}, nil image := a.ImageURL
if a.ImagePath != nil {
image = h.coverURL(AlbumRow{ID: a.ID, CoverPath: a.ImagePath})
}
return gen.GetArtist200JSONResponse{Id: a.ID, Name: a.Name, ImageUrl: image, AlbumCount: a.AlbumCount, TrackCount: a.TrackCount}, nil
} }
// ListArtistAlbums реализует gen.StrictServerInterface. // ListArtistAlbums реализует gen.StrictServerInterface.
@@ -0,0 +1,64 @@
//go:build integration
package uploads
import (
"context"
"crypto/sha256"
"encoding/hex"
"log/slog"
"os"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"pipu/api/internal/auth"
"pipu/api/internal/library"
"pipu/api/internal/platform/db"
)
func TestUploadedArtistPortraitStagingLifetime(t *testing.T) {
address := os.Getenv("TEST_DATABASE_URL")
if address == "" {
t.Skip("TEST_DATABASE_URL is not set")
}
ctx := context.Background()
pool, err := db.Connect(ctx, address)
require.NoError(t, err)
defer pool.Close()
require.NoError(t, db.Migrate(ctx, pool))
store := NewStore(pool)
author := uuid.New()
_, err = pool.Exec(ctx, `INSERT INTO users(id,username,password_hash,role) VALUES($1,$2,'x','admin')`, author, "portrait-"+author.String())
require.NoError(t, err)
t.Cleanup(func() {
_, _ = pool.Exec(ctx, `DELETE FROM upload_batches WHERE author_id=$1`, author)
_, _ = pool.Exec(ctx, `DELETE FROM users WHERE id=$1`, author)
_ = store.ReconcileQuota(ctx)
})
svc := NewService(store, nil, nil, nil, nil, nil, library.FPCalc{}, Config{StagingDir: t.TempDir()}, slog.New(slog.DiscardHandler))
owner := auth.WithPrincipal(ctx, auth.Principal{UserID: author, Role: auth.RoleAdmin})
files := []CreateFile{}
for _, name := range []string{"one.flac", "two.flac"} {
sum := sha256.Sum256([]byte(uuid.NewString()))
files = append(files, CreateFile{ClientID: name, Name: name, Size: 10, SHA256: hex.EncodeToString(sum[:])})
}
batch, reserved, err := svc.Create(owner, nil, files, nil, []byte{0xff, 0xd8, 0xff, 1})
require.NoError(t, err)
require.Len(t, reserved, 2)
first, err := store.File(ctx, reserved[0].FileID)
require.NoError(t, err)
require.NotNil(t, first.ArtistImagePath)
image := svc.stagingAbs(*first.ArtistImagePath)
require.FileExists(t, image)
inUse, err := store.StagingInUse(ctx)
require.NoError(t, err)
require.True(t, inUse[*first.ArtistImagePath], "restart reconciliation must retain pending portraits")
require.NoError(t, svc.Withdraw(owner, first.ID))
require.FileExists(t, image, "the second file still owns this portrait")
require.NoError(t, svc.Withdraw(owner, reserved[1].FileID))
require.NoFileExists(t, image)
unused, err := store.UnusedArtistImage(ctx, batch)
require.NoError(t, err)
require.Equal(t, first.ArtistImagePath, unused)
}
+9 -1
View File
@@ -165,7 +165,15 @@ func (h *Handler) CreateUploadBatch(ctx context.Context, req gen.CreateUploadBat
return nil, err return nil, err
} }
} }
batch, res, err := h.svc.Create(ctx, req.Body.Comment, in, cover) var artistImage []byte
if req.Body.ArtistImageBase64 != nil {
var err error
artistImage, err = DecodeCover(*req.Body.ArtistImageBase64)
if err != nil {
return nil, err
}
}
batch, res, err := h.svc.Create(ctx, req.Body.Comment, in, cover, artistImage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
+1
View File
@@ -148,6 +148,7 @@ type File struct {
ReviewedAt *time.Time ReviewedAt *time.Time
ImportedFileID *uuid.UUID ImportedFileID *uuid.UUID
CoverPath *string CoverPath *string
ArtistImagePath *string
CreatedAt time.Time CreatedAt time.Time
UpdatedAt time.Time UpdatedAt time.Time
} }
+22 -2
View File
@@ -45,8 +45,15 @@ type Service struct {
catalogue ReleaseCatalogue catalogue ReleaseCatalogue
identifier AudioIdentifier identifier AudioIdentifier
encodedFingerprint func(context.Context, string) (string, error) encodedFingerprint func(context.Context, string) (string, error)
artistImages ArtistImages
} }
type ArtistImages interface {
ApplyArtistImage(context.Context, uuid.UUID, []byte) error
}
func (s *Service) SetArtistImages(images ArtistImages) { s.artistImages = images }
type ReleaseCatalogue interface { type ReleaseCatalogue interface {
Search(context.Context, musicbrainz.Query) ([]musicbrainz.Release, error) Search(context.Context, musicbrainz.Query) ([]musicbrainz.Release, error)
Cover(context.Context, musicbrainz.Release) (musicbrainz.Cover, error) Cover(context.Context, musicbrainz.Release) (musicbrainz.Cover, error)
@@ -199,7 +206,7 @@ type CreateFile struct {
} }
// Create создаёт пачку и резервирует место; не поместившиеся и дубли отклоняются с кодом. // Create создаёт пачку и резервирует место; не поместившиеся и дубли отклоняются с кодом.
func (s *Service) Create(ctx context.Context, comment *string, files []CreateFile, cover []byte) (uuid.UUID, []Reserved, error) { func (s *Service) Create(ctx context.Context, comment *string, files []CreateFile, cover []byte, artistImages ...[]byte) (uuid.UUID, []Reserved, error) {
p, err := principal(ctx, auth.PermSuggestTracks) p, err := principal(ctx, auth.PermSuggestTracks)
if err != nil { if err != nil {
return uuid.Nil, nil, err return uuid.Nil, nil, err
@@ -235,12 +242,22 @@ func (s *Service) Create(ctx context.Context, comment *string, files []CreateFil
return uuid.Nil, nil, err return uuid.Nil, nil, err
} }
keepCover := false keepCover := false
var artistImagePath *string
defer func() { defer func() {
if coverPath != nil && !keepCover { if coverPath != nil && !keepCover {
_ = os.Remove(s.stagingAbs(*coverPath)) _ = os.Remove(s.stagingAbs(*coverPath))
} }
if artistImagePath != nil && !keepCover {
_ = os.Remove(s.stagingAbs(*artistImagePath))
}
}() }()
batch, res, err := s.store.CreateBatch(ctx, p.UserID, comment, accepted, st, coverPath) if len(artistImages) > 0 {
artistImagePath, err = s.createCover(artistImages[0])
if err != nil {
return uuid.Nil, nil, err
}
}
batch, res, err := s.store.CreateBatch(ctx, p.UserID, comment, accepted, st, coverPath, artistImagePath)
if err != nil { if err != nil {
return uuid.Nil, nil, err return uuid.Nil, nil, err
} }
@@ -609,6 +626,9 @@ func (s *Service) removeStaging(ctx context.Context, f File) {
if cover, err := s.store.UnusedBatchCover(ctx, f.BatchID); err == nil && cover != nil { if cover, err := s.store.UnusedBatchCover(ctx, f.BatchID); err == nil && cover != nil {
_ = os.Remove(s.stagingAbs(*cover)) _ = os.Remove(s.stagingAbs(*cover))
} }
if image, err := s.store.UnusedArtistImage(ctx, f.BatchID); err == nil && image != nil {
_ = os.Remove(s.stagingAbs(*image))
}
} }
} }
+22 -5
View File
@@ -98,7 +98,7 @@ type Reserved struct {
// CreateBatch создаёт пачку и резервирует место под файлы: проверка «занято + запрошено ≤ лимит» // CreateBatch создаёт пачку и резервирует место под файлы: проверка «занято + запрошено ≤ лимит»
// под блокировкой строки счётчика (docs/uploads.md#3). Дубли по SHA-256 отклоняются, не занимая места. // под блокировкой строки счётчика (docs/uploads.md#3). Дубли по SHA-256 отклоняются, не занимая места.
func (st *Store) CreateBatch(ctx context.Context, author uuid.UUID, comment *string, files []NewFile, s Settings, coverPath *string) (uuid.UUID, []Reserved, error) { func (st *Store) CreateBatch(ctx context.Context, author uuid.UUID, comment *string, files []NewFile, s Settings, coverPath *string, artistImages ...*string) (uuid.UUID, []Reserved, error) {
tx, err := st.pool.Begin(ctx) tx, err := st.pool.Begin(ctx)
if err != nil { if err != nil {
return uuid.Nil, nil, fmt.Errorf("uploads: create batch: %w", err) return uuid.Nil, nil, fmt.Errorf("uploads: create batch: %w", err)
@@ -116,8 +116,12 @@ func (st *Store) CreateBatch(ctx context.Context, author uuid.UUID, comment *str
} }
batchID := newID() batchID := newID()
if _, err := tx.Exec(ctx, `INSERT INTO upload_batches (id, author_id, comment, cover_path) VALUES ($1, $2, $3, $4)`, var artistImage *string
batchID, author, comment, coverPath); err != nil { if len(artistImages) > 0 {
artistImage = artistImages[0]
}
if _, err := tx.Exec(ctx, `INSERT INTO upload_batches (id, author_id, comment, cover_path, artist_image_path) VALUES ($1, $2, $3, $4, $5)`,
batchID, author, comment, coverPath, artistImage); err != nil {
return uuid.Nil, nil, fmt.Errorf("uploads: insert batch: %w", err) return uuid.Nil, nil, fmt.Errorf("uploads: insert batch: %w", err)
} }
out := make([]Reserved, 0, len(files)) out := make([]Reserved, 0, len(files))
@@ -231,7 +235,7 @@ const fileColumns = `f.id, f.batch_id, f.author_id, u.username, b.comment, f.sta
f.size_bytes, f.received_bytes, f.sha256, coalesce(f.staging_path, ''), f.format, f.codec, f.bitrate, f.sample_rate, f.size_bytes, f.received_bytes, f.sha256, coalesce(f.staging_path, ''), f.format, f.codec, f.bitrate, f.sample_rate,
f.bit_depth, f.duration_ms, f.tags, f.metadata, f.metadata_edited, f.matched_artist_id, f.matched_album_id, f.bit_depth, f.duration_ms, f.tags, f.metadata, f.metadata_edited, f.matched_artist_id, f.matched_album_id,
f.duplicate_of_track_id, f.duplicate_level, f.possible_duplicate, f.quality_upgrade, f.target_source_id, f.target_rel_path, f.duplicate_of_track_id, f.duplicate_level, f.possible_duplicate, f.quality_upgrade, f.target_source_id, f.target_rel_path,
f.status_reason, f.reviewed_by, f.reviewed_at, f.imported_file_id, CASE WHEN f.release_group_id IS NULL THEN b.cover_path ELSE rg.cover_path END, f.created_at, f.updated_at, src.name` f.status_reason, f.reviewed_by, f.reviewed_at, f.imported_file_id, CASE WHEN f.release_group_id IS NULL THEN b.cover_path ELSE rg.cover_path END, f.created_at, f.updated_at, src.name, b.artist_image_path`
const fileFrom = ` FROM upload_files f JOIN upload_batches b ON b.id = f.batch_id JOIN users u ON u.id = f.author_id const fileFrom = ` FROM upload_files f JOIN upload_batches b ON b.id = f.batch_id JOIN users u ON u.id = f.author_id
LEFT JOIN upload_release_groups rg ON rg.id=f.release_group_id LEFT JOIN upload_release_groups rg ON rg.id=f.release_group_id
@@ -246,7 +250,7 @@ func scanFile(row pgx.Row) (File, error) {
&f.ClientRelPath, &f.Size, &f.Received, &f.SHA256, &f.StagingPath, &f.Format, &f.Codec, &f.Bitrate, &f.SampleRate, &f.ClientRelPath, &f.Size, &f.Received, &f.SHA256, &f.StagingPath, &f.Format, &f.Codec, &f.Bitrate, &f.SampleRate,
&f.BitDepth, &f.DurationMs, &tags, &meta, &f.MetadataEdited, &f.MatchedArtistID, &f.MatchedAlbumID, &f.BitDepth, &f.DurationMs, &tags, &meta, &f.MetadataEdited, &f.MatchedArtistID, &f.MatchedAlbumID,
&f.DuplicateOfTrackID, &f.DuplicateLevel, &f.PossibleDuplicate, &f.QualityUpgrade, &f.TargetSourceID, &f.TargetRelPath, &f.DuplicateOfTrackID, &f.DuplicateLevel, &f.PossibleDuplicate, &f.QualityUpgrade, &f.TargetSourceID, &f.TargetRelPath,
&f.StatusReason, &f.ReviewedBy, &f.ReviewedAt, &f.ImportedFileID, &f.CoverPath, &f.CreatedAt, &f.UpdatedAt, &f.TargetSourceName) &f.StatusReason, &f.ReviewedBy, &f.ReviewedAt, &f.ImportedFileID, &f.CoverPath, &f.CreatedAt, &f.UpdatedAt, &f.TargetSourceName, &f.ArtistImagePath)
if errors.Is(err, pgx.ErrNoRows) { if errors.Is(err, pgx.ErrNoRows) {
return File{}, ErrNotFound return File{}, ErrNotFound
} }
@@ -353,6 +357,8 @@ func (st *Store) StagingInUse(ctx context.Context) (map[string]bool, error) {
rows, err := st.pool.Query(ctx, `SELECT staging_path FROM upload_files WHERE status::text = ANY($1) AND staging_path IS NOT NULL rows, err := st.pool.Query(ctx, `SELECT staging_path FROM upload_files WHERE status::text = ANY($1) AND staging_path IS NOT NULL
UNION SELECT b.cover_path FROM upload_batches b JOIN upload_files f ON f.batch_id = b.id UNION SELECT b.cover_path FROM upload_batches b JOIN upload_files f ON f.batch_id = b.id
WHERE f.status::text = ANY($1) AND b.cover_path IS NOT NULL WHERE f.status::text = ANY($1) AND b.cover_path IS NOT NULL
UNION SELECT b.artist_image_path FROM upload_batches b JOIN upload_files f ON f.batch_id = b.id
WHERE f.status::text = ANY($1) AND b.artist_image_path IS NOT NULL
UNION SELECT g.cover_path FROM upload_release_groups g JOIN upload_files f ON f.release_group_id=g.id UNION SELECT g.cover_path FROM upload_release_groups g JOIN upload_files f ON f.release_group_id=g.id
WHERE f.status::text = ANY($1) AND g.cover_path IS NOT NULL`, WHERE f.status::text = ANY($1) AND g.cover_path IS NOT NULL`,
occupyingStatuses) occupyingStatuses)
@@ -381,6 +387,17 @@ func (st *Store) UnusedBatchCover(ctx context.Context, batch uuid.UUID) (*string
return cover, err return cover, err
} }
// UnusedArtistImage releases a staged portrait only after every batch file is terminal.
func (st *Store) UnusedArtistImage(ctx context.Context, batch uuid.UUID) (*string, error) {
var image *string
err := st.pool.QueryRow(ctx, `SELECT b.artist_image_path FROM upload_batches b WHERE b.id=$1
AND NOT EXISTS (SELECT 1 FROM upload_files f WHERE f.batch_id=b.id AND f.status::text=ANY($2))`, batch, occupyingStatuses).Scan(&image)
if errors.Is(err, pgx.ErrNoRows) {
return nil, nil
}
return image, err
}
// Stale — недокачанные файлы старше before. // Stale — недокачанные файлы старше before.
func (st *Store) Stale(ctx context.Context, before time.Time) ([]uuid.UUID, error) { func (st *Store) Stale(ctx context.Context, before time.Time) ([]uuid.UUID, error) {
rows, err := st.pool.Query(ctx, `SELECT id FROM upload_files WHERE status = 'uploading' AND updated_at < $1`, before) rows, err := st.pool.Query(ctx, `SELECT id FROM upload_files WHERE status = 'uploading' AND updated_at < $1`, before)
+9
View File
@@ -334,6 +334,15 @@ func (s *Service) importFile(ctx context.Context, f File) error {
if err != nil { if err != nil {
return fmt.Errorf("scan: %w", err) return fmt.Errorf("scan: %w", err)
} }
if f.ArtistImagePath != nil && s.artistImages != nil {
image, err := os.ReadFile(s.stagingAbs(*f.ArtistImagePath))
if err != nil {
return fmt.Errorf("read artist image: %w", err)
}
if err := s.artistImages.ApplyArtistImage(ctx, fileID, image); err != nil {
return fmt.Errorf("artist image: %w", err)
}
}
if err := s.store.CompleteImport(ctx, f.ID, pl.src.ID, rel, fileID); err != nil { if err := s.store.CompleteImport(ctx, f.ID, pl.src.ID, rel, fileID); err != nil {
return err return err
} }
@@ -0,0 +1,5 @@
-- +goose Up
ALTER TABLE upload_batches ADD COLUMN artist_image_path text;
-- +goose Down
ALTER TABLE upload_batches DROP COLUMN artist_image_path;