fix(uploads): load audio previews only on play
ci / module boundaries / boundaries (push) Successful in 7s
ci / ts / ts (packages + web) (push) Failing after 6m33s

This commit is contained in:
mrleo1nid committed 2026-10-29 11:45:09 +03:00
1 parent af06cf1e75
commit ca2a50b515
3 files changed
+24 -6

No files matched your search

+20 -4
View File
@@ -180,16 +180,31 @@ test('queue shows the current full destination after saving a changed source tem
await expect(page.getByText('Artist/Album/Song.flac', { exact: true })).toHaveCount(0);
});
test('queue preview loads duration before play and plays signed audio', async ({ page }) => {
test('queue preview only requests audio after play', async ({ page }) => {
const wav = previewWav();
await page.route('**/api/v1/admin/uploads/files/*/stream?*', (route) => route.fulfill({ contentType: 'audio/wav', body: wav }));
let requested = 0;
let otherRequested = 0;
await page.route('**/api/v1/admin/uploads/files/*/stream?*', (route) => {
requested++;
return route.fulfill({ contentType: 'audio/wav', body: wav });
});
await page.route('**/api/v1/stream/mobile-preview', (route) => {
otherRequested++;
return route.fulfill({ contentType: 'audio/wav', body: wav });
});
await setup(page, 'queue');
await page.getByRole('button', { name: 'Развернуть пачку', exact: true }).click();
const audio = page.locator('audio').first();
await expect(audio).toHaveAttribute('preload', 'metadata');
await expect.poll(() => audio.evaluate((el: HTMLAudioElement) => el.duration)).toBe(2);
await expect(audio).toHaveAttribute('preload', 'none');
await page.getByRole('button', { name: 'Свернуть пачку', exact: true }).click();
await page.getByRole('button', { name: 'Развернуть пачку', exact: true }).click();
expect(requested).toBe(0);
expect(otherRequested).toBe(0);
await audio.evaluate((el: HTMLAudioElement) => el.play());
await expect.poll(() => audio.evaluate((el: HTMLAudioElement) => el.duration)).toBe(2);
await expect.poll(() => audio.evaluate((el: HTMLAudioElement) => el.currentTime)).toBeGreaterThan(0);
expect(requested).toBeGreaterThan(0);
expect(otherRequested).toBe(0);
expect(await audio.evaluate((el: HTMLAudioElement) => el.error)).toBeNull();
});
@@ -210,6 +225,7 @@ test('queue preview explains HTTP failures and recovers on retry', async ({ page
});
await page.getByRole('button', { name: 'Развернуть пачку', exact: true }).click();
const preview = page.locator('audio').first().locator('..');
await preview.locator('audio').evaluate((el: HTMLAudioElement) => el.play().catch(() => undefined));
await expect(preview.getByRole('alert')).toHaveText('Не удалось загрузить аудио: HTTP 403.');
available = true;
await preview.getByRole('button', { name: 'Повторить', exact: true }).click();
@@ -45,10 +45,11 @@ export function AudioPreview({ url, name, active }: { url: string; name: string;
setHttpStatus(null);
void queryClient.invalidateQueries({ queryKey: ['admin-uploads'] });
audio.current?.load();
void audio.current?.play().catch(() => { /* Native media errors are shown below. */ });
};
return <div className={u.preview}>
<audio ref={audio} controls preload={active ? 'metadata' : 'none'} src={url}
<audio ref={audio} controls preload="none" src={url}
aria-label={t('uploads.listen', { name })}
onPlay={(event) => {
if (event.currentTarget.paused) return;
+2 -1
View File
@@ -165,7 +165,8 @@
Если рассчитать путь нельзя, показаны причина и действие для исправления вместо старого пути.
- Прослушивание staging выдаёт сам API с поддержкой Range: приватные файлы tus (`0640`, каталоги `0750`)
недоступны пользователю nginx. Выдача не зависит от `X_ACCEL`; права существующих файлов менять не нужно.
Метаданные аудио загружаются после раскрытия пачки; при сбое показана ошибка (включая HTTP-статус, если доступен)
Аудио и его метаданные загружаются только после Play (`preload="none"`); раскрытие пачки не запускает запросы к файлам.
При сбое показана ошибка (включая HTTP-статус, если доступен)
и кнопка повторной попытки с обновлением подписанных ссылок.
Одновременно играет только один предпросмотр: запуск другого ставит предыдущий на паузу с сохранением позиции.
Сворачивание пачки или уход со страницы также останавливает её прослушивание.