博客建立起来也有一段时间了,在使用过程中感觉原有音乐功能还有待完善。一是音乐胶囊在桌面端页面无滚动条和移动端竖屏状态下无法唤出,执行相关操作要返其它页面或横屏使用;二是音乐页面只支持QQ、网易云等音乐平台,不支持本地音乐,因版权问题较多歌曲无法播放。为进一步提升使用体验,本人针对痛点堵点进行了能力范围内的一些细微改动并加以记录,希望对来客有所帮助。以下是第一篇:右键菜单添加音乐控制功能。实际效果欢迎呼出右键菜单体验。

主题魔改风险高,没有备份全报销。劝君三思再保存,莫等黄字泪汪汪。

本教程基于Solitude V3.0.20,作者技术能力有限 ,其他主题或版本请自行研究迁移。


改动简概

功能:实现歌曲信息实时显示及胶囊音乐播放控制;

结构:增加音乐控制模块,歌名、歌手分行居中显示;

逻辑:首次开启菜单读取播放列表第一首歌曲;

样式:Hover高亮、字体样式等同步主题风格;

缺陷:进入音乐页面同样读取歌曲信息,逻辑有待改进;

缺陷:原有音乐有限控制相关功能未一同清除。


实现过程

定义功能逻辑

定位到source/js/main.js,在文件最后一行添加以下内容,引入音乐信息获取与按钮功能相关功能逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
// 更新右键菜单播放/暂停图标
function updateRightmenuMusicToggleIcon(forceState) {
const playSvg = document.getElementById('rightmenu-music-svg-play');
const pauseSvg = document.getElementById('rightmenu-music-svg-pause');
let isPlaying = forceState;
if (typeof isPlaying === 'undefined') {
const meting = document.querySelector('meting-js');
isPlaying = !!(meting && meting.aplayer && meting.aplayer.audio && !meting.aplayer.audio.paused);
}
if (playSvg && pauseSvg) {
playSvg.style.display = isPlaying ? 'none' : 'block';
pauseSvg.style.display = isPlaying ? 'block' : 'none';
}
}
if (typeof sco !== 'undefined') {
const oldMusicToggle = sco.musicToggle;
sco.musicToggle = function() {
if (typeof oldMusicToggle === 'function') oldMusicToggle.apply(this, arguments);
updateRightmenuMusicToggleIcon();
};
}

// 更新右键菜单歌曲信息
function updateRightmenuMusicTitleAndIcon(retry = 0) {
const meting = document.querySelector('meting-js');
const titleEl = document.getElementById('rightmenu-music-title');
const artistEl = document.getElementById('rightmenu-music-artist');
let displayTitle = '';
let displayArtist = '';

if (meting && meting.aplayer && meting.aplayer.list && meting.aplayer.list.audios.length > 0) {
const index = meting.aplayer.list.index;
let current = meting.aplayer.list.audios[index];
if (!current || !current.name) {
current = meting.aplayer.list.audios[0];
}
displayTitle = current.name || '';
displayArtist = current.artist || '';
} else if (retry < 3) {
setTimeout(() => updateRightmenuMusicTitleAndIcon(retry + 1), 150);
return;
} else {
displayTitle = '';
displayArtist = '';
}

if (displayTitle && titleEl) titleEl.textContent = displayTitle;
if (displayArtist && artistEl) artistEl.textContent = displayArtist;

if (meting && meting.aplayer) {
updateRightmenuMusicToggleIcon(!meting.aplayer.audio.paused);
}
}

// 初始化右键菜单事件绑定
function bindRightmenuMusicEvents() {
function tryBind() {
const meting = document.querySelector('meting-js');
if (meting && meting.aplayer) {
meting.aplayer.on('listswitch', () => updateRightmenuMusicTitleAndIcon());
meting.aplayer.on('play', () => updateRightmenuMusicToggleIcon(true));
meting.aplayer.on('pause', () => updateRightmenuMusicToggleIcon(false));
updateRightmenuMusicTitleAndIcon();
return true;
}
return false;
}

if (!tryBind()) {
const observer = new MutationObserver(() => {
if (tryBind()) observer.disconnect();
});
observer.observe(document.body, { childList: true, subtree: true });
}
}

// 页面加载、点击和右键触发菜单刷新
document.addEventListener('DOMContentLoaded', bindRightmenuMusicEvents);
document.getElementById('rightmenu-music-capsule')?.addEventListener('click', updateRightmenuMusicTitleAndIcon);
document.addEventListener('contextmenu', () => updateRightmenuMusicTitleAndIcon());


// 监听右键音乐菜单过渡完成
const rightmenuMenus = document.getElementById('rightmenu-music-capsule');
if (rightmenuMenus) {
rightmenuMenus.addEventListener('transitionend', () => {
if (window.innerWidth <= 768 && rightmenuMenus.classList.contains('open')) {
updateRightmenuMusicTitleAndIcon();
}
});
}

