Implement local track upload

This commit is contained in:
2026-01-14 00:28:19 +05:00
parent df9fe970ab
commit 5fd026c026
2 changed files with 129 additions and 13 deletions

View File

@ -16,13 +16,15 @@ 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';
import { addLocalTrack } from '~/composeables/api/track-controller/track-controller';
const files = ref<File[]>([]);
const currentPlaylistStore = useCurrentPlaylistStore();
const progressEntries = ref<Map<string, StreamProgress200Item>>(new Map());
let listener: EventSourceListener<StreamProgress200Item> | null = null;
const ongoingUploads = ref<Map<string, { file: File; progress: number }>>(new Map());
const sortedProgressEntries = computed(() => {
return Array.from(progressEntries.value.values())
.sort((a, b) => {
@ -60,6 +62,69 @@ async function listenImports() {
})
}
async function handleFileUpload(uploadedFiles: File[]) {
if (!currentPlaylistStore.id || currentPlaylistStore.id === -1) {
console.error('No playlist selected');
return;
}
for (const file of uploadedFiles) {
const uploadId = generateUploadId(file);
ongoingUploads.value.set(uploadId, { file, progress: 0 });
try {
const formData = new FormData();
formData.append('source', file);
await addLocalTrack(
currentPlaylistStore.id,
{ source: file },
{
onUploadProgress: (progressEvent) => {
if (progressEvent.total) {
const progress = Math.round((progressEvent.loaded * 100) / progressEvent.total);
ongoingUploads.value.set(uploadId, {
file,
progress: Math.min(progress, 100)
});
ongoingUploads.value = new Map(ongoingUploads.value);
}
}
}
);
ongoingUploads.value.delete(uploadId);
ongoingUploads.value = new Map(ongoingUploads.value);
const index = files.value.findIndex(f =>
f.name === file.name &&
f.size === file.size &&
f.lastModified === file.lastModified
);
if (index !== -1) {
files.value.splice(index, 1);
}
} catch (error) {
console.error('Upload failed:', error);
ongoingUploads.value.delete(uploadId);
ongoingUploads.value = new Map(ongoingUploads.value);
}
}
}
function generateUploadId(file: File): string {
return `${file.name}-${file.size}-${file.lastModified}-${Date.now()}`;
}
function handleFileChange(changedFiles: File[]) {
files.value = changedFiles;
handleFileUpload(changedFiles);
}
function onYoutubeClick(e: MouseEvent) {
e.stopPropagation();
}
@ -91,7 +156,8 @@ onUnmounted(() => {
</Outline>
</template>
<div class="w-full flex flex-col p-8">
<FileUpload v-model="files" class="rounded-lg border border-dashed border-muted" @onChange="">
<FileUpload v-model="files" class="rounded-lg border border-dashed border-muted"
@onChange="handleFileChange" :ongoing-uploads="ongoingUploads">
<template #default>
<FileUploadGrid />
</template>
@ -113,8 +179,12 @@ onUnmounted(() => {
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">
<Empty class="border border-dashed"
v-if="progressEntries.size === 0 && ongoingUploads.size === 0">
<EmptyHeader>
<EmptyMedia variant="icon">
<MegaphoneOff />