feat: save modified subtitle

This commit is contained in:
2024-07-28 00:44:55 +08:00
parent d637442fda
commit c80940312d
7 changed files with 97 additions and 40 deletions

View File

@@ -37,9 +37,9 @@ defineShortcuts({
},
},
'meta_s': {
handler: () => {
handler: async () => {
if (isDropdownOpen.value && isDownloadable.value) {
startDownload(props.course.subtitle_url, `眩生花微课_${ props.course.title }_${ props.course.task_id }.srt`)
await startDownload(await fetchCourseSubtitleUrl(props.course), `眩生花微课_${ props.course.title }_${ props.course.task_id }.srt`)
}
},
},
@@ -220,7 +220,7 @@ const copyTaskId = (extraMessage?: string) => {
disabled: !isDownloadable,
click: () => isPreviewModalOpen = true,
}, {
label: '查看字幕',
label: '编辑字幕',
icon: 'i-solar-subtitles-linear',
shortcuts: [metaSymbol, 'D'],
disabled: !isDownloadable,
@@ -233,8 +233,8 @@ const copyTaskId = (extraMessage?: string) => {
icon: 'i-tabler-file-download',
shortcuts: [metaSymbol, 'S'],
disabled: !isDownloadable,
click: () => {
startDownload(course.subtitle_url, `眩生花微课_${ props.course.title }_${ props.course.task_id }.srt`)
click: async () => {
await startDownload(await fetchCourseSubtitleUrl(course), `眩生花微课_${ props.course.title }_${ props.course.task_id }.srt`)
}
}], [{
label: '删除记录',

View File

@@ -1,5 +1,6 @@
<script setup lang="ts">
import type { PropType } from 'vue'
import { encode } from '@monosky/base64'
interface Subtitle {
start: string;
@@ -17,18 +18,22 @@ const props = defineProps({
const dayjs = useDayjs()
const toast = useToast()
const loginState = useLoginState()
const isDrawerActive = ref(false)
const isLoading = ref(true)
const isSaving = ref(false)
const rawSrt = ref<string | null>(null)
const subtitles = ref<Subtitle[]>([])
const modified = ref(false)
const videoElement = ref<HTMLVideoElement | null>(null)
const loadSrt = async () => {
isLoading.value = true
try {
const response = await fetch(props.course.subtitle_url)
// const response = await fetch(props.course.subtitle_url)
const response = await fetch(await fetchCourseSubtitleUrl(props.course))
const text = await response.text()
rawSrt.value = text
parseSrt(text)
@@ -129,6 +134,30 @@ const onSubtitleInputClick = (subtitle: Subtitle) => {
videoElement.value.pause()
}
const saveNewSubtitle = () => {
isSaving.value = true
const encodedSubtitle = encode(generateSrt())
useFetchWrapped<
req.gen.CourseSubtitleCreate & AuthedRequest,
BaseResponse<resp.gen.CourseSubtitleCreate>
>('App.Digital_VideoSubtitle.CreateFile', {
token: loginState.token!,
user_id: loginState.user.id,
sub_type: 1,
sub_content: encodedSubtitle,
task_id: props.course?.task_id,
}).then(_ => {
modified.value = false
toast.add({
color: 'green',
title: '字幕已保存',
description: '修改后的字幕文件已保存',
})
}).finally(() => {
isSaving.value = false
})
}
onMounted(() => {
if (rawSrt.value) {
parseSrt(rawSrt.value)
@@ -240,6 +269,7 @@ defineExpose({
:autofocus="false"
:color="subtitle.active ? 'primary' : undefined"
@click="onSubtitleInputClick(subtitle)"
@input="() => { if(!modified) modified = true }"
>
<template #trailing>
<Icon v-if="subtitle.active" name="tabler:keyframe-align-vertical-filled"/>
@@ -252,11 +282,12 @@ defineExpose({
<template #footer>
<!-- TODO: 24/07/02 Modified subtitles upload -->
<UButton @click="() => {
console.log(generateSrt())
}">
Generate
</UButton>
<div class="flex justify-end items-center gap-2">
<span v-if="modified" class="text-sm text-yellow-500 font-medium">已更改但未保存</span>
<UButton :disabled="!modified" :loading="isSaving" @click="saveNewSubtitle">
保存{{ isSaving ? '' : '' }}
</UButton>
</div>
</template>
</UCard>
</USlideover>

View File

@@ -0,0 +1,24 @@
export const fetchCourseSubtitleUrl = async (course: resp.gen.CourseGenItem) => {
const loginState = useLoginState()
const subtitleRecord = await useFetchWrapped<
{
page?: number
perpage?: number
task_id: string
} & AuthedRequest,
BaseResponse<PagedData<resp.gen.CourseSubtitleCreate>>
>('App.Digital_VideoSubtitle.GetList', {
token: loginState.token!,
user_id: loginState.user.id,
task_id: course.task_id,
page: 1,
perpage: 1,
})
if (subtitleRecord.data.items.length !== 1) {
return course.subtitle_url
}
return subtitleRecord.data.items[0].url
}

View File

@@ -27,7 +27,7 @@ export default defineNuxtConfig({
icons: ['tabler', 'solar', 'line-md', 'svg-spinners'],
},
colorMode: {
preference: 'dark',
preference: 'light',
},
dayjs: {
locales: ['zh', 'en'],

View File

@@ -14,6 +14,7 @@
"@iconify-json/solar": "^1.1.9",
"@iconify-json/svg-spinners": "^1.1.2",
"@iconify-json/tabler": "^1.1.105",
"@monosky/base64": "^0.0.3",
"@nuxt/image": "^1.7.0",
"@nuxt/ui": "^2.14.1",
"@uniiem/object-trim": "^0.2.0",

21
typings/types.d.ts vendored
View File

@@ -85,6 +85,18 @@ namespace req {
to_user_id: number
task_id: string
}
/**
* @param sub_type 0为绿幕生成1为PPT生成
* @param sub_content BASE64后的ass字幕字符串
* @param sub_ver optional 字幕版本
*/
interface CourseSubtitleCreate {
sub_type: 0 | 1
task_id: string
sub_content: string
sub_ver?: number
}
}
interface AssistantTemplateList {
@@ -152,6 +164,15 @@ namespace resp {
interface CourseGenDelete {
code: 0 | 1
}
/**
* @param video_sub_id 字幕记录 ID
* @param url 已上传的ass文件URL(文件存放于OSS)
*/
interface CourseSubtitleCreate {
video_sub_id: number
url: string
}
}
}

View File

@@ -1541,6 +1541,11 @@
semver "^7.3.5"
tar "^6.1.11"
"@monosky/base64@^0.0.3":
version "0.0.3"
resolved "https://registry.yarnpkg.com/@monosky/base64/-/base64-0.0.3.tgz#32f512911df482df9f5bce208c9eff3a13a94589"
integrity sha512-PNNOPniUyjY725FvPjfRZ++Bbufjy3sy2YH7HdvpmwexuZSSdJ1UDkZx03gnWAbhM3hIhBTWbF49/9xMSbNRgg==
"@netlify/functions@^2.8.0":
version "2.8.0"
resolved "https://registry.yarnpkg.com/@netlify/functions/-/functions-2.8.0.tgz#245f4be789891159d95391c171263ded8ee1ee3a"
@@ -7647,16 +7652,7 @@ streamx@^2.15.0, streamx@^2.18.0:
optionalDependencies:
bare-events "^2.2.0"
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -7743,14 +7739,7 @@ stringify-object@^3.3.0:
is-obj "^1.0.1"
is-regexp "^1.0.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -8838,16 +8827,7 @@ workbox-window@7.1.0, workbox-window@^7.1.0:
"@types/trusted-types" "^2.0.2"
workbox-core "7.1.0"
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^7.0.0:
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==