搭建菜单模组

定位到layout/includes/rightmenu.pug,在div#rightmenu-mask前一行添加以下内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 音乐信息和按钮区块
div.rightMenu-group.rightMenuMusic
div.rightMenu-item
span.rightmenu-music-info
span#rightmenu-music-title 歌曲名
span#rightmenu-music-artist 歌手名
div.rightMenu-item
div.rightmenu-music-btns、
// 上一曲按钮
button.menu-child-btn(type="button" onclick="sco.musicSkipBack(); updateRightmenuMusicTitleAndIcon();" title="上一曲")
svg(xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24")
g(fill="none")
path(d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z")
path(fill="currentColor" d="m20.43 5.865l.078.699l.072.767l.036.448l.051.75l.03.55l.038.894l.019.641l.012.675l.004.707l-.004.707l-.012.675l-.019.641l-.024.606l-.028.569l-.05.78l-.035.47l-.09.986l-.079.698a1.332 1.332 0 0 1-1.844 1.065l-.49-.213l-.846-.386l-.62-.298l-.458-.226l-.748-.381l-.538-.283l-.566-.306l-.594-.329l-.619-.353l-.615-.36l-.582-.349l-.809-.5l-.73-.47l-.443-.292l-.406-.274l-.54-.373l-.587-.42l-.43-.319a1.332 1.332 0 0 1 .002-2.13l.325-.242l.422-.306l.517-.363l.607-.414l.694-.458l.51-.327l.546-.342l.581-.355l.617-.366l.32-.186q.312-.18.613-.349l.588-.326l.563-.304l.793-.414l.725-.365l.442-.215l.597-.283l.802-.362l.355-.154a1.332 1.332 0 0 1 1.846 1.065ZM6 5a1 1 0 0 1 .993.883L7 6v12a1 1 0 0 1-.883.993L6 19H5a1 1 0 0 1-.993-.883L4 18V6a1 1 0 0 1 .883-.993L5 5z")
// 播放/暂停按钮
button.menu-child-btn(type="button" id="rightmenu-music-toggle" onclick="sco.musicToggle()" title="播放/暂停")
svg#rightmenu-music-svg-play(xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" style="display:block")
g(fill="none" fill-rule="evenodd")
path(d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z")
path(fill="currentColor" d="M5.669 4.76a1.47 1.47 0 0 1 2.04-1.177c1.062.454 3.442 1.533 6.462 3.276c3.021 1.744 5.146 3.267 6.069 3.958c.788.591.79 1.763.001 2.356c-.914.687-3.013 2.19-6.07 3.956c-3.06 1.766-5.412 2.832-6.464 3.28c-.906.387-1.92-.2-2.038-1.177c-.138-1.142-.396-3.735-.396-7.237c0-3.5.257-6.092.396-7.235")
svg#rightmenu-music-svg-pause(xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" style="display:none")
g(fill="none")
path(d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z")
path(fill="currentColor" d="M7 5a1 1 0 0 1 1 1v12a1 1 0 0 1-2 0V6a1 1 0 0 1 1-1m10 0a1 1 0 0 1 1 1v12a1 1 0 0 1-2 0V6a1 1 0 0 1 1-1")
// 下一曲按钮
button.menu-child-btn(type="button" onclick="sco.musicSkipForward(); updateRightmenuMusicTitleAndIcon();" title="下一曲")
svg(xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24")
g(fill="none")
path(d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z")
path(fill="currentColor" d="M3.569 5.865A1.332 1.332 0 0 1 5.415 4.8l.646.283l.511.233l.597.283l.676.331l.49.249l.793.414l.564.304l.588.326l.613.349l.633.37l.599.361l.564.349l.778.496l.694.458l.607.414l.517.363l.541.394l.206.154c.71.535.71 1.594.001 2.13l-.43.319l-.273.198l-.664.465l-.595.404l-.443.292l-.73.47l-.81.5l-.581.35l-.615.36l-.62.352l-.593.33l-.566.305l-.538.283l-.748.381l-.673.331l-.773.364l-.744.332l-.224.096a1.332 1.332 0 0 1-1.844-1.065l-.08-.698l-.071-.767l-.053-.689l-.05-.78l-.028-.57l-.024-.605l-.019-.64l-.015-1.026v-.715l.015-1.024l.03-.948l.026-.587l.03-.55l.052-.75l.054-.657l.07-.722zM19 5a1 1 0 0 1 .993.883L20 6v12a1 1 0 0 1-.883.993L19 19h-1a1 1 0 0 1-.993-.883L17 18V6a1 1 0 0 1 .883-.993L18 5z")

如果不喜欢Svg图标,也可以换成Font Awesome图标,我只是单纯没用过想试一下。

引入相关样式

定位到blog/source/custom.csssolitude/source/css/_layout/rightmenu.styl,添加以下内容。如无custom.css,新建或者使用自己原有自定义Css文件亦可;如需在styl中引入,请自行修改格式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/* 右键菜单音乐菜单样式 */
.rightMenu-group.rightMenuMusic {
margin-top: 0.5rem;
padding: 0.35rem 0.3rem;
border-top: 1px dashed var(--efu-theme-op);
}

.rightMenu-group.rightMenuMusic .music-capsule-block {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
background: var(--efu-card-bg);
border-radius: 8px;
border: var(--style-border-always);
margin: 0;
padding: 8px 0 4px 0;
}

.rightMenu-group.rightMenuMusic .music-title-ellipsis {
display: block;
text-align: center;
margin: 0 0 8px 0;
font-size: 1em;
color: var(--efu-fontcolor);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
padding: 0 8px;
box-sizing: border-box;
}

.rightMenu-group.rightMenuMusic .music-capsule-btns {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
width: 100%;
gap: 20px;
margin: 8px 0 4px 0;
}

.rightMenu-group.rightMenuMusic .rightmenu-music-info {
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
background: none !important;
color: inherit !important;
pointer-events: auto;
}

.rightMenu-group.rightMenuMusic .rightmenu-music-info span#rightmenu-music-title {
font-size: 1em;
color: var(--efu-fontcolor);
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
text-align: center;
}

.rightMenu-group.rightMenuMusic .rightmenu-music-info span#rightmenu-music-artist {
font-size: 0.92em;
color: var(--efu-gray);
margin: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
text-align: center;
}

.rightMenu-group.rightMenuMusic .rightmenu-music-btns {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
width: 100%;
gap: 16px;
background: none;
}

.rightMenu-group.rightMenuMusic .rightmenu-music-btns .menu-child-btn {
width: 32px;
height: 32px;
font-size: 1.1em;
color: var(--efu-fontcolor);
background: none;
border: none;
border-radius: 8px;
margin: 0;
cursor: pointer;
transition: background 0.2s;
}

.rightMenu-group.rightMenuMusic .rightmenu-music-btns .menu-child-btn:hover,
.rightMenu-group.rightMenuMusic .rightmenu-music-btns .menu-child-btn:active {
background: var(--efu-gray-op);
}

/* 歌曲信息基础样式 */
.rightMenuMusic .rightMenu-item .rightmenu-music-info,
.rightMenuMusic .rightMenu-item .rightmenu-music-info * {
color: var(--efu-fontcolor) !important;
background: none !important;
box-shadow: none !important;
display: block !important;
height: auto !important;
visibility: visible !important;
opacity: 1 !important;
}

/* 歌手名颜色单独控制 */
.rightMenuMusic .rightmenu-music-info #rightmenu-music-artist {
color: var(--efu-gray) !important;
}

/* 歌曲信息布局 */
.rightMenuMusic .rightmenu-music-info {
display: flex;
flex-direction: column;
align-items: center;
gap: 0 !important;
margin: 0 !important;
padding: 0 !important;
}
.rightMenuMusic .rightmenu-music-info span#rightmenu-music-title,
.rightMenuMusic .rightmenu-music-info span#rightmenu-music-artist {
margin: 0 !important;
padding: 0 !important;
line-height: 1.1 !important;
display: block;
}

/* 按钮区块 */
.rightMenuMusic .rightmenu-music-btns {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
width: 100%;
gap: 16px;
}
.rightMenuMusic .menu-child-btn {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
font-size: 1.1em;
color: var(--efu-fontcolor);
background: none;
border: none;
border-radius: 8px;
margin: 0;
padding: 0;
cursor: pointer;
transition: background 0.2s;
}
.rightMenuMusic .menu-child-btn svg {
width: 1.5em;
height: 1.5em;
min-width: 24px;
min-height: 24px;
display: block;
margin: auto;
}

/* 去掉 item 高亮,只保留按钮高亮 */
.rightMenuMusic .rightMenu-item:hover,
.rightMenuMusic .rightMenu-item:active,
.rightMenuMusic .rightMenu-item:hover .rightmenu-music-info,
.rightMenuMusic .rightMenu-item:active .rightmenu-music-info,
.rightMenuMusic .rightMenu-item:hover .rightmenu-music-btns,
.rightMenuMusic .rightMenu-item:active .rightmenu-music-btns {
background: none !important;
box-shadow: none !important;
}
.rightMenuMusic .menu-child-btn:hover,
.rightMenuMusic .menu-child-btn:active {
background: var(--efu-main) !important;
color: #fff !important;
}

结语

改动全程在Cursor+Claude-4-Sonnet下进行,如借鉴过程中发现存在问题或有待改进的地方,欢迎在评论区留言,我看到会第一时间回复。