Skip to content

Commit

Permalink
feat: 简单实现下弹幕导出为字幕文件
Browse files Browse the repository at this point in the history
  • Loading branch information
WhiteMinds committed Nov 2, 2022
1 parent 54b6a8f commit 3f66318
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 11 deletions.
3 changes: 2 additions & 1 deletion packages/http-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"lodash": "^4.17.21",
"morgan": "^1.10.0",
"ramda": "^0.28.0",
"steno": "^1.0.0"
"steno": "^1.0.0",
"subtitle": "^4.2.1"
},
"devDependencies": {
"@types/cors": "^2.8.12",
Expand Down
8 changes: 8 additions & 0 deletions packages/http-server/src/routes/api_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,14 @@ export namespace API {

export type Resp = RecordExtraData
}

export namespace createRecordSRT {
export interface Args {
id: RecordModel['id']
}

export type Resp = string
}
}

export interface UpdateRecorder {
Expand Down
64 changes: 64 additions & 0 deletions packages/http-server/src/routes/record.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { API } from './api_types'
import { createPagedResultGetter, getNumberFromQuery } from './utils'
import * as db from '../db'
import { assertStringType } from '../utils'
import { parseSync, stringifySync } from 'subtitle'
import { RecordExtraData } from '@autorecord/manager'

const router = Router()

Expand Down Expand Up @@ -77,4 +79,66 @@ router.route('/records/:id/extra_data').get(async (req, res) => {
})
})

router.route('/records/:id/srt').post(async (req, res) => {
const { id } = req.params
const record = db.getRecord(id)
if (record == null) {
res.json({ payload: null }).status(404)
return
}

const folder = path.dirname(record.savePath)
const name = path.basename(record.savePath, path.extname(record.savePath))
const extraDataPath = path.join(folder, name + '.json')
if (!fs.existsSync(extraDataPath)) {
res.json({ payload: null }).status(404)
return
}

// 感觉不用做什么优化,这里直接覆盖旧文件
const srtPath = path.join(folder, name + '.srt')
await genSRTFile(extraDataPath, srtPath)

res.json({
// 考虑到服务端安全,这里就先只返回 filename
payload: name + '.srt',
})
})

// ass 看起来只有序列化和反序列化的库(如 ass-compiler),没有支持帮助排列弹幕的库,
// 要自己实现,成本较高。所以先只简单实现个 srt 的,后面有需要的话再加个 ass 的版本。
export async function genSRTFile(
extraDataPath: string,
srtPath: string
): Promise<void> {
// TODO: 这里要不要考虑用 RecordExtraDataController 去操作?
const buffer = await fs.promises.readFile(extraDataPath)
const recordExtraData = JSON.parse(buffer.toString()) as RecordExtraData

const parsedSRT = parseSync('')

recordExtraData.messages.forEach((msg) => {
switch (msg.type) {
case 'comment':
const start = msg.timestamp - recordExtraData.meta.recordStartTimestamp
// TODO: 先简单写个固定值
const life = 4500
parsedSRT.push({
type: 'cue',
data: {
start: start,
end: start + life,
text: msg.text,
},
})
break
}
})

await fs.promises.writeFile(
srtPath,
stringifySync(parsedSRT, { format: 'SRT' })
)
}

