AnzhiYu主题音乐馆添加歌曲下载

本文修改主题为AnZhiYu主题,若使用其他主题,请自行确认修改位置。

该方法通过外部引入,无需修改主题源文件。

写在前面

AnZhiYu主题自动生成的的音乐馆页面只提供了音乐的播放功能,并不能直接下载歌曲(虽然你可以自己用开发者工具抓包下载,但这不是一个好习惯)。因此,我们需要在音乐馆页面添加歌曲下载的功能。但是,AnZhiYu主题的音乐馆页面是通过Aplayer插件生成的,因此直接修改主题文件无法直接修改播放页面。故我们另辟蹊径,通过修改其在此页面外挂的按钮来添加歌曲下载功能。

预览

修改

添加下载按钮

修改[BlogRoot]\themes\anzhiyu\layout\includes\page\music.pug文件,添加下载按钮和下载加载动画。

1
2
3
4
5
6
7
8
9
10
11
12
#anMusic-page
#anMusicBtnGetSong(title="随机单曲,打开异世界的大门")
i(class="anzhiyufont anzhiyu-icon-shuffle")
#anMusicRefreshBtn(title="立即刷新最新歌单")
i(class="anzhiyufont anzhiyu-icon-arrows-rotate")
#anMusicSwitching(title="切换歌单")
i(class="anzhiyufont anzhiyu-icon-repeat")
+ #anMusicDownloadBtn(title="下载歌曲")
+ i(class="fa-solid fa-download")
+ #download-loader
+ i(class="fa-solid fa-spin")
#anMusic-page-meting

调整样式

修改[BlogRoot]\themes\anzhiyu\source\css\_page\music.styl文件,调整下载按钮的样式。
从76行左右开始,调整如下代码,调整下载按钮的样式。

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
@media screen and (max-width: 1400px)
body
#anMusic-page
- #anMusicSwitching, #anMusicRefreshBtn, #anMusicBtnGetSong
+ #anMusicSwitching, #anMusicRefreshBtn, #anMusicBtnGetSong, #anMusicDownloadBtn
right 7vw
#anMusicSwitching
bottom: 100px
#anMusicRefreshBtn
bottom: 160px
#anMusicBtnGetSong
bottom: 220px
+ #anMusicDownloadBtn
+ bottom: 280px

#anMusic-page
- #anMusicRefreshBtn, #anMusicBtnGetSong, #anMusicSwitching
+ #anMusicRefreshBtn, #anMusicBtnGetSong, #anMusicSwitching, #anMusicDownloadBtn
position: fixed;
display: flex;
width: 50px;
height: 50px;
bottom: 100px;
padding: 5px;
background: var(--anzhiyu-white-op);
backdrop-filter: saturate(180%) blur(20px);
-webkit-backdrop-filter: blur(20px);
border-radius: 50%;
color: #fff;
text-align: center;
justify-content: center;
align-items: center;
cursor: pointer;
z-index 2
#anMusicBtnGetSong
right: 11vw;
#anMusicRefreshBtn
right: 7vw;
#anMusicSwitching
right: 15vw;
+ #anMusicDownloadBtn
+ right: 19vw;
+maxWidth768()
div#anMusicBtnGetSong
right: 80px;
bottom: 150px;
div#anMusicRefreshBtn
right: 20px;
bottom: 150px;
div#anMusicSwitching
right: 140px;
bottom: 150px;
+ div#anMusicDownloadBtn
+ right: 200px;
+ bottom: 150px;

添加[BlogRoot]\themes\anzhiyu\source\css\custom\music.css文件或者修改你自定义的CSS文件,添加如下代码,添加点击下载按钮后的动画效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#download-loader {
display: none;
border: 16px solid #f3f3f3; /* Light grey */
border-top: 16px solid #3498db; /* Blue */
border-radius: 50%;
width: 120px;
height: 120px;
animation: loader-spin 2s linear infinite;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}

@keyframes loader-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}

添加下载功能

修改[BlogRoot]\themes\anzhiyu\source\js\utils.js文件,添加下载歌曲的功能。

在约879行左右,添加如下代码,加入下载歌曲按钮的监听事件。

1
2
3
4
5
6
7
8
9
  // 监听音乐背景改变
