fix(uploads): allow signed audio previews and compact destination details
ci / module boundaries / boundaries (push) Successful in 7s
ci / db / db (push) Successful in 47s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m42s
ci / ts / ts (packages + web) (push) Failing after 5m24s
ci / api / api (push) Successful in 42s
ci / module boundaries / boundaries (push) Successful in 7s
ci / db / db (push) Successful in 47s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m42s
ci / ts / ts (packages + web) (push) Failing after 5m24s
ci / api / api (push) Successful in 42s
This commit is contained in:
1 parent
30c3c85f89
commit
37e33c656b
8 files changed
+137
-19
No files matched your search
@@ -13,7 +13,7 @@ async function setup(page: Page, scenario: 'queue' | 'template' | 'mine') {
|
||||
const source = structuredClone(mobileFixtures['/api/v1/admin/sources'].items[0]);
|
||||
const file = structuredClone(mobileFixtures['/api/v1/admin/uploads'].items[0]);
|
||||
const files = [
|
||||
{ ...file, id: 'visible', original_name: 'visible.flac', target_source_name: source.name, target_rel_path: 'Artist/Album/01 - Song.flac', target_storage_path: 'nas:/music/Artist/Album/01 - Song.flac' },
|
||||
{ ...file, id: 'visible', original_name: 'visible.flac', target_source_name: source.name, target_rel_path: 'Artist/Album/01 - Song.flac', target_storage_path: 'nas:/music/Artist/Album/01 - Song.flac', stream_url: `/api/v1/admin/uploads/files/${file.id}/stream?exp=9999999999&sig=test` },
|
||||
{ ...file, id: 'approved', original_name: 'approved.flac', status: 'approved' },
|
||||
{ ...file, id: 'hidden', original_name: 'hidden.flac', status: 'processing' },
|
||||
{ ...file, id: 'withdrawn', original_name: 'withdrawn.flac', metadata: null, status: 'withdrawn' },
|
||||
@@ -78,7 +78,7 @@ test('queue shows the storage and path, and recalculates after fixing placement'
|
||||
await expect(page.getByText('Artist/Album/01 - Song.flac', { exact: true })).toBeHidden();
|
||||
await page.getByRole('button', { name: 'Развернуть пачку', exact: true }).click();
|
||||
await expect(page.getByText('Artist/Album/01 - Song.flac', { exact: true })).toBeVisible();
|
||||
await expect(page.getByText('nas:/music/Artist/Album/01 - Song.flac', { exact: true })).toBeVisible();
|
||||
await expect(page.getByText('Artist/Album/01 - Song.flac', { exact: true })).toHaveAttribute('title', 'nas:/music/Artist/Album/01 - Song.flac');
|
||||
await expect(page.getByText('Хранилище не выбрано', { exact: true })).toBeVisible();
|
||||
await page.getByRole('checkbox', { name: 'Выбрать всю пачку', exact: true }).check();
|
||||
await page.getByRole('button', { name: 'Свернуть пачку', exact: true }).click();
|
||||
@@ -156,7 +156,7 @@ test('queue shows the current full destination after saving a changed source tem
|
||||
}, path);
|
||||
await navigate('/admin/uploads');
|
||||
await page.getByRole('button', { name: 'Развернуть пачку', exact: true }).click();
|
||||
await expect(page.getByText('nas:/music/Artist/Album/Song.flac', { exact: true })).toBeVisible();
|
||||
await expect(page.getByText('Artist/Album/Song.flac', { exact: true })).toHaveAttribute('title', 'nas:/music/Artist/Album/Song.flac');
|
||||
await navigate(sourcePath);
|
||||
await page.getByRole('textbox', { name: 'Шаблон пути', exact: true }).fill('{albumartist}/{album}/{track:02}. {title}.{ext}');
|
||||
const section = page.getByRole('heading', { name: 'Приём загрузок и шаблон пути' }).locator('..');
|
||||
@@ -165,6 +165,24 @@ test('queue shows the current full destination after saving a changed source tem
|
||||
await expect(page.locator('html')).toHaveAttribute('data-saved-template', '{albumartist}/{album}/{track:02}. {title}.{ext}');
|
||||
await navigate('/admin/uploads');
|
||||
await page.getByRole('button', { name: 'Развернуть пачку', exact: true }).click();
|
||||
await expect(page.getByText('nas:/music/Artist/Album/01. Song.flac', { exact: true })).toBeVisible();
|
||||
await expect(page.getByText('Artist/Album/01. Song.flac', { exact: true })).toHaveAttribute('title', 'nas:/music/Artist/Album/01. Song.flac');
|
||||
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 }) => {
|
||||
const dataSize = 8000 * 2 * 2;
|
||||
const wav = Buffer.alloc(44 + dataSize);
|
||||
wav.write('RIFF', 0); wav.writeUInt32LE(36 + dataSize, 4); wav.write('WAVEfmt ', 8);
|
||||
wav.writeUInt32LE(16, 16); wav.writeUInt16LE(1, 20); wav.writeUInt16LE(1, 22);
|
||||
wav.writeUInt32LE(8000, 24); wav.writeUInt32LE(16000, 28); wav.writeUInt16LE(2, 32); wav.writeUInt16LE(16, 34);
|
||||
wav.write('data', 36); wav.writeUInt32LE(dataSize, 40);
|
||||
await page.route('**/api/v1/admin/uploads/files/*/stream?*', (route) => 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 audio.evaluate((el: HTMLAudioElement) => el.play());
|
||||
await expect.poll(() => audio.evaluate((el: HTMLAudioElement) => el.currentTime)).toBeGreaterThan(0);
|
||||
expect(await audio.evaluate((el: HTMLAudioElement) => el.error)).toBeNull();
|
||||
});
|
||||
@@ -142,7 +142,7 @@ function QueueRow({
|
||||
</div>
|
||||
<div className={u.rowActions}>
|
||||
{f.stream_url && (
|
||||
<audio controls preload="none" src={f.stream_url} aria-label={t('uploads.listen', { name: f.original_name })} />
|
||||
<audio controls preload="metadata" src={f.stream_url} aria-label={t('uploads.listen', { name: f.original_name })} />
|
||||
)}
|
||||
{editable && (
|
||||
<Button variant="ghost" aria-label={t('uploads.edit', { name: f.original_name })} onClick={onEdit}>
|
||||
@@ -160,14 +160,7 @@ function QueueRow({
|
||||
<span className={u.placementLabel}>{t(f.status === 'imported' ? 'uploads.destination_imported' : 'uploads.destination')}</span>
|
||||
<strong>{f.target_source_name ?? t(f.target_source_id ? 'uploads.target_selected' : 'uploads.target_not_selected')}</strong>
|
||||
{f.target_rel_path ? (
|
||||
<>
|
||||
<code className={u.target}>{f.target_rel_path}</code>
|
||||
<span className={u.sub}>{t('uploads.target_path_hint')}</span>
|
||||
{f.target_storage_path && <>
|
||||
<span className={u.placementLabel}>{t('uploads.target_full_path')}</span>
|
||||
<code className={u.target}>{f.target_storage_path}</code>
|
||||
</>}
|
||||
</>
|
||||
<code className={u.target} title={f.target_storage_path ?? undefined}>{f.target_rel_path}</code>
|
||||
) : (
|
||||
<>
|
||||
<span className={u.warn}>{t('uploads.target_path_missing')}</span>
|
||||
|
||||
Reference in new issue
Block a user