217 lines
8.1 KiB
Vue
217 lines
8.1 KiB
Vue
<script lang="ts" setup>
|
|
import type { PropType } from 'vue'
|
|
|
|
const props = defineProps({
|
|
video: {
|
|
type: Object as PropType<GBVideoItem>,
|
|
required: true,
|
|
},
|
|
})
|
|
|
|
const emit = defineEmits({
|
|
delete: (video: GBVideoItem) => video,
|
|
})
|
|
|
|
const dayjs = useDayjs()
|
|
const toast = useToast()
|
|
|
|
const isPreviewModalOpen = ref(false)
|
|
const isFullContentOpen = ref(false)
|
|
const downloadingState = reactive({
|
|
subtitle: 0,
|
|
video: 0,
|
|
})
|
|
|
|
const startDownload = (url: string, filename: string) => {
|
|
if (url.endsWith('.ass')) {
|
|
downloadingState.subtitle = 0
|
|
} else {
|
|
downloadingState.video = 0
|
|
}
|
|
|
|
const {
|
|
download,
|
|
progressEmitter,
|
|
} = useDownload(url, filename)
|
|
|
|
progressEmitter.on('progress', progress => {
|
|
if (url.endsWith('.ass')) {
|
|
downloadingState.subtitle = progress
|
|
} else {
|
|
downloadingState.video = progress
|
|
}
|
|
console.log(downloadingState)
|
|
})
|
|
|
|
progressEmitter.on('done', () => {
|
|
if (url.endsWith('.ass')) {
|
|
downloadingState.subtitle = 100
|
|
} else {
|
|
downloadingState.video = 100
|
|
}
|
|
toast.add({
|
|
title: '下载完成',
|
|
description: '资源下载已完成',
|
|
color: 'green',
|
|
icon: 'i-tabler-check',
|
|
})
|
|
})
|
|
|
|
progressEmitter.on('error', err => {
|
|
if (url.endsWith('.ass')) {
|
|
downloadingState.subtitle = 0
|
|
} else {
|
|
downloadingState.video = 0
|
|
}
|
|
toast.add({
|
|
title: '下载失败',
|
|
description: err.message || '下载失败,未知错误',
|
|
color: 'red',
|
|
icon: 'i-tabler-alert-triangle',
|
|
})
|
|
})
|
|
|
|
download()
|
|
}
|
|
|
|
const onClick = () => {
|
|
console.log('click delete')
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
class="w-full flex gap-2 rounded-xl border border-neutral-200 dark:border-neutral-700 hover:shadow transition overflow-hidden p-3"
|
|
>
|
|
<div class="flex-0 h-48 aspect-[10/16] flex flex-col items-center justify-center rounded-lg shadow overflow-hidden relative group">
|
|
<div v-if="!video.video_cover" class="w-full h-full bg-primary flex flex-col justify-center items-center gap-2">
|
|
<UIcon class="animate-spin text-4xl text-white" name="tabler:loader"/>
|
|
<div class="flex flex-col items-center gap-0.5">
|
|
<span class="text-sm font-bold text-white/90">火速生成中</span>
|
|
<span class="text-xs font-medium text-white/50">{{ video.progress }}%</span>
|
|
</div>
|
|
</div>
|
|
<NuxtImg v-else :src="video.video_cover" class="w-full h-full brightness-90 object-cover"/>
|
|
<div class="absolute inset-0 bg-black/10 backdrop-blur-md flex justify-center items-center rounded-lg opacity-0 group-hover:opacity-100 duration-300">
|
|
<div class="rounded-full w-14 aspect-square bg-gray-300/50 backdrop-blur-md flex justify-center items-center cursor-pointer" @click="isPreviewModalOpen = true">
|
|
<Icon name="i-tabler-play" class="text-white text-3xl" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="flex-1 flex flex-col justify-between gap-2">
|
|
<div class="flex-1 rounded-lg bg-neutral-100 dark:bg-neutral-800 p-2 px-2.5">
|
|
<ul class="grid grid-cols-2 gap-1.5">
|
|
<li class="col-span-2">
|
|
<!-- <h2 class="text-2xs font-medium text-primary-500">标题</h2>-->
|
|
<p class="text-sm font-bold line-clamp-1">{{ video.title || '无标题' }}</p>
|
|
</li>
|
|
<li class="">
|
|
<h2 class="text-2xs font-medium text-primary-500">完成时间</h2>
|
|
<p class="text-xs line-clamp-1">{{ video.complete_time ? dayjs(video.complete_time * 1000).format('YYYY-MM-DD HH:mm:ss') : '进行中' }}</p>
|
|
</li>
|
|
<li class="">
|
|
<h2 class="text-2xs font-medium text-primary-500">生成耗时</h2>
|
|
<p class="text-xs line-clamp-1">{{ video.duration ? dayjs.duration(video.duration || 0).format('HH:mm:ss') : '进行中' }}</p>
|
|
</li>
|
|
<li class="col-span-2 cursor-pointer" @click="isFullContentOpen = true">
|
|
<h2 class="text-2xs font-medium text-primary-500">驱动文本</h2>
|
|
<p class="text-xs line-clamp-4 text-justify">{{ video.content }}</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="flex justify-end sm:justify-between items-center group flex-nowrap whitespace-nowrap">
|
|
<!-- <div-->
|
|
<!-- class="hidden sm:flex items-center gap-1 transition-all group-hover:opacity-0 group-hover:pointer-events-none">-->
|
|
<!-- <UIcon class="text-primary text-lg" name="i-tabler-user-square-rounded"/>-->
|
|
<!-- <p class="text-xs">数字人 {{ video.digital_human_id }}</p>-->
|
|
<!-- </div>-->
|
|
<div
|
|
class="w-fit hidden sm:flex items-center gap-1 transition-all group-hover:opacity-0 group-hover:pointer-events-none">
|
|
<p class="text-2xs text-neutral-400 dark:text-neutral-500">{{ video.digital_human_id }}</p>
|
|
</div>
|
|
<div class="space-x-2">
|
|
<UButton
|
|
class="transition-all sm:opacity-0 sm:translate-x-4 sm:pointer-events-none group-hover:opacity-100 group-hover:translate-x-0 group-hover:pointer-events-auto"
|
|
color="red"
|
|
icon="i-tabler-trash"
|
|
size="xs"
|
|
variant="soft"
|
|
@click="emit('delete', video)"
|
|
/>
|
|
<UButtonGroup size="xs">
|
|
<UButton
|
|
:label="downloadingState.subtitle > 0 && downloadingState.subtitle < 100 ? `${downloadingState.subtitle.toFixed(0)}%` : '字幕'"
|
|
:loading="downloadingState.subtitle > 0 && downloadingState.subtitle < 100"
|
|
:disabled="!video.subtitle"
|
|
color="primary"
|
|
leading-icon="i-tabler-file-download"
|
|
variant="soft"
|
|
@click="startDownload(video.subtitle!, (video.title || video.task_id) + '.ass')"
|
|
/>
|
|
<UButton
|
|
:label="downloadingState.video > 0 && downloadingState.video < 100 ? `${downloadingState.video.toFixed(0)}%` : '视频'"
|
|
:loading="downloadingState.video > 0 && downloadingState.video < 100"
|
|
:disabled="!video.video_url"
|
|
color="primary"
|
|
leading-icon="i-tabler-download"
|
|
variant="soft"
|
|
@click="startDownload(video.video_url!, (video.title || video.task_id) + '.mp4')"
|
|
/>
|
|
</UButtonGroup>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Full video content -->
|
|
<UModal v-model="isFullContentOpen">
|
|
<UCard :ui="{ ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
|
|
<template #header>
|
|
<div class="flex items-center justify-between">
|
|
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
|
|
{{ video.title || '无标题' }}
|
|
<span class="block text-xs text-primary">驱动内容</span>
|
|
</h3>
|
|
<UButton
|
|
color="gray"
|
|
icon="i-tabler-x"
|
|
variant="ghost"
|
|
@click="isFullContentOpen = false"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<div>
|
|
<article class="prose">
|
|
<p class="text-justify">{{ video.content }}</p>
|
|
</article>
|
|
</div>
|
|
|
|
<template #footer>
|
|
<div class="flex justify-end gap-2">
|
|
<UButton color="primary" @click="isFullContentOpen = false">关闭</UButton>
|
|
</div>
|
|
</template>
|
|
</UCard>
|
|
</UModal>
|
|
<UModal v-model="isPreviewModalOpen">
|
|
<UCard :ui="{ ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
|
|
<template #header>
|
|
<div class="flex items-center justify-between">
|
|
<div class="text-base font-semibold leading-6 text-gray-900 dark:text-white overflow-hidden">
|
|
<p>绿幕视频预览</p>
|
|
<p class="text-xs text-blue-500 w-full overflow-hidden text-nowrap text-ellipsis">
|
|
{{ video.title }}
|
|
</p>
|
|
</div>
|
|
<UButton class="-my-1" color="gray" icon="i-tabler-x" variant="ghost" @click="isPreviewModalOpen = false" />
|
|
</div>
|
|
</template>
|
|
|
|
<video class="w-full rounded shadow" controls autoplay :src="video.video_url" />
|
|
</UCard>
|
|
</UModal>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
|
|
</style> |