dsh-music-player 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -5
- package/lib/client.js +28 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,9 +23,11 @@ DeepSeek Harness 本地音乐/小说播放插件。
|
|
|
23
23
|
|
|
24
24
|
## 截图
|
|
25
25
|
|
|
26
|
-

|
|
27
27
|
|
|
28
|
-

|
|
29
|
+
|
|
30
|
+

|
|
29
31
|
|
|
30
32
|

|
|
31
33
|
|
|
@@ -117,17 +119,17 @@ dsh plugin --profile <profile> add ./dsh-music-player-0.1.0.tgz
|
|
|
117
119
|
- 新歌:新歌速递(最新/内地/港台/欧美/韩国等)。
|
|
118
120
|
- 搜索:搜歌曲与歌单,带搜索历史(localStorage,最近 10 条)。
|
|
119
121
|
- **播放**:点击任意歌曲即可播放(同一播放条 + 频谱);VIP 标识显示在歌名后,行尾显示歌手名。
|
|
120
|
-
- **收藏**:播放条爱心按钮把当前在线曲目收藏到 QQ
|
|
122
|
+
- **收藏**:播放条爱心按钮把当前在线曲目收藏到 QQ 音乐「我喜欢」,已收藏歌曲爱心实时点亮。
|
|
121
123
|
- **续播**:在线播放进度(当前曲目 + 队列)刷新后自动恢复,点 ▶ 续播。
|
|
122
124
|
- 在线曲目不占本地曲库的 500 首上限,与本地/讲书完全隔离。
|
|
123
125
|
|
|
124
126
|
## AI 讲书
|
|
125
127
|
|
|
126
|
-
把本地 `.txt` 小说交给 AI
|
|
128
|
+
把本地 `.txt` 小说交给 AI 朗读。**AI 语音目前仅支持xiaomi提供方(限时免费),请在设置中配置好再使用此功能。**
|
|
127
129
|
|
|
128
130
|
### 前置
|
|
129
131
|
|
|
130
|
-
在 DSH 的模型设置里配置一个
|
|
132
|
+
在 DSH 的模型设置里配置一个xiaomi提供方(含 api key)。未配置时,小说列表会提示"未配置xiaomi提供方"。
|
|
131
133
|
|
|
132
134
|
### 使用
|
|
133
135
|
|
package/lib/client.js
CHANGED
|
@@ -710,6 +710,10 @@ window.__ModuleLoader__.load({
|
|
|
710
710
|
let shuffleQueue = [];
|
|
711
711
|
let shufflePos = -1;
|
|
712
712
|
let shuffleScopeKey = null;
|
|
713
|
+
// 在线 QQ 队列连续失败的跳过次数:某首歌因版权下架/拿不到地址而触发
|
|
714
|
+
// <audio> error 时自动跳到下一首;连续跳过次数达到队列长度(整列都试过)
|
|
715
|
+
// 即停止报错——且停止后不再 step,杜绝无限循环跳歌。成功播放(onPlay)清零。
|
|
716
|
+
let qqErrorSkipCount = 0;
|
|
713
717
|
function buildShuffleQueue(anchorId) {
|
|
714
718
|
const ids = activeIds();
|
|
715
719
|
// Fisher-Yates
|
|
@@ -778,7 +782,7 @@ window.__ModuleLoader__.load({
|
|
|
778
782
|
bookRestorePos = -1;
|
|
779
783
|
audio.src = track.url;
|
|
780
784
|
audio.load();
|
|
781
|
-
set({ currentId: id, currentName: track.name, currentArtists: track.artists ||
|
|
785
|
+
set({ currentId: id, currentName: track.name, currentArtists: track.artists || [], pendingId: null, pendingName: null, error: null, tocOpen: false, currentSection: '', qqFaved: false });
|
|
782
786
|
syncShufflePos();
|
|
783
787
|
loadEnvelope(id, track.url);
|
|
784
788
|
prefetchNext();
|
|
@@ -947,7 +951,7 @@ window.__ModuleLoader__.load({
|
|
|
947
951
|
// near-instant (server-side synthesis cache) so we don't flash a spinner
|
|
948
952
|
// at every chunk boundary. Only user-initiated plays show it.
|
|
949
953
|
const showBuffer = !silent;
|
|
950
|
-
set({ currentId: 'book:' + id, currentName: book.name, pendingId: null, pendingName: null, error: null, vizState: 'ok' });
|
|
954
|
+
set({ currentId: 'book:' + id, currentName: book.name, currentArtists: [], pendingId: null, pendingName: null, error: null, vizState: 'ok' });
|
|
951
955
|
// Load the chapter structure for the toc + the current-section label (the
|
|
952
956
|
// current section is derived from the chunk index once the meta arrives).
|
|
953
957
|
const startFrom = from;
|
|
@@ -1310,7 +1314,7 @@ window.__ModuleLoader__.load({
|
|
|
1310
1314
|
set({ duration: (bookTimeBase() + audio.duration) });
|
|
1311
1315
|
}
|
|
1312
1316
|
};
|
|
1313
|
-
const onPlay = () => { set({ playing: true, error: null }); acquireWakeLock(); };
|
|
1317
|
+
const onPlay = () => { qqErrorSkipCount = 0; set({ playing: true, error: null }); acquireWakeLock(); };
|
|
1314
1318
|
const onPause = () => { set({ playing: false }); savePlayback(); releaseWakeLock(); };
|
|
1315
1319
|
const onEnded = () => {
|
|
1316
1320
|
// A novel plays chunk-by-chunk: when a chunk ends, auto-advance to the
|
|
@@ -1359,6 +1363,17 @@ window.__ModuleLoader__.load({
|
|
|
1359
1363
|
}).catch(() => {});
|
|
1360
1364
|
}
|
|
1361
1365
|
} else {
|
|
1366
|
+
// 在线 QQ 队列:某首歌因版权下架/拿不到播放地址而加载失败时,自动
|
|
1367
|
+
// 跳到下一首继续播放(不因单曲失败中断整个队列);只有队列里就这一
|
|
1368
|
+
// 首、或连续跳过次数已达队列长度(整列都试过)才停下报错——且停止后
|
|
1369
|
+
// 不再 step,杜绝无限循环跳歌。
|
|
1370
|
+
const isQQTrack = store.currentId !== null && String(store.currentId).startsWith('qq:');
|
|
1371
|
+
const qLen = (store.qqQueue || []).length;
|
|
1372
|
+
if (isQQTrack && qLen > 1 && qqErrorSkipCount < qLen) {
|
|
1373
|
+
qqErrorSkipCount++;
|
|
1374
|
+
step(1);
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1362
1377
|
set({ error: '音频加载或解码失败', playing: false });
|
|
1363
1378
|
}
|
|
1364
1379
|
};
|
|
@@ -1432,6 +1447,7 @@ window.__ModuleLoader__.load({
|
|
|
1432
1447
|
// (togglePlay), and the readout is pinned to the restored values.
|
|
1433
1448
|
set({
|
|
1434
1449
|
currentId: track.id, currentName: track.name,
|
|
1450
|
+
currentArtists: track.artists || [],
|
|
1435
1451
|
position: pos, duration: savedDur,
|
|
1436
1452
|
pendingId: null, pendingName: null, error: null, scope,
|
|
1437
1453
|
});
|
|
@@ -1475,7 +1491,7 @@ window.__ModuleLoader__.load({
|
|
|
1475
1491
|
// <audio> element here (avoiding the Chromium 'getTopURL' quirk on
|
|
1476
1492
|
// refresh); togglePlay loads + seeks the chunk when the user resumes.
|
|
1477
1493
|
set({
|
|
1478
|
-
currentId: 'book:' + book.id, currentName: book.name,
|
|
1494
|
+
currentId: 'book:' + book.id, currentName: book.name, currentArtists: [],
|
|
1479
1495
|
position: base + pos, duration: base + (Number.isFinite(audio.duration) ? audio.duration : 0),
|
|
1480
1496
|
pendingId: null, pendingName: null, error: null, playing: false,
|
|
1481
1497
|
bookBuffering: false, bookBufferingSilent: false, bookError: '', bookBufferingSince: 0,
|
|
@@ -1563,7 +1579,8 @@ window.__ModuleLoader__.load({
|
|
|
1563
1579
|
tracks: result.tracks || [], books: result.books || [],
|
|
1564
1580
|
count: result.count || 0, loading: false, error: null,
|
|
1565
1581
|
});
|
|
1566
|
-
restoreLatest
|
|
1582
|
+
// 换目录只刷新列表,不调用 restoreLatest:否则播放中的在线 QQ 曲目会
|
|
1583
|
+
// 触发 restorePlayback 把 tab 强制切回「QQ音乐」(选完目录被跳回的问题)。
|
|
1567
1584
|
} else {
|
|
1568
1585
|
set({ loading: false, error: (result && result.error) || '设置目录失败' });
|
|
1569
1586
|
}
|
|
@@ -1975,6 +1992,9 @@ window.__ModuleLoader__.load({
|
|
|
1975
1992
|
}
|
|
1976
1993
|
}
|
|
1977
1994
|
|
|
1995
|
+
// 在线 QQ 面板的「上次所在层」只在本次会话首次挂载时恢复一次,避免切 tab
|
|
1996
|
+
// (QQ 音乐 ↔ 本地音乐/AI讲书)重新挂载时又被 localStorage 里的旧层拉回播放列表页。
|
|
1997
|
+
let qqUiRestored = false;
|
|
1978
1998
|
function QQOnlinePanel() {
|
|
1979
1999
|
const s = useStore();
|
|
1980
2000
|
const [loggedIn, setLoggedIn] = useState(s.qqLoggedIn || false);
|
|
@@ -2066,7 +2086,8 @@ window.__ModuleLoader__.load({
|
|
|
2066
2086
|
// 默认落在「我的歌单」tab:登录态下加载我的歌单;未登录时主 UI 不显示。
|
|
2067
2087
|
loadMine();
|
|
2068
2088
|
loadRecommended();
|
|
2069
|
-
|
|
2089
|
+
// 仅本次会话首次挂载时恢复上次所在层;之后切 tab 重挂不再拉回旧层。
|
|
2090
|
+
if (!qqUiRestored) { qqUiRestored = true; restoreUi(loadUi()); }
|
|
2070
2091
|
// 点击搜索框外时关闭历史下拉。
|
|
2071
2092
|
const onDocClick = (e) => { if (histRef.current !== null && !histRef.current.contains(e.target)) setHistOpen(false); };
|
|
2072
2093
|
document.addEventListener('mousedown', onDocClick);
|
|
@@ -3034,7 +3055,7 @@ window.__ModuleLoader__.load({
|
|
|
3034
3055
|
if (track !== null) {
|
|
3035
3056
|
audio.src = track.url;
|
|
3036
3057
|
audio.load();
|
|
3037
|
-
set({ currentId: intent.id, currentName: track.name, error: null, scope: { kind: 'library' } });
|
|
3058
|
+
set({ currentId: intent.id, currentName: track.name, currentArtists: track.artists || [], error: null, scope: { kind: 'library' } });
|
|
3038
3059
|
loadEnvelope(intent.id, track.url);
|
|
3039
3060
|
prefetchNext();
|
|
3040
3061
|
savePlayback();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-music-player",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "DeepSeek Harness 本地音乐 + AI 讲书插件:Host 扫描音乐目录并以 HTTP 流式提供音频、解析 .txt 小说结构并经 MiMo TTS 合成朗读,浏览器侧提供播放条/播放面板/章节目录跳转/多声音选择/实时频谱,并注册 music_play 模型工具",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|