Upload from the library queue straight to storage and retire the upload page
This commit is contained in:
@@ -1,68 +1,42 @@
|
||||
<script lang="ts">
|
||||
import { genreLabels } from '$lib/data/genres';
|
||||
import {
|
||||
filesFromDrop,
|
||||
readDuration,
|
||||
summarizeQueue,
|
||||
toQueuedUpload,
|
||||
UPLOAD_FORMATS,
|
||||
uploadLimitsLabel,
|
||||
type QueuedUpload,
|
||||
type Visibility
|
||||
} from '$lib/data/uploadQueue';
|
||||
import { filesFromDrop, UPLOAD_FORMATS, uploadLimitsLabel } from '$lib/data/uploadQueue';
|
||||
import { uploadQueue as queue } from '$lib/state/uploadQueue.svelte';
|
||||
import { plural } from '$lib/utils/format';
|
||||
import type { GenreOption } from '$lib/types';
|
||||
import FileDropzone from '$lib/components/ui/forms/FileDropzone.svelte';
|
||||
import Alert from '$lib/components/ui/primitives/Alert.svelte';
|
||||
import Button from '$lib/components/ui/primitives/Button.svelte';
|
||||
import UploadQueueList from '$lib/components/music/library/UploadQueueList.svelte';
|
||||
import UploadSettings from '$lib/components/music/library/UploadSettings.svelte';
|
||||
|
||||
interface Props {
|
||||
genres: GenreOption[];
|
||||
interface AlbumChoice {
|
||||
id: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
let { genres }: Props = $props();
|
||||
interface Props {
|
||||
genres: GenreOption[];
|
||||
albums: AlbumChoice[];
|
||||
}
|
||||
|
||||
let { genres, albums }: Props = $props();
|
||||
|
||||
const ACCEPT = UPLOAD_FORMATS.map((ext) => `.${ext}`).join(',');
|
||||
|
||||
let queue = $state<QueuedUpload[]>([]);
|
||||
let selectedGenres = $state<string[]>([]);
|
||||
let visibility = $state<Visibility>('private');
|
||||
let coauthors = $state<string[]>([]);
|
||||
|
||||
const single = $derived(queue.length === 1 ? queue[0] : null);
|
||||
const summary = $derived(summarizeQueue(queue));
|
||||
const genresLabel = $derived(genreLabels(selectedGenres));
|
||||
const visibilityLabel = $derived(visibility === 'private' ? 'Privado' : 'Público');
|
||||
const summary = $derived(queue.summary);
|
||||
const genresLabel = $derived(genreLabels(queue.selectedGenres));
|
||||
const queueMeta = $derived(
|
||||
[
|
||||
plural(summary.ready, 'lista', 'listas'),
|
||||
summary.failed > 0 ? `${summary.failed} con error` : null
|
||||
summary.ready > 0 ? plural(summary.ready, 'lista', 'listas') : null,
|
||||
summary.uploading > 0 ? `${summary.uploading} subiendo` : null,
|
||||
summary.processing > 0 ? `${summary.processing} procesando` : null,
|
||||
summary.done > 0 ? plural(summary.done, 'terminada', 'terminadas') : null,
|
||||
summary.errored + summary.failed > 0 ? `${summary.errored + summary.failed} con error` : null
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' · ')
|
||||
);
|
||||
|
||||
function enqueue(files: File[]) {
|
||||
const added = files.map(toQueuedUpload);
|
||||
queue = [...queue, ...added];
|
||||
for (const item of added) {
|
||||
if (item.error) continue;
|
||||
readDuration(item.file).then((duration) => {
|
||||
queue = queue.map((current) =>
|
||||
current.id === item.id ? { ...current, duration } : current
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function setTitle(id: string, title: string) {
|
||||
queue = queue.map((item) => (item.id === id ? { ...item, title } : item));
|
||||
}
|
||||
|
||||
function remove(id: string) {
|
||||
queue = queue.filter((item) => item.id !== id);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="mt-6 grid items-start gap-6 lg:grid-cols-2">
|
||||
@@ -72,17 +46,18 @@
|
||||
title="Arrastra tus archivos aquí"
|
||||
hint={uploadLimitsLabel()}
|
||||
inputLabel="Elegir archivos de audio"
|
||||
onfiles={enqueue}
|
||||
onfiles={(files) => queue.enqueue(files)}
|
||||
readDrop={filesFromDrop}
|
||||
/>
|
||||
|
||||
<UploadSettings
|
||||
{genres}
|
||||
bind:selectedGenres
|
||||
bind:visibility
|
||||
bind:coauthors
|
||||
bind:title={() => single?.title ?? '', (value) => single && setTitle(single.id, value)}
|
||||
titleDisabled={single === null}
|
||||
{albums}
|
||||
bind:selectedGenres={queue.selectedGenres}
|
||||
bind:albumId={queue.albumId}
|
||||
hasCover={queue.cover !== null}
|
||||
oncover={(file) => (queue.cover = file)}
|
||||
disabled={queue.running}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -91,24 +66,40 @@
|
||||
<div>
|
||||
<h2 class="text-display-3 text-fg">Cola de subida</h2>
|
||||
<p class="mt-1 text-xs text-fg-2">
|
||||
{queue.length > 0 ? queueMeta : 'Todavía no hay archivos'}
|
||||
{queue.items.length > 0 ? queueMeta : 'Todavía no hay archivos'}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
{#if queue.length > 0}
|
||||
<Button variant="secondary" size="sm" onclick={() => (queue = [])}>Vaciar cola</Button>
|
||||
{#if queue.items.length > 0}
|
||||
<Button variant="secondary" size="sm" onclick={() => queue.clear()}>Vaciar cola</Button>
|
||||
{/if}
|
||||
<Button disabled>Añadir {summary.ready} a la biblioteca</Button>
|
||||
<Button
|
||||
disabled={summary.ready === 0}
|
||||
loading={queue.running}
|
||||
onclick={() => queue.start(genres)}
|
||||
>
|
||||
Subir {summary.ready}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{#if queue.length > 0}
|
||||
|
||||
<p class="mt-3 text-xs text-fg-2">
|
||||
Se añadirá sin publicar: publícala desde Canciones cuando termine de procesarse.
|
||||
</p>
|
||||
|
||||
{#if queue.notice}
|
||||
<Alert tone="danger" class="mt-4">{queue.notice}</Alert>
|
||||
{/if}
|
||||
|
||||
{#if queue.items.length > 0}
|
||||
<UploadQueueList
|
||||
class="mt-4"
|
||||
items={queue}
|
||||
items={queue.items}
|
||||
{genresLabel}
|
||||
{visibilityLabel}
|
||||
onRemove={remove}
|
||||
onTitle={setTitle}
|
||||
onRemove={(id) => queue.remove(id)}
|
||||
onCancel={(id) => queue.cancel(id)}
|
||||
onRetry={(id) => queue.retry(id)}
|
||||
onTitle={(id, title) => queue.setTitle(id, title)}
|
||||
/>
|
||||
{:else}
|
||||
<p class="mt-8 mb-6 text-center text-sm text-fg-2">
|
||||
|
||||
Reference in New Issue
Block a user