Skip to content

Commit ba52e66

Browse files
committed
feat: 简繁搜索转换、HLS代理播放、空格键控制、托盘图标修复
- 添加简繁转换搜索功能 (opencc-js) - 添加 HLS m3u8 视频代理服务 - 添加视频播放器空格键暂停/播放 - 修复系统托盘图标路径问题 - 添加 asarUnpack 配置 - 修复 anime1.pw SSL 问题 (使用 curl fallback) - 添加 e2e 测试 (chinese-conversion, hls-video-playback, video-keyboard-control)
1 parent df400ee commit ba52e66

File tree

17 files changed

+30059
-45217
lines changed

17 files changed

+30059
-45217
lines changed

dist-electron/main/index.js

Lines changed: 27221 additions & 43344 deletions
Large diffs are not rendered by default.

dist-electron/main/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist-electron/preload/index.cjs

Lines changed: 122 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -18,219 +18,184 @@
1818
* 职责: 安全地暴露主进程 API 到渲染进程
1919
*/
2020

21-
const { contextBridge, ipcRenderer } = require('electron')
21+
const { contextBridge, ipcRenderer } = require("electron");
2222

2323
// IPC 频道白名单
2424
const validChannels = [
25-
// 窗口
26-
'window:minimize',
27-
'window:maximize',
28-
'window:close',
29-
'window:toggleFullscreen',
30-
'window:getState',
25+
"window:minimize",
26+
"window:maximize",
27+
"window:close",
28+
"window:toggleFullscreen",
29+
"window:getState",
3130

3231
// 番剧
33-
'anime:list',
34-
'anime:listWithProgress',
35-
'anime:detail',
36-
'anime:episodes',
37-
'anime:search',
38-
'anime:searchWithProgress',
39-
'anime:bangumi',
40-
'anime:video',
41-
'anime:video:proxy',
42-
'anime:cache:status',
43-
'anime:cache:refresh',
44-
'anime:pwEpisodes',
32+
"anime:list",
33+
"anime:listWithProgress",
34+
"anime:detail",
35+
"anime:episodes",
36+
"anime:search",
37+
"anime:searchWithProgress",
38+
"anime:bangumi",
39+
"anime:video",
40+
"anime:video:proxy",
41+
"anime:getHlsProxyUrl",
42+
"anime:cache:status",
43+
"anime:cache:refresh",
44+
"anime:pwEpisodes",
4545

4646
// 收藏
47-
'favorite:list',
48-
'favorite:batchStatus',
49-
'favorite:add',
50-
'favorite:remove',
51-
'favorite:check',
47+
"favorite:list",
48+
"favorite:batchStatus",
49+
"favorite:add",
50+
"favorite:remove",
51+
"favorite:check",
5252

5353
// 播放历史
54-
'history:list',
55-
'history:save',
56-
'history:progress',
57-
'history:clear',
58-
'history:delete',
59-
'history:byAnime',
60-
'history:batchProgress',
54+
"history:list",
55+
"history:save",
56+
"history:progress",
57+
"history:clear",
58+
"history:delete",
59+
"history:byAnime",
6160

6261
// 设置
63-
'settings:get',
64-
'settings:set',
65-
'settings:getAll',
62+
"settings:get",
63+
"settings:set",
64+
"settings:getAll",
6665

6766
// 下载
68-
'download:list',
69-
'download:add',
70-
'download:pause',
71-
'download:resume',
72-
'download:cancel',
73-
'download:onProgress',
74-
'download:getHistory',
67+
"download:list",
68+
"download:add",
69+
"download:pause",
70+
"download:resume",
71+
"download:cancel",
72+
"download:onProgress",
73+
"download:getHistory",
7574

7675
// 自动下载
77-
'autoDownload:getConfig',
78-
'autoDownload:updateConfig',
79-
'autoDownload:getStatus',
80-
'autoDownload:getHistory',
81-
'autoDownload:previewFilter',
82-
'autoDownload:runCheck',
76+
"autoDownload:getConfig",
77+
"autoDownload:updateConfig",
78+
"autoDownload:getStatus",
79+
"autoDownload:getHistory",
80+
"autoDownload:previewFilter",
81+
"autoDownload:runCheck",
8382

8483
// 系统
85-
'system:showItemInFolder',
86-
'system:openExternal',
84+
"system:showItemInFolder",
85+
"system:openExternal",
8786

8887
// 更新
89-
'update:check',
90-
'update:download',
91-
'update:install',
92-
'update:onAvailable',
93-
'update:onProgress',
94-
'update:onDownloaded',
95-
]
88+
"update:check",
89+
"update:download",
90+
"update:install",
91+
"update:onAvailable",
92+
"update:onProgress",
93+
"update:onDownloaded",
94+
];
9695

