Skip to content

Commit 0a52ab5

Browse files
feat(plugin): add winddrift.splayerlrc-classisland v1.0.3
1 parent f1f2088 commit 0a52ab5

1 file changed

Lines changed: 162 additions & 0 deletions

File tree

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/**
2+
* @name ClassIsland 联动
3+
* @id winddrift.splayerlrc-classisland
4+
* @version 1.0.3
5+
* @author imsyy & WindDrift
6+
* @homepage https://github.com/WindDrift/SPlayerLRC-In-ClassIsland
7+
* @type control
8+
* @grant network
9+
* @apiLevel 2
10+
* @description 把当前歌词推送到 ClassIsland 主界面
11+
* @updateUrl https://raw.githubusercontent.com/WindDrift/SPlayerLRC-In-ClassIsland/main/SPlayerLRC-In-ClassIsland.js
12+
*/
13+
splayer.register({
14+
events: ["trackChange", "lyricChange", "lineChange"],
15+
settings: [
16+
{
17+
key: "port",
18+
type: "number",
19+
label: "端口",
20+
description: "ClassIsland 歌词组件监听的本地端口",
21+
default: 50063,
22+
min: 1024,
23+
max: 65535,
24+
},
25+
{
26+
key: "showTranslation",
27+
type: "switch",
28+
label: "显示翻译",
29+
description: "当歌词行包含翻译时,将翻译显示在副行",
30+
default: true,
31+
},
32+
{
33+
key: "showNextLine",
34+
type: "switch",
35+
label: "无翻译时显示下一行",
36+
description: "当没有翻译可显示时,将下一行歌词显示在副行",
37+
default: true,
38+
},
39+
{
40+
key: "skipBackgroundLyrics",
41+
type: "switch",
42+
label: "跳过背景歌词",
43+
description: "开启后,背景歌词不会显示在 ClassIsland 上,但其所属主歌词仍会正常显示",
44+
default: false,
45+
},
46+
{
47+
key: "overlapDuetAsExtra",
48+
type: "switch",
49+
label: "重叠对唱显示在副行",
50+
description: "开启后,与主歌词时间重叠的对唱歌词会显示在副行,主歌词保持显示在主行",
51+
default: true,
52+
},
53+
],
54+
});
55+
56+
const post = (lyric, extra) => {
57+
const port = splayer.getSetting("port") || 50063;
58+
splayer
59+
.request(`http://127.0.0.1:${port}/component/lyrics/lyrics/`, {
60+
method: "POST",
61+
headers: { "Content-Type": "application/json" },
62+
body: JSON.stringify({ lyric, extra }),
63+
})
64+
.catch(() => {});
65+
};
66+
67+
/** 一行歌词 → 纯文本 */
68+
const lineText = (line) => (line && line.words ? line.words.map((w) => w.word).join("") : "");
69+
70+
/** 向前查找最近的主歌词行索引(用于背景歌词) */
71+
const findPrevMainLineIndex = (lines, from) => {
72+
for (let i = from; i >= 0; i--) {
73+
if (!lines[i].isBG) return i;
74+
}
75+
return -1;
76+
};
77+
78+
/** 查找与当前行时间重叠的最近主歌词行索引(用于对唱) */
79+
const findOverlappingMainLineIndex = (lines, from) => {
80+
const cur = lines[from];
81+
for (let i = from - 1; i >= 0; i--) {
82+
const line = lines[i];
83+
if (line.isBG || line.isDuet) continue;
84+
if (line.endTime > cur.startTime) return i;
85+
}
86+
return -1;
87+
};
88+
89+
/** 向后查找下一行应显示的歌词行索引(开启跳过时将跳过背景歌词行) */
90+
const findNextLineIndex = (lines, from, skipBG) => {
91+
for (let i = from + 1; i < lines.length; i++) {
92+
if (!skipBG || !lines[i].isBG) return i;
93+
}
94+
return -1;
95+
};
96+
97+
let lines = [];
98+
99+
splayer.player.on("trackChange", ({ track }) => {
100+
if (track) post(track.title, track.artists);
101+
});
102+
103+
splayer.player.on("lyricChange", ({ lines: ls }) => {
104+
lines = ls || [];
105+
});
106+
107+
splayer.player.on("lineChange", ({ index }) => {
108+
const skipBG = splayer.getSetting("skipBackgroundLyrics");
109+
const overlapDuet = splayer.getSetting("overlapDuetAsExtra");
110+
const cur = lines[index];
111+
112+
let lyric = "";
113+
let extra = "";
114+
let mainLine = cur;
115+
116+
if (cur && cur.isBG) {
117+
// 当前是背景歌词行,向前找到所属主歌词行
118+
const mainIdx = findPrevMainLineIndex(lines, index);
119+
if (mainIdx < 0) {
120+
// 找不到所属主行时,跳过背景歌词模式下不发送
121+
if (skipBG) return;
122+
lyric = lineText(cur);
123+
} else if (skipBG) {
124+
// 跳过背景歌词模式下,保持主行显示,但不在副行显示背景歌词
125+
mainLine = lines[mainIdx];
126+
lyric = lineText(mainLine);
127+
} else {
128+
mainLine = lines[mainIdx];
129+
lyric = lineText(mainLine);
130+
extra = lineText(cur);
131+
}
132+
} else if (overlapDuet && cur && cur.isDuet) {
133+
// 当前是对唱行,若与某主行时间重叠,则主行保持主显示,对唱行显示在副行
134+
const mainIdx = findOverlappingMainLineIndex(lines, index);
135+
if (mainIdx >= 0) {
136+
mainLine = lines[mainIdx];
137+
lyric = lineText(mainLine);
138+
extra = lineText(cur);
139+
} else {
140+
lyric = lineText(cur);
141+
}
142+
} else {
143+
lyric = lineText(cur);
144+
}
145+
146+
// 默认情况下,主歌词行的下一行若是背景歌词行,则把背景歌词显示在副行
147+
if (!skipBG && cur && !cur.isBG && lines[index + 1] && lines[index + 1].isBG) {
148+
extra = lineText(lines[index + 1]);
149+
}
150+
151+
// 仍未确定副行时,按原有逻辑回退:翻译 > 下一行
152+
if (!extra && mainLine) {
153+
if (splayer.getSetting("showTranslation") && mainLine.translatedLyric) {
154+
extra = mainLine.translatedLyric;
155+
} else if (splayer.getSetting("showNextLine")) {
156+
const nextIdx = findNextLineIndex(lines, index, skipBG);
157+
if (nextIdx >= 0) extra = lineText(lines[nextIdx]);
158+
}
159+
}
160+
161+
post(lyric, extra);
162+
});

0 commit comments

Comments
 (0)