fix(uploads): load audio previews only on play
This commit is contained in:
1 parent
af06cf1e75
commit
ca2a50b515
3 files changed
+24
-6
No files matched your search
@@ -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);
|
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();
|
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 setup(page, 'queue');
|
||||||
await page.getByRole('button', { name: 'Развернуть пачку', exact: true }).click();
|
await page.getByRole('button', { name: 'Развернуть пачку', exact: true }).click();
|
||||||
const audio = page.locator('audio').first();
|
const audio = page.locator('audio').first();
|
||||||
await expect(audio).toHaveAttribute('preload', 'metadata');
|
await expect(audio).toHaveAttribute('preload', 'none');
|
||||||
await expect.poll(() => audio.evaluate((el: HTMLAudioElement) => el.duration)).toBe(2);
|
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 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);
|
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();
|
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();
|
await page.getByRole('button', { name: 'Развернуть пачку', exact: true }).click();
|
||||||
const preview = page.locator('audio').first().locator('..');
|
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.');
|
await expect(preview.getByRole('alert')).toHaveText('Не удалось загрузить аудио: HTTP 403.');
|
||||||
available = true;
|
available = true;
|
||||||
await preview.getByRole('button', { name: 'Повторить', exact: true }).click();
|
await preview.getByRole('button', { name: 'Повторить', exact: true }).click();
|
||||||
|
|||||||
@@ -45,10 +45,11 @@ export function AudioPreview({ url, name, active }: { url: string; name: string;
|
|||||||
setHttpStatus(null);
|
setHttpStatus(null);
|
||||||
void queryClient.invalidateQueries({ queryKey: ['admin-uploads'] });
|
void queryClient.invalidateQueries({ queryKey: ['admin-uploads'] });
|
||||||
audio.current?.load();
|
audio.current?.load();
|
||||||
|
void audio.current?.play().catch(() => { /* Native media errors are shown below. */ });
|
||||||
};
|
};
|
||||||
|
|
||||||
return <div className={u.preview}>
|
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 })}
|
aria-label={t('uploads.listen', { name })}
|
||||||
onPlay={(event) => {
|
onPlay={(event) => {
|
||||||
if (event.currentTarget.paused) return;
|
if (event.currentTarget.paused) return;
|
||||||
|
|||||||
+2
-1
@@ -165,7 +165,8 @@
|
|||||||
Если рассчитать путь нельзя, показаны причина и действие для исправления вместо старого пути.
|
Если рассчитать путь нельзя, показаны причина и действие для исправления вместо старого пути.
|
||||||
- Прослушивание staging выдаёт сам API с поддержкой Range: приватные файлы tus (`0640`, каталоги `0750`)
|
- Прослушивание staging выдаёт сам API с поддержкой Range: приватные файлы tus (`0640`, каталоги `0750`)
|
||||||
недоступны пользователю nginx. Выдача не зависит от `X_ACCEL`; права существующих файлов менять не нужно.
|
недоступны пользователю nginx. Выдача не зависит от `X_ACCEL`; права существующих файлов менять не нужно.
|
||||||
Метаданные аудио загружаются после раскрытия пачки; при сбое показана ошибка (включая HTTP-статус, если доступен)
|
Аудио и его метаданные загружаются только после Play (`preload="none"`); раскрытие пачки не запускает запросы к файлам.
|
||||||
|
При сбое показана ошибка (включая HTTP-статус, если доступен)
|
||||||
и кнопка повторной попытки с обновлением подписанных ссылок.
|
и кнопка повторной попытки с обновлением подписанных ссылок.
|
||||||
Одновременно играет только один предпросмотр: запуск другого ставит предыдущий на паузу с сохранением позиции.
|
Одновременно играет только один предпросмотр: запуск другого ставит предыдущий на паузу с сохранением позиции.
|
||||||
Сворачивание пачки или уход со страницы также останавливает её прослушивание.
|
Сворачивание пачки или уход со страницы также останавливает её прослушивание.
|
||||||
|
|||||||
Reference in new issue
Block a user