9796
// 安全的 IPC 调用
9897
const invoke = async (channel, ...args) => {
9998
if (validChannels.includes(channel)) {
100-
return ipcRenderer.invoke(channel, ...args)
99+
return ipcRenderer.invoke(channel, ...args);
101100
}
102-
throw new Error(`Invalid channel: ${channel}`)
103-
}
101+
throw new Error(`Invalid channel: ${channel}`);
102+
};
104103

105104
// 事件监听
106105
const on = (channel, callback) => {
107106
if (validChannels.includes(channel)) {
108-
ipcRenderer.on(channel, (_, ...args) => callback(...args))
107+
ipcRenderer.on(channel, (_, ...args) => callback(...args));
109108
}
110-
}
109+
};
111110

112-
// 完整的 API 对象
111+
// 暴露 API
113112
const api = {
114113
window: {
115-
minimize: () => invoke('window:minimize'),
116-
maximize: () => invoke('window:maximize'),
117-
close: () => invoke('window:close'),
118-
toggleFullscreen: () => invoke('window:toggleFullscreen'),
119-
getState: () => invoke('window:getState')
120-
},
121-
122-
app: {
123-
getInfo: () => {
124-
return {
125-
name: 'Anime1 Desktop',
126-
version: '0.3.0',
127-
electronVersion: process.versions.electron,
128-
platform: process.platform
129-
}
130-
}
114+
minimize: () => invoke("window:minimize"),
115+
maximize: () => invoke("window:maximize"),
116+
close: () => invoke("window:close"),
117+
toggleFullscreen: () => invoke("window:toggleFullscreen"),
118+
getState: () => invoke("window:getState"),
131119
},
132120

133121
anime: {
134-
getList: (params) => invoke('anime:list', params),
135-
getListWithProgress: (params) => invoke('anime:listWithProgress', params),
136-
getDetail: (params) => invoke('anime:detail', params),
137-
getEpisodes: (params) => invoke('anime:episodes', params),
138-
search: (params) => invoke('anime:search', params),
139-
searchWithProgress: (params) => invoke('anime:searchWithProgress', params),
140-
getBangumiInfo: (params) => invoke('anime:bangumi', params),
141-
extractVideo: (params) => invoke('anime:video', params),
142-
getVideoProxyUrl: (params) => invoke('anime:video:proxy', params),
143-
getCacheStatus: () => invoke('anime:cache:status'),
144-
refreshCache: () => invoke('anime:cache:refresh'),
145-
parsePwEpisodes: (params) => invoke('anime:pwEpisodes', params)
122+
getList: (params) => invoke("anime:list", params),
123+
getListWithProgress: (params) => invoke("anime:listWithProgress", params),
124+
getDetail: (params) => invoke("anime:detail", params),
125+
getEpisodes: (params) => invoke("anime:episodes", params),
126+
search: (params) => invoke("anime:search", params),
127+
searchWithProgress: (params) => invoke("anime:searchWithProgress", params),
128+
getBangumiInfo: (params) => invoke("anime:bangumi", params),
129+
extractVideo: (params) => invoke("anime:video", params),
130+
getVideoProxyUrl: (params) => invoke("anime:video:proxy", params),
131+
getHlsProxyUrl: (params) => invoke("anime:getHlsProxyUrl", params),
132+
getCacheStatus: () => invoke("anime:cache:status"),
133+
refreshCache: () => invoke("anime:cache:refresh"),
134+
parsePwEpisodes: (params) => invoke("anime:pwEpisodes", params),
146135
},
147136

148137
favorite: {
149-
getList: () => invoke('favorite:list'),
150-
batchStatus: (params) => invoke('favorite:batchStatus', params),
151-
add: (params) => invoke('favorite:add', params),
152-
remove: (params) => invoke('favorite:remove', params),
153-
check: (params) => invoke('favorite:check', params)
138+
getList: () => invoke("favorite:list"),
139+
batchStatus: (params) => invoke("favorite:batchStatus", params),
140+
add: (params) => invoke("favorite:add", params),
141+
remove: (params) => invoke("favorite:remove", params),
142+
check: (params) => invoke("favorite:check", params),
154143
},
155144

156145
history: {
157-
getList: (params) => invoke('history:list', params),
158-
save: (params) => invoke('history:save', params),
159-
getProgress: (params) => invoke('history:progress', params),
160-
clear: () => invoke('history:clear'),
161-
delete: (params) => invoke('history:delete', params),
162-
getByAnime: (params) => invoke('history:byAnime', params),
163-
batchProgress: (params) => invoke('history:batchProgress', params)
146+
getList: (params) => invoke("history:list", params),
147+
save: (params) => invoke("history:save", params),
148+
getProgress: (params) => invoke("history:progress", params),
149+
clear: () => invoke("history:clear"),
150+
delete: (params) => invoke("history:delete", params),
151+
getByAnime: (params) => invoke("history:byAnime", params),
152+
batchProgress: (params) => invoke("history:batchProgress", params),
164153
},
165154

166155
settings: {
167-
get: (params) => invoke('settings:get', params),
168-
set: (params) => invoke('settings:set', params),
169-
getAll: () => invoke('settings:getAll')
156+
get: (params) => invoke("settings:get", params),
157+
set: (params) => invoke("settings:set", params),
158+
getAll: () => invoke("settings:getAll"),
170159
},
171160

172161
download: {
173-
getList: () => invoke('download:list'),
174-
add: (params) => invoke('download:add', params),
175-
pause: (params) => invoke('download:pause', params),
176-
resume: (params) => invoke('download:resume', params),
177-
cancel: (params) => invoke('download:cancel', params),
178-
onProgress: (callback) => on('download:progress', callback)
162+
getList: () => invoke("download:list"),
163+
add: (params) => invoke("download:add", params),
164+
pause: (params) => invoke("download:pause", params),
165+
resume: (params) => invoke("download:resume", params),
166+
cancel: (params) => invoke("download:cancel", params),
167+
onProgress: (callback) => on("download:progress", callback),
179168
},
180169

181170
autoDownload: {
182-
getConfig: () => invoke('autoDownload:getConfig'),
183-
updateConfig: (params) => invoke('autoDownload:updateConfig', params),
184-
getStatus: () => invoke('autoDownload:getStatus'),
185-
getHistory: (params) => invoke('autoDownload:getHistory', params),
186-
previewFilter: (params) => invoke('autoDownload:previewFilter', params),
187-
runCheck: () => invoke('autoDownload:runCheck')
171+
getConfig: () => invoke("autoDownload:getConfig"),
172+
updateConfig: (params) => invoke("autoDownload:updateConfig", params),
173+
getStatus: () => invoke("autoDownload:getStatus"),
174+
getHistory: (params) => invoke("autoDownload:getHistory", params),
175+
previewFilter: (params) => invoke("autoDownload:previewFilter", params),
176+
runCheck: () => invoke("autoDownload:runCheck"),
188177
},
189178

190179
system: {
191-
showItemInFolder: (params) => invoke('system:showItemInFolder', params),
192-
openExternal: (params) => invoke('system:openExternal', params)
180+
showItemInFolder: (params) => invoke("system:showItemInFolder", params),
181+
openExternal: (params) => invoke("system:openExternal", params),
193182
},
194183

195184
update: {
196-
check: () => invoke('update:check'),
197-
download: () => invoke('update:download'),
198-
install: () => invoke('update:install'),
199-
200-
// 事件监听
201-
onAvailable: (callback) => on('update:onAvailable', callback),
202-
onProgress: (callback) => on('update:onProgress', callback),
203-
onDownloaded: (callback) => on('update:onDownloaded', callback),
204-
205-
// 测试 API
206-
getTestUpdate: () => ({
207-
hasUpdate: true,
208-
currentVersion: '0.3.0',
209-
latestVersion: '0.3.1',
210-
isPrerelease: false,
211-
releaseNotes: '测试更新 - 模拟的版本',
212-
downloadUrl: 'http://test-mock-update.dmg',
213-
publishedAt: new Date().toISOString()
214-
}),
215-
216-
setTestVersion: (version) => {
217-
console.log('[测试] 版本设置为:', version)
218-
return { success: true }
219-
},
220-
221-
clearTestMode: () => {
222-
console.log('[测试] 清除测试模式')
223-
return { success: true }
224-
},
225-
226-
triggerUpdateAvailable: () => {
227-
console.log('[测试] 触发 update-available 事件')
228-
return { success: true }
229-
},
230-
231-
getStatus: () => invoke('update:getStatus')
185+
check: () => invoke("update:check"),
186+
download: () => invoke("update:download"),
187+
install: () => invoke("update:install"),
188+
onAvailable: (callback) => on("update:onAvailable", callback),
189+
onProgress: (callback) => on("update:onProgress", callback),
190+
onDownloaded: (callback) => on("update:onDownloaded", callback),
232191
},
233-
}
192+
};
234193