export { router }
1 change: 1 addition & 0 deletions packages/manager/src/record_extra_data_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface RecordExtraData {
meta: {
title?: string
recordStartTimestamp: number
// TODO: 要再加个 video width / height,给之后可能会做的 ass 使用
}
/** 这个数组预期上是一个根据 timestamp 排序的有序数组,方便做一些时间段查询 */
messages: Message[]
Expand Down
10 changes: 10 additions & 0 deletions packages/web/src/services/LARServerService/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,15 @@ async function getRecordExtraData(
return resp.data.payload
}

async function createRecordSRT(
args: API.createRecordSRT.Args
): Promise<API.createRecordSRT.Resp> {
const resp = await requester.post<{ payload: API.createRecordSRT.Resp }>(
`/records/${args.id}/srt`
)
return resp.data.payload
}

async function getRecordVideoURL(args: { id: string }): Promise<string> {
return `${baseURL}/records/${args.id}/video`
}
Expand All @@ -135,6 +144,7 @@ export const LARServerService = {
updateManager,
getRecords,
getRecordExtraData,
createRecordSRT,
getRecordVideoURL,

getServerMessages,
Expand Down
22 changes: 17 additions & 5 deletions packages/web/src/views/Records/index.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
<template>
<div>
录像历史:
<div v-for="record in records">
{{ record.savePath }}
<router-link :to="{ name: RouteNames.Player, query: { id: record.id } }">
<button>play</button>
</router-link>
<div v-for="record in records" class="flex">
<div class="flex-auto truncate" :title="record.savePath">
{{ record.savePath }}
</div>
<div class="flex-shrink-0 px-2 space-x-2">
<router-link
:to="{ name: RouteNames.Player, query: { id: record.id } }"
>
<button>play</button>
</router-link>
<button @click="genSRT(record.id)">生成 srt 字幕</button>
</div>
</div>
</div>
</template>
Expand Down Expand Up @@ -34,4 +41,9 @@ onMounted(async () => {
console.log(res)
records.value = res.items
})
const genSRT = async (id: string) => {
const file = await LARServerService.createRecordSRT({ id })
console.log('gen SRT successful', file)
}
</script>
5 changes: 3 additions & 2 deletions packages/web/src/views/SideMenuLayout/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@
</template>
播放器
</MenuItem>
<MenuItem :route-name="RouteNames.RecordProcessing">
<!-- TODO: 原本预期上有一个录播处理的功能来做一些剪辑、弹幕合成之类的操作,但是比较复杂,先放着 -->
<!-- <MenuItem :route-name="RouteNames.RecordProcessing">
<template #icon="{ svgProps }">
<IconVideo v-bind="svgProps" />
</template>
录播处理
</MenuItem>
</MenuItem> -->
<MenuItem :route-name="RouteNames.Settings">
<template #icon="{ svgProps }">
<IconSettings v-bind="svgProps" />
Expand Down
47 changes: 44 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ __metadata:
nodemon: ^2.0.20
ramda: ^0.28.0
steno: ^1.0.0
subtitle: ^4.2.1
ts-node: ^10.9.1
typescript: ^4.8.3
languageName: unknown
Expand Down Expand Up @@ -1747,6 +1748,15 @@ __metadata:
languageName: node
linkType: hard

"@types/multipipe@npm:^3.0.0":
version: 3.0.0
resolution: "@types/multipipe@npm:3.0.0"
dependencies:
"@types/node": "*"
checksum: 633cbf626bb6962cfecf3a3c61dcd69c2fd75a2d8e51b182d0b4336572a7b40a81947e413e946c61ca78abffc76a2d73b0843a25d84f61cd8bcbd97e56feca62
languageName: node
linkType: hard

"@types/node@npm:*":
version: 18.7.12
resolution: "@types/node@npm:18.7.12"
Expand Down Expand Up @@ -4127,6 +4137,15 @@ __metadata:
languageName: node
linkType: hard

"duplexer2@npm:^0.1.2":
version: 0.1.4
resolution: "duplexer2@npm:0.1.4"
dependencies:
readable-stream: ^2.0.2
checksum: 744961f03c7f54313f90555ac20284a3fb7bf22fdff6538f041a86c22499560eb6eac9d30ab5768054137cb40e6b18b40f621094e0261d7d8c35a37b7a5ad241
languageName: node
linkType: hard

"duplexer3@npm:^0.1.4":
version: 0.1.5
resolution: "duplexer3@npm:0.1.5"
Expand Down Expand Up @@ -6732,6 +6751,16 @@ __metadata:
languageName: node
linkType: hard

"multipipe@npm:^4.0.0":
version: 4.0.0
resolution: "multipipe@npm:4.0.0"
dependencies:
duplexer2: ^0.1.2
object-assign: ^4.1.0
checksum: 5a494ec2ce5bfdb389882ca595e3c4a33cae6c90dad879db2e3aa9a94484d8b164b0fb7b58ccf7593ae7e8c6213fd3f53a736b2c98e4f14c5ed1d38debc33f98
languageName: node
linkType: hard

"mute-stream@npm:0.0.8, mute-stream@npm:~0.0.4":
version: 0.0.8
resolution: "mute-stream@npm:0.0.8"
Expand Down Expand Up @@ -7113,7 +7142,7 @@ __metadata:
languageName: node
linkType: hard

"object-assign@npm:^4":
"object-assign@npm:^4, object-assign@npm:^4.1.0":
version: 4.1.1
resolution: "object-assign@npm:4.1.1"
checksum: fcc6e4ea8c7fe48abfbb552578b1c53e0d194086e2e6bbbf59e0a536381a292f39943c6e9628af05b5528aa5e3318bb30d6b2e53cadaf5b8fe9e12c4b69af23f
Expand Down Expand Up @@ -8053,7 +8082,7 @@ __metadata:
languageName: node
linkType: hard

"readable-stream@npm:~2.3.6":
"readable-stream@npm:^2.0.2, readable-stream@npm:~2.3.6":
version: 2.3.7
resolution: "readable-stream@npm:2.3.7"
dependencies:
Expand Down Expand Up @@ -8647,7 +8676,7 @@ __metadata:
languageName: node
linkType: hard

"split2@npm:^3.0.0":
"split2@npm:^3.0.0, split2@npm:^3.2.2":
version: 3.2.2
resolution: "split2@npm:3.2.2"
dependencies:
Expand Down Expand Up @@ -8818,6 +8847,18 @@ __metadata:
languageName: node
linkType: hard

"subtitle@npm:^4.2.1":
version: 4.2.1
resolution: "subtitle@npm:4.2.1"
dependencies:
"@types/multipipe": ^3.0.0
multipipe: ^4.0.0
split2: ^3.2.2
strip-bom: ^4.0.0
checksum: 0aa6ff70bd039345e9cd810d547f0536dccf12092a1fbda0c34060a875a0793de97de9e54af8855877247677b345a9b8f62b1671289c52402cd170c449ba363b
languageName: node
linkType: hard

"sumchecker@npm:^3.0.1":
version: 3.0.1
resolution: "sumchecker@npm:3.0.1"
Expand Down

0 comments on commit 3f66318

Please sign in to comment.