feat: wasm-output plugin supports multiple pages

Co-authored-by: lainio24 <lainio24@outlook.com>
This commit is contained in:
WakelessSloth56 2024-04-26 16:36:14 +08:00
parent 0b3dfac457
commit 4979500d2c
No known key found for this signature in database
GPG Key ID: 3AFBE2AD7700CA19
3 changed files with 62 additions and 49 deletions

View File

@ -62,6 +62,7 @@
"linebreak", "linebreak",
"Liveroom", "Liveroom",
"materialdesignicons", "materialdesignicons",
"matroska",
"medialist", "medialist",
"mimetype", "mimetype",
"minmax", "minmax",
@ -133,6 +134,6 @@
"*.tsx": "${capture}.ts", "*.tsx": "${capture}.ts",
"tsconfig.json": "tsconfig.*.json", "tsconfig.json": "tsconfig.*.json",
"package.json": "package-lock.json, yarn.lock, pnpm-lock.yaml, .*", "package.json": "package-lock.json, yarn.lock, pnpm-lock.yaml, .*",
"index.ts": "index.*.ts, index.md, index.*.md", "index.ts": "index.*.ts, index.md, index.*.md"
} }
} }

View File

@ -1,12 +1,17 @@
import { DownloadPackage } from '@/core/download' import { DownloadPackage } from '@/core/download'
import { meta } from '@/core/meta' import { meta } from '@/core/meta'
import { getComponentSettings } from '@/core/settings'
import { Toast } from '@/core/toast' import { Toast } from '@/core/toast'
import { title as pluginTitle } from '.'
import type { Options } from '../../../../components/video/download'
import { DownloadVideoAction } from '../../../../components/video/download/types'
import { FFmpeg } from './ffmpeg' import { FFmpeg } from './ffmpeg'
import { getCacheOrGet, httpGet, toastProgress, toBlobUrl } from './utils' import { getCacheOrGet, httpGet, toastProgress, toBlobUrl } from './utils'
const ffmpeg = new FFmpeg() const ffmpeg = new FFmpeg()
async function load(toast: Toast) { async function loadFFmpeg() {
const toast = Toast.info('初始化', pluginTitle)
await ffmpeg.load({ await ffmpeg.load({
workerLoadURL: toBlobUrl( workerLoadURL: toBlobUrl(
await getCacheOrGet( await getCacheOrGet(
@ -33,24 +38,26 @@ async function load(toast: Toast) {
'application/wasm', 'application/wasm',
), ),
}) })
toast.message = '完成!'
toast.close()
} }
export async function run(
async function single(
name: string, name: string,
toast: Toast,
videoUrl: string, videoUrl: string,
audioUrl: string, audioUrl: string,
isFlac: boolean, isFlac: boolean,
pageIndex = 1,
totalPages = 1,
) { ) {
if (!ffmpeg.loaded) { const toast = Toast.info('', `${pluginTitle} - ${pageIndex} / ${totalPages}`)
await load(toast)
}
ffmpeg.writeFile('video', await httpGet(videoUrl, toastProgress(toast, '正在下载视频流'))) ffmpeg.writeFile('video', await httpGet(videoUrl, toastProgress(toast, '正在下载视频流')))
ffmpeg.writeFile('audio', await httpGet(audioUrl, toastProgress(toast, '正在下载音频流'))) ffmpeg.writeFile('audio', await httpGet(audioUrl, toastProgress(toast, '正在下载音频流')))
toast.message = '混流中……'
toast.message = '混流中……'
const outputExt = isFlac ? 'mkv' : 'mp4' const outputExt = isFlac ? 'mkv' : 'mp4'
name = isFlac ? name.replace(/.[^/.]+$/, `.${outputExt}`) : name name = name.replace(/.[^/.]+$/, `.${outputExt}`)
await ffmpeg.exec([ await ffmpeg.exec([
'-i', '-i',
'video', 'video',
@ -59,7 +66,7 @@ export async function run(
'-c:v', '-c:v',
'copy', 'copy',
'-c:a', '-c:a',
isFlac ? 'flac' : 'copy', 'copy',
'-f', '-f',
isFlac ? 'matroska' : 'mp4', isFlac ? 'matroska' : 'mp4',
`output.${outputExt}`, `output.${outputExt}`,
@ -71,7 +78,44 @@ export async function run(
}) })
toast.message = '完成!' toast.message = '完成!'
toast.duration = 1500 toast.duration = 1000
await DownloadPackage.single(name, outputBlob) await DownloadPackage.single(name, outputBlob)
} }
export async function run(action: DownloadVideoAction) {
if (!ffmpeg.loaded) {
await loadFFmpeg()
}
const pages = lodash.chunk(
action.infos.flatMap(it => it.titledFragments),
2,
)
const { dashAudioExtension, dashFlacAudioExtension, dashVideoExtension } =
getComponentSettings<Options>('downloadVideo').options
for (let i = 0; i < pages.length; i++) {
const page = pages[i]
const [video, audio] = page
if (
!(
page.length === 2 &&
video.extension === dashVideoExtension &&
(audio.extension === dashAudioExtension || audio.extension === dashFlacAudioExtension)
)
) {
throw new Error('仅支持 DASH 格式视频和音频')
}
await single(
video.title,
video.url,
audio.url,
audio.extension === dashFlacAudioExtension,
i + 1,
pages.length,
)
}
}

View File

@ -1,11 +1,9 @@
import { Toast, ToastType } from '@/core/toast' import { Toast } from '@/core/toast'
import { PluginMetadata } from '@/plugins/plugin' import { PluginMetadata } from '@/plugins/plugin'
import { DownloadVideoOutput } from '../../../../components/video/download/types' import { DownloadVideoOutput } from '../../../../components/video/download/types'
import { run } from './handler' import { run } from './handler'
import { getComponentSettings } from '@/core/settings'
import type { Options } from '../../../../components/video/download'
const title = 'WASM 混流输出' export const title = 'WASM 混流输出'
const desc = '使用 WASM 在浏览器中下载并合并音视频' const desc = '使用 WASM 在浏览器中下载并合并音视频'
export const plugin: PluginMetadata = { export const plugin: PluginMetadata = {
@ -23,41 +21,11 @@ export const plugin: PluginMetadata = {
displayName: 'WASM', displayName: 'WASM',
description: `${desc},运行过程中请勿关闭页面,初次使用或清除缓存后需要加载约 30 MB 的 WASM 文件`, description: `${desc},运行过程中请勿关闭页面,初次使用或清除缓存后需要加载约 30 MB 的 WASM 文件`,
runAction: async action => { runAction: async action => {
const fragments = action.infos.flatMap(it => it.titledFragments)
const { dashAudioExtension, dashFlacAudioExtension, dashVideoExtension } =
getComponentSettings<Options>('downloadVideo').options
if (
fragments.length > 2 ||
(fragments.length === 2 &&
!(
fragments[0].extension === dashVideoExtension &&
(fragments[1].extension === dashAudioExtension ||
fragments[1].extension === dashFlacAudioExtension)
))
) {
Toast.error('仅支持单个视频下载', title).duration = 1500
} else {
const toast = Toast.info('正在加载', title)
try { try {
if (fragments.length === 2) { await run(action)
await run(
fragments[0].title,
toast,
fragments[0].url,
fragments[1].url,
fragments[1].extension === dashFlacAudioExtension,
) // [dash]
} else {
toast.type = ToastType.Error
toast.message = '仅支持 dash 格式视频下载'
toast.duration = 1500
}
} catch (error) { } catch (error) {
toast.close()
Toast.error(String(error), title) Toast.error(String(error), title)
} }
}
}, },
}) })
}) })