134 lines
5.0 KiB
Vue
134 lines
5.0 KiB
Vue
<script setup lang="ts">
|
|
import Outline from '@/components/ui/outline/Outline.vue';
|
|
import SidebarTrigger from '@/components/ui/sidebar/SidebarTrigger.vue';
|
|
import Empty from '@/components/ui/empty/Empty.vue';
|
|
import UploadEntry from '@/components/internal/import/uploadentry/UploadEntry.vue';
|
|
import EmptyHeader from '@/components/ui/empty/EmptyHeader.vue';
|
|
import EmptyMedia from '@/components/ui/empty/EmptyMedia.vue';
|
|
import EmptyTitle from '@/components/ui/empty/EmptyTitle.vue';
|
|
import EmptyDescription from '@/components/ui/empty/EmptyDescription.vue';
|
|
|
|
import { MegaphoneOff, Play } from 'lucide-vue-next';
|
|
import type { EventSourceListener } from '~/composeables/api/event-source';
|
|
import type { StreamProgress200Item } from '~/composeables/api/models';
|
|
import { streamProgress } from '~/composeables/api/progress-sse-controller/progress-sse-controller';
|
|
import { useCurrentPlaylistStore } from '~/stores/use-current-playlist-store';
|
|
import FileUpload from '@/components/ui/file-upload/FileUpload.vue';
|
|
import FileUploadGrid from '@/components/ui/file-upload/FileUploadGrid.vue';
|
|
import Button from '@/components/ui/button/Button.vue';
|
|
|
|
const files = ref<File[]>([]);
|
|
|
|
const currentPlaylistStore = useCurrentPlaylistStore();
|
|
const progressEntries = ref<Map<string, StreamProgress200Item>>(new Map());
|
|
let listener: EventSourceListener<StreamProgress200Item> | null = null;
|
|
|
|
const sortedProgressEntries = computed(() => {
|
|
return Array.from(progressEntries.value.values())
|
|
.sort((a, b) => {
|
|
const timeA = a.timestamp || 0;
|
|
const timeB = b.timestamp || 0;
|
|
return timeB - timeA;
|
|
});
|
|
});
|
|
|
|
const unwatch = watch(() => currentPlaylistStore.id, (newId, oldId) => {
|
|
if (newId !== -1 && newId !== oldId) {
|
|
listenImports();
|
|
} else if (newId === -1) {
|
|
stopImports();
|
|
}
|
|
}, { immediate: true });
|
|
|
|
const updateEntryFromProgress = (data: StreamProgress200Item) => {
|
|
if (!data.trackSourceId)
|
|
return;
|
|
progressEntries.value = new Map(progressEntries.value);
|
|
progressEntries.value.set(data.id!.toString(), data);
|
|
}
|
|
|
|
async function listenImports() {
|
|
if (listener) {
|
|
listener.close();
|
|
}
|
|
streamProgress(currentPlaylistStore.id).then(listener => {
|
|
listener.handle(data => {
|
|
updateEntryFromProgress(data);
|
|
})
|
|
}).catch(err => {
|
|
console.error(err);
|
|
})
|
|
}
|
|
|
|
function onYoutubeClick(e: MouseEvent) {
|
|
e.stopPropagation();
|
|
}
|
|
|
|
function stopImports() {
|
|
if (listener) {
|
|
listener.close();
|
|
listener = null;
|
|
}
|
|
}
|
|
|
|
onUnmounted(() => {
|
|
stopImports();
|
|
unwatch();
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div class="flex-1">
|
|
<NuxtLayout name="default">
|
|
<template #header>
|
|
<Outline side="bottom" padding="dense" class="w-full">
|
|
<div class="flex gap-8 w-full items-center">
|
|
<SidebarTrigger :size="5" />
|
|
<h2 class="scroll-m-20 text-3xl font-semibold tracking-tight transition-colors first:mt-0">
|
|
Import tracks
|
|
</h2>
|
|
</div>
|
|
</Outline>
|
|
</template>
|
|
<div class="w-full flex flex-col p-8">
|
|
<FileUpload v-model="files" class="rounded-lg border border-dashed border-muted" @onChange="">
|
|
<template #default>
|
|
<FileUploadGrid />
|
|
</template>
|
|
<template #content>
|
|
<h4 class="z-20 scroll-m-20 text-xl font-semibold tracking-tight">
|
|
Drag and drop your audio files
|
|
</h4>
|
|
<p class="z-20 font-normal text-muted-foreground">
|
|
or
|
|
</p>
|
|
<Button class="z-20" variant="destructive" @click="onYoutubeClick">
|
|
<Play />
|
|
From Youtube
|
|
</Button>
|
|
</template>
|
|
</FileUpload>
|
|
<div>
|
|
<h3 class="scroll-m-20 text-2xl font-semibold tracking-tight">
|
|
Uploaded files
|
|
</h3>
|
|
<div class="space-y-2">
|
|
<UploadEntry v-for="entry in sortedProgressEntries" :key="entry.id" :entry="entry" />
|
|
<Empty class="border border-dashed" v-if="progressEntries.size === 0">
|
|
<EmptyHeader>
|
|
<EmptyMedia variant="icon">
|
|
<MegaphoneOff />
|
|
</EmptyMedia>
|
|
<EmptyTitle>No imports found</EmptyTitle>
|
|
<EmptyDescription>
|
|
Upload any track to see their progress.
|
|
</EmptyDescription>
|
|
</EmptyHeader>
|
|
</Empty>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</NuxtLayout>
|
|
</div>
|
|
</template>
|