Skip to content

Commit 0ee5657

Browse files
committed
feat: add loading subtitle prompt in the video sheet
1 parent bdbc818 commit 0ee5657

File tree

13 files changed

+252
-127
lines changed

13 files changed

+252
-127
lines changed

electron-builder.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,8 @@ releaseInfo:
7272
修复初次播放视频时无法显示设置的问题
7373
修复自动匹配字幕无法显示的问题
7474
添加开关 mini 进度条设置
75+
修复自动匹配字幕导入报错
76+
修复加载视频时出现报错
77+
修复重置 App 后字幕无法加载
78+
修复弹幕来源取消勾选后,下次播放视频无法生效的问题
79+
添加加载字幕提示

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"@radix-ui/react-tabs": "^1.1.0",
6464
"@radix-ui/react-toast": "^1.2.1",
6565
"@radix-ui/react-toggle": "^1.1.0",
66+
"@radix-ui/react-tooltip": "^1.1.8",
6667
"@sentry/react": "^8.42.0",
6768
"@suemor/xgplayer": "^3.0.21",
6869
"@tanstack/react-query": "^5.62.9",

pnpm-lock.yaml

Lines changed: 36 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/main/lib/ffmpeg.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import path from 'node:path'
33

44
import ffmpegPath from '@ffmpeg-installer/ffmpeg'
55
import ffprobePath from '@ffprobe-installer/ffprobe'
6-
import { screenshotsPath, subtitlesPath } from '@main/constants/app'
6+
import { createStorageFolder, screenshotsPath, subtitlesPath } from '@main/constants/app'
77
import ffmpeg from 'fluent-ffmpeg'
88
import { nanoid } from 'nanoid'
99

@@ -14,6 +14,7 @@ export default class FFmpeg {
1414
ffmpeg: ffmpeg.FfmpegCommand
1515

1616
constructor(inputPath: string) {
17+
createStorageFolder()
1718
this.ffmpeg = ffmpeg(inputPath)
1819
}
1920

src/main/lib/utils.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
import path from 'node:path'
2+
13
import { getRendererHandlers } from '@main/windows/setting'
24
import logger from 'electron-log'
35

6+
import FFmpeg from './ffmpeg'
7+
import { getFilePathFromProtocolURL } from './protocols'
8+
49
export async function sleep(ms: number) {
510
return new Promise((resolve) => setTimeout(resolve, ms))
611
}
@@ -41,3 +46,20 @@ export function quickLaunchViaVideo() {
4146
getRendererHandlers()?.importAnime.send({ path: filePath })
4247
}
4348
}
49+
50+
export async function coverSubtitleToAss(targetPath: string) {
51+
const filePath = getFilePathFromProtocolURL(targetPath)
52+
const extName = path.extname(filePath)
53+
if (!extName) {
54+
return
55+
}
56+
if (extName === '.ass' || extName === '.ssa') {
57+
return {
58+
fileName: path.basename(filePath),
59+
filePath,
60+
}
61+
}
62+
const ffmepg = new FFmpeg(filePath)
63+
const outPutPath = await ffmepg.coverToAssSubtitle()
64+
return outPutPath
65+
}

