Greasy Fork is available in English.
Download musics in album from Ximalaya website
// ==UserScript==
// @name 喜马拉雅Album下载
// @namespace http://tampermonkey.net/
// @version 0.1
// @description Download musics in album from Ximalaya website
// @author J. Chen
// @match https://www.ximalaya.com/album/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=ximalaya.com
// @grant none
// ==/UserScript==
//=============================
//添加axios的js
var axios_src = document.createElement("script");
axios_src.src = "https://unpkg.com/axios/dist/axios.min.js";
document.body.append(axios_src);
//必须写在最前面
var callback = function(){
// 在 DOM 完全加载完后执行
//console.log("********* 00 ***************");
//console.log(document.readyState);
pageListener();
main();
};
//当document文档正在加载时,返回"loading"。
//当文档结束渲染但在加载内嵌资源时,返回"interactive",并引发DOMContentLoaded事件。
//当文档加载完成时,返回"complete",并引发load事件。
if(document.readyState === "complete"){
console.log("********* 0 ***************");
pageListener();
main();
}
else{
console.log("********* 1 ***************");
document.addEventListener('readystatechange', callback,false);
console.log("********* 2 ***************");
//window.addEventListener('load', callback, false);
}
//监测页面变化函数
function pageListener(){
var pagesNode = document.getElementsByClassName("pagination-page N_t")[0];
var ulNode = document.getElementsByClassName("sound-list H_g")[0].children[0];
var liNodes = ulNode.querySelectorAll("li");
var oldTitle = ulNode.children[0].getElementsByClassName("text _nO")[0].children[0].title;
var newTitle = oldTitle;
var changeTimes = 0;
//text _nO
var targetNode = pagesNode;
// 观察者的选项(要观察哪些突变)
var config = { attributes: true, childList: true, subtree: true };
//var config = { attributes: true};
// 当观察到页数变时执行的回调函数
var callback = function(mutationsList) {
mutationsList.forEach(function(item,index){
if (item.type == 'childList') {
console.log('子节点发生改变');
} else if (item.type == 'attributes') {
changeTimes += 1;
console.log('修改了'+item.attributeName+'属性');
var refreshPage = setInterval(checkPage,1000); //翻页后页面可能没及时刷新,用此函数不停地刷新
var ulNode = document.getElementsByClassName("sound-list H_g")[0].children[0];
//var oldTitle = ulNode.children[0].getElementsByClassName("text _nO")[0].children[0].title;
newTitle = ulNode.children[0].getElementsByClassName("text _nO")[0].children[0].title;
var i = 0;
function checkPage(){
ulNode = document.getElementsByClassName("sound-list H_g")[0].children[0];
newTitle = ulNode.children[0].getElementsByClassName("text _nO")[0].children[0].title;
//console.log(i);
i += 1;
if(oldTitle != newTitle){
oldTitle = newTitle;
clearInterval(refreshPage);//停止调用timeinterval
//console.log("old title: " + oldTitle);
//console.log("new title: " + newTitle);
console.log("页面已刷新");
//oldTitle = newTitle;
main();
}
}
} else if (item.type == 'subtree'){
console.log('subtree changed');
}
else {
console.log('others');
}
});
};
// 创建一个链接到回调函数的观察者实例
var observer = new MutationObserver(callback);
// 开始观察已配置突变的目标节点
observer.observe(targetNode, config);
// 停止观察
//observer.disconnect();
}
//在页面添加下载按钮和功能
function main(){
var url_base = "https://www.ximalaya.com/revision/play/v1/audio?id="
var ulNode = document.getElementsByClassName("sound-list H_g")[0].children[0];
var liNodes = ulNode.querySelectorAll("li");
var buttons = [];
for(var i=0; i< liNodes.length; i++){
//添加下载button
buttons.push ( document.createElement("input") );
buttons[i].setAttribute("type", "button");
buttons[i].setAttribute("value", "下载");
//绑定点击事件
buttons[i].onclick = function () {
var textLiNode = this.parentNode.getElementsByClassName("text _nO")[0].children[0];
var title = textLiNode.title;
var href = textLiNode.href;
console.log(title);
console.log(href);
var id = href.split("/").pop();
var url = url_base + id + "&ptype=1"
//console.log(url);
var httpRequest = new XMLHttpRequest();//第一步:建立所需的对象
httpRequest.open('GET', url, true);//第二步:打开连接
httpRequest.send();//第三步:发送请求 将请求参数写在URL中
/**
* 获取数据后的处理程序
*/
httpRequest.onreadystatechange = function () {
if (httpRequest.readyState == 4 && httpRequest.status == 200) {
var json = httpRequest.responseText;//获取到json字符串,还需解析
var obj = JSON.parse(json);
var src = obj.data["src"]; //url of m4a file
var filename = title +".m4a";
console.log("***" + i + "***");
console.log(src);
console.log(filename);
//添加下载功能
download(src, filename);
}
};
//添加下载功能
};
liNodes[i].insertBefore(buttons[i], liNodes[i].childNodes[0]);
}
}
//音频文件下载函数
function download (downUrl, fileName) {
axios({
method: 'get',
url: downUrl,
responseType: 'blob',
headers: {'content-type': 'audio/mpeg'}
// headers: {'content-length': '4066786', 'content-type': 'audio/mpeg'}
}).then((res) => {
// eslint-disable-next-line no-unused-vars
let blobType = 'application/force-download' // 设置blob请求头
// eslint-disable-next-line no-unused-vars
let blob = new Blob([res.data], {type: res.data.type}) // 创建blob 设置blob文件类型 data 设置为后端返回的文件(例如mp3,jpeg) type:这里设置后端返回的类型 为 mp3
let downa = document.createElement('a') // 创建A标签
// eslint-disable-next-line no-unused-vars
let href = window.URL.createObjectURL(blob) // 创建下载的链接
downa.href = href // 下载地址
downa.download = fileName // 下载文件名
document.body.appendChild(downa)
downa.click() // 模拟点击A标签
document.body.removeChild(downa) // 下载完成移除元素
window.URL.revokeObjectURL(href) // 释放blob对象
}).catch(function (error) {
console.log(error)
})
}