235194
// 暴露到 window.api
236-
contextBridge.exposeInMainWorld('api', api)
195+
console.log("[Preload] Starting to expose API...");
196+
try {
197+
contextBridge.exposeInMainWorld("api", api);
198+
console.log("[Preload] API exposed successfully");
199+
} catch (error) {
200+
console.error("[Preload] Failed to expose API:", error);
201+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* 简繁体转换功能测试
3+
*
4+
* 测试搜索时简体/繁体字都能匹配到对应的番剧
5+
*/
6+
import { test, expect } from '../fixtures'
7+
8+
test.describe('简繁体搜索转换', () => {
9+
test('搜索应该返回结果', async ({ window }) => {
10+
const searchResult = await window.evaluate(async () => {
11+
return await window.api.anime.search({ keyword: '火', page: 1 })
12+
})
13+
14+
expect(searchResult).toBeDefined()
15+
expect(searchResult.success).toBe(true)
16+
expect(Array.isArray(searchResult.data.animeList)).toBe(true)
17+
})
18+
19+
test('搜索不存在的词应该返回空列表', async ({ window }) => {
20+
const searchResult = await window.evaluate(async () => {
21+
return await window.api.anime.search({ keyword: '完全不存在的番剧名称xyz123', page: 1 })
22+
})
23+
24+
expect(searchResult).toBeDefined()
25+
expect(searchResult.success).toBe(true)
26+
expect(Array.isArray(searchResult.data.animeList)).toBe(true)
27+
expect(searchResult.data.animeList.length).toBe(0)
28+
})
29+
30+
test('搜索应该返回分页信息', async ({ window }) => {
31+
const searchResult = await window.evaluate(async () => {
32+
return await window.api.anime.search({ keyword: '火', page: 1 })
33+
})
34+
35+
expect(searchResult).toBeDefined()
36+
expect(searchResult.success).toBe(true)
37+
38+
expect(searchResult.data.currentPage).toBeDefined()
39+
expect(searchResult.data.totalPages).toBeDefined()
40+
expect(searchResult.data.hasNext).toBeDefined()
41+
})
42+
})

0 commit comments

Comments
 (0)