src/main/tipc/player.ts

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { BilibiliXmlDanmakus } from '@main/lib/danmaku'
66
import { parseBilibiliDanmaku } from '@main/lib/danmaku'
77
import FFmpeg from '@main/lib/ffmpeg'
88
import { getFilePathFromProtocolURL } from '@main/lib/protocols'
9+
import { coverSubtitleToAss } from '@main/lib/utils'
910
import { showFileSelectionDialog } from '@main/modules/showDialog'
1011
import { calculateFileHashByBuffer } from '@renderer/lib/calc-file-hash'
1112
import { dialog } from 'electron'
@@ -139,21 +140,7 @@ export const playerRoute = {
139140
if (!filePath) {
140141
return
141142
}
142-
const extName = path.extname(filePath)
143-
if (!extName) {
144-
return
145-
}
146-
if (extName === '.ass' || extName === '.ssa') {
147-
return {
148-
fileName: path.basename(filePath),
149-
filePath,
150-
}
151-
}
152-
153-
const ffmepg = new FFmpeg(filePath)
154-
const outPutPath = ffmepg.coverToAssSubtitle()
155-
156-
return outPutPath
143+
return coverSubtitleToAss(filePath)
157144
}),
158145
getSubtitlesIntroFromAnime: t.procedure.input<{ path: string }>().action(async ({ input }) => {
159146
const ffmpeg = new FFmpeg(getFilePathFromProtocolURL(input.path))
@@ -181,6 +168,7 @@ export const playerRoute = {
181168
fileName: file,
182169
filePath: path.join(directoryPath, file),
183170
}))
171+
184172
return matchedFiles
185173
}),
186174
immportDanmakuFile: t.procedure.action(async () => {

src/main/tipc/utils.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
import path from 'node:path'
2-
31
import { getFilePathFromProtocolURL } from '@main/lib/protocols'
2+
import { coverSubtitleToAss } from '@main/lib/utils'
43

54
import { t } from './_instance'
65

76
export const utilsRoute = {
87
getFilePathFromProtocolURL: t.procedure.input<{ path: string }>().action(async ({ input }) => {
98
return getFilePathFromProtocolURL(input.path)
109
}),
11-
getFileNameFromPath: t.procedure.input<{ path: string }>().action(async ({ input }) => {
12-
return path.basename(input.path)
10+
coverSubtitleToAss: t.procedure.input<{ path: string }>().action(async ({ input }) => {
11+
return coverSubtitleToAss(input.path)
1312
}),
1413
}

src/renderer/src/atoms/player.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export const initialMatchedVideo = {
3535
animeId: 0,
3636
}
3737

38-
export const playerSettingSheetAtom = atom(false)
38+
export const playerSettingSheetAtom = atomWithReset(false)
3939

4040
export type MatchedVideoType = typeof initialMatchedVideo
4141

@@ -52,11 +52,13 @@ export const useClearPlayingVideo = () => {
5252
const resetVideo = useResetAtom(videoAtom)
5353
const resetProgress = useResetAtom(loadingDanmuProgressAtom)
5454
const resetCurrentMatchedVideo = useResetAtom(currentMatchedVideoAtom)
55+
const resetPlayerSettingSheet = useResetAtom(playerSettingSheetAtom)
5556

5657
return () => {
5758
resetVideo()
5859
resetProgress()
5960
resetCurrentMatchedVideo()
61+
resetPlayerSettingSheet()
6062
}
6163
}
6264

src/renderer/src/components/modules/player/loading/hooks.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -211,19 +211,16 @@ export const useDanmakuData = () => {
211211

212212
const handleIsSelected = () => {
213213
// 如果历史记录中有选中的弹幕库,就返回 true
214-
if (historyDanmaku?.selected) {
215-
return true
216-
}
217214

218-
if (!related.url.includes('bilibili')) {
219-
return true
215+
if (related.url.includes('bilibili')) {
216+
return (
217+
related.url ===
218+
thirdPartyDanmakuUrlData?.find((item) => item.url.includes('bilibili'))?.url
219+
)
220220
}
221+
return historyDanmaku?.selected
221222

222223
// bilibili 弹幕库感觉有重复的弹幕,目前只默认加载一个 bilibili 弹幕库
223-
return (
224-
related.url ===
225-
thirdPartyDanmakuUrlData?.find((item) => item.url.includes('bilibili'))?.url
226-
)
227224
}
228225
// 使用弹幕缓存
229226
if (historyDanmaku && !history?.newBangumi) {
@@ -301,7 +298,11 @@ export const useDanmakuData = () => {
301298
}
302299

303300
// 官方弹幕库和第三方弹幕库都加载成功后,返回所有弹幕数据
304-
if (!onlyLoadDandanplayDanmaku && results.every((result) => result.data !== undefined)) {
301+
if (
302+
!onlyLoadDandanplayDanmaku &&
303+
results.every((result) => result.data !== undefined) &&
304+
thirdPartyResult.length === thirdPartyDanmakuUrlData.length
305+
) {
305306
return [dandanplayDanmakuData, ...thirdPartyDanmakuData, ...manualResult]
306307
}
307308

src/renderer/src/components/modules/player/setting/items/subtitle/SubtitleImport.tsx

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
import { playerSettingSheetAtom, videoAtom } from '@renderer/atoms/player'
32
import { jotaiStore } from '@renderer/atoms/store'
43
import {
@@ -24,12 +23,12 @@ import { useSubtitle } from './hooks'
2423

2524
export const SubtitleImport = () => {
2625
const player = usePlayerInstance()
27-
const { subtitlesData, fetchSubtitleBody } = useSubtitle()
26+
const { subtitlesData, fetchSubtitleBody, isLoadingEmbeddedSubtitle } = useSubtitle()
2827
const { toast } = useToast()
2928
const { hash } = useAtomValue(videoAtom)
3029
const fileInputRef = useRef<HTMLInputElement | null>(null)
3130
const { data: defaultValue, isFetching } = useQuery({
32-
queryKey: ['subtitlesDefaultValue', hash],
31+
queryKey: ['subtitlesDefaultValue', hash, isLoadingEmbeddedSubtitle],
3332
queryFn: async () => {
3433
const history = await db.history.get(hash)
3534
return history?.subtitles?.defaultId?.toString() ?? '-1'
@@ -48,33 +47,47 @@ export const SubtitleImport = () => {
4847
}
4948
}, [player])
5049

51-
const importSubtitleFromBrowser = (e: ChangeEvent<HTMLInputElement>) => {
50+
const importSubtitleFromBrowser = async (e: ChangeEvent<HTMLInputElement>) => {
5251
const changeEvent = e as unknown as ChangeEvent<HTMLInputElement>
5352
const file = changeEvent.target?.files?.[0]
5453

5554
if (!file) {
5655
return
5756
}
5857
const url = URL.createObjectURL(file)
59-
fetchSubtitleBody({ path: url, fileName: file.name })
60-
toast({
61-
title: '导入字幕成功',
62-
duration: 1500,
63-
})
64-
jotaiStore.set(playerSettingSheetAtom, false)
58+
try {
59+
await fetchSubtitleBody({ path: url, fileName: file.name })
60+
toast({
61+
title: '导入字幕成功',
62+
duration: 1500,
63+
})
64+
jotaiStore.set(playerSettingSheetAtom, false)
65+
} catch {
66+
toast({
67+
title: '导入字幕失败',
68+
duration: 1500,
69+
})
70+
}
6571
}
6672

6773
const importSubtitleFromClient = async () => {
6874
const subtitlePath = await tipcClient?.importSubtitle()
6975
if (!subtitlePath) {
7076
return
7177
}
72-
fetchSubtitleBody({ path: subtitlePath.filePath, fileName: subtitlePath.fileName })
73-
toast({
74-
title: '导入字幕成功',
75-
duration: 1500,
76-
})
77-
jotaiStore.set(playerSettingSheetAtom, false)
78+
try {
79+
await fetchSubtitleBody({ path: subtitlePath.filePath, fileName: subtitlePath.fileName })
80+
toast({
81+
title: '导入字幕成功',
82+
duration: 1500,
83+
})
84+
jotaiStore.set(playerSettingSheetAtom, false)
85+
} catch {
86+
toast({
87+
title: '导入字幕失败',
88+
duration: 1500,
89+
})
90+
}
7891
}
7992

8093
if (!defaultValue || isFetching) {
@@ -85,17 +98,20 @@ export const SubtitleImport = () => {
8598
<Select
8699
defaultValue={defaultValue.toString()}
87100
onValueChange={(id) => fetchSubtitleBody({ id: +id })}
101+
disabled={isLoadingEmbeddedSubtitle}
88102
>
89103
<SelectTrigger className="w-[200px]">
90104
<SelectValue placeholder="选中字幕" />
91105
</SelectTrigger>
92106
<SelectContent>
93107
<SelectGroup>
94-
<SelectItem value={'-1'}>关闭</SelectItem>
108+
<SelectItem value={'-1'}>
109+
{isLoadingEmbeddedSubtitle ? '正在加载字幕中...' : '关闭'}
110+
</SelectItem>
95111
{subtitlesData?.tags?.map((subtitle) => {
96112
return (
97113
<SelectItem value={subtitle.id.toString()} key={subtitle.id}>
98-
{subtitle.title}
114+
{isLoadingEmbeddedSubtitle ? '正在加载字幕中...' : subtitle.title}
99115
</SelectItem>
100116
)
101117
})}
@@ -124,4 +140,4 @@ export const SubtitleImport = () => {
124140
)}
125141
</>
126142
)
127-
}
143+
}

0 commit comments

Comments
 (0)