-
Notifications
You must be signed in to change notification settings - Fork 7
feat: 流媒体播放 #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
feat: 流媒体播放 #16
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
932daba
feat: 接入自建流媒体服务器(Subsonic/Jellyfin/Emby)
imsyy 65cf4b0
feat: 基础完善流媒体
imsyy e6cf3f6
fix: 凭证加密存储
imsyy 8a436b0
feat: 流媒体触底加载
imsyy 3c23dd2
fix: 修复流媒体设置跳转
imsyy b3f1c3b
fix: 尝试解决流媒体播放问题
imsyy 6ea570d
feat: 优化流媒体播放
imsyy 9644007
fix: 流媒体凭据落盘剥离与重登副作用修复
imsyy cac8258
fix: 流媒体歌词智能识别格式与心跳上报使用最新 token
imsyy e4b1775
fix: 格式化
imsyy 2bb9491
refactor: 流媒体封面分级、上报串行化与 store 整理
imsyy 0c62c04
fix: 格式化
imsyy da525bd
refactor: 播放模块拆分、非本地加载并行化与 PR review 修复
imsyy ab63f69
fix: 切歌时立即重置进度避免桌面歌词错位高亮
imsyy File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| /** | ||
| * 流媒体相关 IPC: | ||
| * - loadServers / saveServers:服务器配置持久化 | ||
| */ | ||
| import fs from "node:fs"; | ||
| import path from "node:path"; | ||
| import { app, ipcMain, safeStorage } from "electron"; | ||
| import { writeFileSync as atomicWriteSync } from "atomically"; | ||
| import { streamingLog } from "@main/utils/logger"; | ||
| import type { StreamingServerConfig } from "@shared/types/streaming"; | ||
|
|
||
| const STORAGE_FILE = path.join(app.getPath("userData"), "streaming.json"); | ||
|
|
||
| /** 持久化形态:密码加密、accessToken/userId 不持久化(每次会话重新登录) */ | ||
| interface PersistedServer extends Omit< | ||
| StreamingServerConfig, | ||
| "password" | "accessToken" | "userId" | ||
| > { | ||
| encryptedPassword: string; | ||
| } | ||
|
|
||
| interface PersistedState { | ||
| servers: PersistedServer[]; | ||
| activeServerId: string | null; | ||
| } | ||
|
|
||
| const readPersisted = (): PersistedState => { | ||
| try { | ||
| const raw = JSON.parse(fs.readFileSync(STORAGE_FILE, "utf-8")) as PersistedState; | ||
| if (!Array.isArray(raw?.servers)) return { servers: [], activeServerId: null }; | ||
| return { servers: raw.servers, activeServerId: raw.activeServerId ?? null }; | ||
| } catch { | ||
| return { servers: [], activeServerId: null }; | ||
| } | ||
| }; | ||
|
|
||
| const writePersisted = (data: PersistedState): void => { | ||
| try { | ||
| const dir = path.dirname(STORAGE_FILE); | ||
| if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); | ||
| atomicWriteSync(STORAGE_FILE, JSON.stringify(data, null, 2)); | ||
| } catch (err) { | ||
| streamingLog.error("写入 streaming.json 失败:", err); | ||
| } | ||
| }; | ||
|
|
||
| /** | ||
| * 加密密码 | ||
| * @param plain 明文密码 | ||
| * @returns 加密后的密码 | ||
| */ | ||
| const encryptPassword = (plain: string): string => { | ||
| if (!plain) return ""; | ||
| if (!safeStorage.isEncryptionAvailable()) { | ||
| return Buffer.from(plain, "utf-8").toString("base64"); | ||
| } | ||
| return safeStorage.encryptString(plain).toString("base64"); | ||
|
Comment on lines
+52
to
+57
|
||
| }; | ||
|
|
||
| /** | ||
| * 解密密码 | ||
| * @param encrypted 加密后的密码 | ||
| * @returns 明文密码 | ||
| */ | ||
| const decryptPassword = (encrypted: string): string => { | ||
| if (!encrypted) return ""; | ||
| try { | ||
| const buf = Buffer.from(encrypted, "base64"); | ||
| if (!safeStorage.isEncryptionAvailable()) { | ||
| return buf.toString("utf-8"); | ||
| } | ||
| return safeStorage.decryptString(buf); | ||
| } catch { | ||
| return ""; | ||
| } | ||
| }; | ||
|
|
||
| export const registerStreamingIpc = (): void => { | ||
| ipcMain.handle("streaming:loadServers", () => { | ||
| const persisted = readPersisted(); | ||
| const servers: StreamingServerConfig[] = persisted.servers.map((s) => ({ | ||
| id: s.id, | ||
| name: s.name, | ||
| type: s.type, | ||
| url: s.url, | ||
| username: s.username, | ||
| password: decryptPassword(s.encryptedPassword), | ||
| lastConnected: s.lastConnected, | ||
| })); | ||
| return { servers, activeServerId: persisted.activeServerId }; | ||
| }); | ||
|
|
||
| ipcMain.handle( | ||
| "streaming:saveServers", | ||
| (_e, payload: { servers: StreamingServerConfig[]; activeServerId: string | null }): void => { | ||
| const servers: PersistedServer[] = (payload?.servers ?? []).map((s) => ({ | ||
| id: s.id, | ||
| name: s.name, | ||
| type: s.type, | ||
| url: s.url, | ||
| username: s.username, | ||
| encryptedPassword: encryptPassword(s.password), | ||
| lastConnected: s.lastConnected, | ||
| })); | ||
| writePersisted({ servers, activeServerId: payload?.activeServerId ?? null }); | ||
| }, | ||
| ); | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.