addEventListenerMusic: function () {
const anMusicPage = document.getElementById("anMusic-page");
const aplayerIconMenu = anMusicPage.querySelector(".aplayer-info .aplayer-time .aplayer-icon-menu");
const anMusicBtnGetSong = anMusicPage.querySelector("#anMusicBtnGetSong");
const anMusicRefreshBtn = anMusicPage.querySelector("#anMusicRefreshBtn");
const anMusicSwitchingBtn = anMusicPage.querySelector("#anMusicSwitching");
+ const anMusicDownloadBtn = anMusicPage.querySelector("#anMusicDownloadBtn");
const metingAplayer = anMusicPage.querySelector("meting-js").aplayer;

在约904行左右,添加如下代码,先用fetch请求歌曲的下载地址,然后用createObjectURL将歌曲数据转化为blob对象,最后用a标签下载歌曲。

注意:由于使用了fetch请求下载地址,因此你的音乐资源地址需要支持跨域请求,否则可能下载失败。

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
    // 监听增加单曲按钮
anMusicBtnGetSong.addEventListener("click", () => {
if (changeMusicListFlag) {
const anMusicPage = document.getElementById("anMusic-page");
const metingAplayer = anMusicPage.querySelector("meting-js").aplayer;
const allAudios = metingAplayer.list.audios;
const randomIndex = Math.floor(Math.random() * allAudios.length);
// 随机播放一首
metingAplayer.list.switch(randomIndex);
} else {
anzhiyu.cacheAndPlayMusic();
}
});
anMusicRefreshBtn.addEventListener("click", () => {
localStorage.removeItem("musicData");
anzhiyu.snackbarShow("已移除相关缓存歌曲");
});
anMusicSwitchingBtn.addEventListener("click", () => {
anzhiyu.changeMusicList();
});

+ anMusicDownloadBtn.addEventListener("click", async () => {
+ // 下载当前播放的歌曲
+ const loader = document.getElementById('download-loader');
+ const timeout = 10000; // 超时时间设置为10秒
+ loader.style.display = 'block'; // 显示加载动画
+ const controller = new AbortController();
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
+
+ const anMusicPage = document.getElementById("anMusic-page");
+ const metingAplayer = anMusicPage.querySelector("meting-js").aplayer;
+ const currentSong = metingAplayer.list.audios[metingAplayer.list.index];
+ const downloadUrl = currentSong.url;
+
+ let format = '';
+ if (downloadUrl.endsWith('.flac')) {
+ format = '.flac';
+ } else if (downloadUrl.endsWith('.mp3')) {
+ format = '.mp3';
+ } else if (downloadUrl.endsWith('.m4a')) {
+ format = '.m4a';
+ } else if (downloadUrl.endsWith('.ogg')) {
+ format = '.ogg';
+ } else if (downloadUrl.endsWith('.wav')) {
+ format = '.wav';
+ } else {
+ format = '.mp3';
+ }
+
+ downloadName = currentSong.name + " - " + currentSong.artist + format;
+ console.log("下载地址:", downloadUrl);
+ console.log("文件名:", downloadName);
+
+ try {
+ const response = await fetch(downloadUrl, { signal: controller.signal });
+ clearTimeout(timeoutId);
+
+ if (!response.ok) {
+ throw new Error(`网络错误,状态码: ${response.status}`);
+ }
+
+ const blob = await response.blob();
+ const downloadLink = document.createElement("a");
+ const objectUrl = URL.createObjectURL(blob);
+ downloadLink.href = objectUrl;
+ downloadLink.download = downloadName;
+ downloadLink.click();
+ URL.revokeObjectURL(downloadLink.href); // 释放内存
+
+ } catch (error) {
+ if (error.name === 'AbortError') {
+ if (GLOBAL_CONFIG.Snackbar !== undefined) {
+ anzhiyu.snackbarShow("下载超时,请稍后再试");
+ }
+ } else {
+ if (GLOBAL_CONFIG.Snackbar !== undefined) {
+ anzhiyu.snackbarShow("下载失败" + error.message);
+ }
+ }
+ } finally {
+ loader.style.display = 'none'; // 隐藏加载动画
+ }
+ });