// ==UserScript==
// @name 唱吧下载器
// @namespace http://tampermonkey.net/
// @version 0.2.4
// @description 在听歌页面,提供该用户的所有歌曲下载
// @author cw2012
// @match http*://changba.com/s/*
// @icon https://changba.com/favicon.ico
// @connect *
// @require https://cdn.bootcdn.net/ajax/libs/jszip/3.5.0/jszip.min.js
// @grant GM_xmlhttpRequest
// @grant GM_download
// @run-at document-end
// ==/UserScript==
// 2021-01 歌手个人主页已不可用: http*://changba.com/u/*
function newSong(id, name,enworkid, hasMV){
let song = new Object();
song.id = id;
song.name = name;
song.enworkid = enworkid;
song.hasMV = hasMV;
return song;
}
// 利用blob下载文件
function downloadFileByBlob(blobContent, filename) {
const blobUrl = URL.createObjectURL(blobContent)
const eleLink = document.createElement('a')
eleLink.download = filename
eleLink.style.display = 'none'
eleLink.href = blobUrl
eleLink.click();
}
(function() {
'use strict';
//首先禁止自动播放,烦死了
let audioEle = document.getElementById('audio');
if(audioEle){
audioEle.autoplay = false;
}
insertCss();
let userId, userName=document.querySelector('.ulevel').innerText;
let pageIndex = 0, curSongIndex = 0;
let songList = [], songCount, zip, mvCount = 0;
$('span.info').css('width','60px');
let sibling = document.querySelector('span.fav');
sibling.parentNode.innerHTML = '<span class="export info" style="width:60px" data-status="0" id="btn_download"><em>下载</em></span>'+ sibling.parentNode.innerHTML;
document.getElementById('btn_download').addEventListener('click',()=>{
if(audioEle){
GM_download({
url: audioEle.src,
name: `${userName}-${$('.title').html()}.mp3`
});
}else{
GM_download({
url:document.getElementById('jp_video_0').src,
name: `${userName}-${$('.title').html()}.mp4`
});
}
});
addListBox();
analysisSongList();
function addListBox(){
let listBox= document.createElement('div');
listBox.id = 'songListBox';
let titleDiv = document.createElement('div');
titleDiv.className ='widget-header-simple song-list-title';
titleDiv.innerHTML = '<p id="songListTitle" style="font-size:16px">正在解析用户作品列表</p>';
listBox.append(titleDiv);
document.body.append(listBox);
}
function analysisSongList(){
userId = document.querySelector('.focus').getAttribute('data-userid');
let tmpEle = document.querySelectorAll('.ulevel>a.uname')[0];
tmpEle.onclick = ()=>{window.open('https://changba.com/wap/index.php?s='+document.querySelector('.focus').getAttribute('data-userid'),'_blank')};
updateSongList();
}
function updateSongList(){
GM_xmlhttpRequest({
url: `https://changba.com/member/personcenter/loadmore.php?ver=1&pageNum=${pageIndex>0?pageIndex:''}&type=0&userid=${userId}`,
method: 'get',
timeout: 15000,
responseType : 'json',
onload: res=>{
pageIndex++;
const data = res.response;
if(data.length>0){
for(var i = 0; i< data.length;i++){
if(data[i].ismv ==`style='display:inline'`){
songList.push(newSong(data[i].workid, data[i].songname, data[i].enworkid, true));
mvCount++;
}else{
songList.push(newSong(data[i].workid, data[i].songname, data[i].enworkid, false));
}
}
updateSongList();
}else{
showSongList();
}
},
ontimeout:()=>{console.log('获取列表超时'+pageIndex)},
onerror: err=>{
alert(`获取歌曲列表失败`);
}
});
}
function showSongList(){
document.getElementById('songListTitle').innerText = `该用户有${songList.length}首歌${mvCount?('(其中包含'+mvCount+'个MV)'):''}`;
let songListDiv = document.createElement('div');
songListDiv.className = 'songList';
let ol = document.createElement('ol');
songList.forEach((item,index)=>{
let li = document.createElement('li');
li.className = 'song';
let a = document.createElement('a');
a.innerText = '下载';
a.href = '#';
let span = document.createElement('span');
span.innerText = item.name;
if(item.hasMV){
let img = document.createElement('img');
img.className = 'mv';
img.src='https://www.zhangxinxu.com/study/image/pixel.gif';
span.append(img);
}
a.addEventListener('click',()=>{
downloadSingleSong(item,a);
});
li.append(span);
li.append(a);
a = document.createElement('a');
a.innerText = '查看';
a.href = 'https://changba.com/s/' + item.enworkid;
li.append(a);
ol.append(li);
});
songListDiv.append(ol);
document.getElementById('songListBox').append(songListDiv);
}
function downloadSingleSong(song,a){
let id= song.id, name=song.name,enworkid=song.enworkid;
let fileName = `${userName}-${name}${song.hasMV?'.mp4':'.mp3'}`;
let progress = document.createElement('progress');
progress.value= 0;
progress.max = 100;
GM_xmlhttpRequest({
url: `https://changba.com/s/${enworkid}`,
method: 'get',
timeout: 5000,
onload: res =>{
let html = res.responseText;
let index = html.indexOf(song.hasMV?'video_url:':'commonObj.url');
if(index!=-1){
html = html.substr(index, 200);
html = html.match(/'[\w/=+]+'/g)[0];
html = html.substr(1, html.length -2);
let url = getMp3Url(html);
if(song.hasMV)
url = 'http:'+getMp4Url(url);
a.parentElement.insertBefore(progress, a);
GM_download({
url: url,
name: fileName,
onprogress: prog=>{progress.value = prog.loaded;progress.max = prog.total;},
onload: res=>{ progress.remove();},
ontimeout: res=>{alert(`下载"${fileName}"超时,请重试`); progress.remove();},
onerror:res=>{alert(`下载"${fileName}"出错,请重试`); progress.remove();}
});
}
}
});
}
function getMp3Url(html){
var km= 'a17fe74e421c2cbf3dc323f4b4f3a1af' , iv = CryptoJS.enc.Utf8.parse(km.substring(0,16))
, iv2 = CryptoJS.enc.Utf8.parse(km.substring(16))
, audio_url = '';
audio_url = CryptoJS.AES.decrypt(html, iv2, {
'iv': iv,
'padding': CryptoJS.pad.Pkcs7
})['toString'](CryptoJS.enc.Utf8);
return audio_url;
}
function getMp4Url(origin_video_url){
var arr = new Array(-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,0x3e,-0x1,-0x1,-0x1,0x3f,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,-0x1,-0x1,-0x1,-0x1,-0x1,-0x1,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,-0x1,-0x1,-0x1,-0x1,-0x1);
var charCode1, charCode2, charCode3, charCode4, i, length, resultStr;
for (length = origin_video_url['length'],
i = 0x0,
resultStr = ''; i < length;) {
do {
charCode1 = arr[0xff & origin_video_url['charCodeAt'](i++)];
} while (i < length && -0x1 == charCode1);
if (-0x1 == charCode1)
break;
do {
charCode2 = arr[0xff & origin_video_url['charCodeAt'](i++)];
} while (i < length && -0x1 == charCode2);
if (-0x1 == charCode2)
break;
resultStr += String['fromCharCode'](charCode1 << 0x2 | (0x30 & charCode2) >> 0x4);
do {
if (0x3d == (charCode3 = 0xff & origin_video_url['charCodeAt'](i++)))
return resultStr;
charCode3 = arr[charCode3];
} while (i < length && -0x1 == charCode3); if (-0x1 == charCode3)
break;
resultStr += String['fromCharCode']((0xf & charCode2) << 0x4 | (0x3c & charCode3) >> 0x2);
do {
if (0x3d == (charCode4 = 0xff & origin_video_url['charCodeAt'](i++)))
return resultStr;
charCode4 = arr[charCode4];
} while (i < length && -0x1 == charCode4); if (-0x1 == charCode4)
break;
resultStr += String['fromCharCode']((0x3 & charCode3) << 0x6 | charCode4);
}
return resultStr;
}
// 显示下载进度
function progress(){
let bar = document.getElementById('progressBar');
if(bar){
document.getElementById('prog-num').innerText = `正在下载:${curSongIndex + 1}/${songCount}`;
document.getElementById('song-name').innerText = songList[curSongIndex].name;
if(curSongIndex + 1 == songCount){
bar.remove();
}
return;
}
let progressBox = document.createElement('div');
progressBox.id = 'progressBar';
progressBox.style.position = 'fixed';
progressBox.style.background='white';
progressBox.style.borderRadius='10px';
progressBox.style.background= `rgb(255 80 70)`;
progressBox.style.border = 'solid white 1px';
progressBox.style.boxShadow='0 8px 16px 0 rgba(0,0,0,.2), 0 6px 20px 0 rgba(0,0,0,.19)';
progressBox.style.color = '#fff';
progressBox.style.bottom='200px'; // 显示的位置不能离底部太低了,会被其他元素遮挡
progressBox.style.left='2vh';
progressBox.style.transition='1.5s';
progressBox.style.padding = '10px';
let progNum = document.createElement('p');
progNum.id = 'prog-num';
progNum.innerText = `1/${songCount}`;
let downName = document.createElement('p');
downName.id = 'song-name';
downName.innerText='正在下载...';
progressBox.append(progNum);
progressBox.append(downName);
document.body.appendChild(progressBox);
}
})();
function insertCss(){
var style=document.createElement('style');
const myStyle = `
#songListBox{
position:fixed;
height:400px;
width:400px;
border-radius:0px 10px 10px 0px;
box-shadow:#fba9a9 3px 4px 40px 1px;
top:100px;
}
.widget-header-simple.song-list-title{
color:#fff;
text-align:center;
display:flex;
align-items:center;
justify-content:center;
border-radius:0px 10px 0px 0px;
}
ol{
padding:10px;
}
li.song{
margin:10px 0;
font-size:14px;
color:black;
padding: 5px;
background: #ffeded;
display: flex;
align-items: center;
justify-content: space-evenly;
flex-direction: row;
}
li.song span{
width: 210px;
}
div.songList{
overflow: scroll;
overflow-y: auto;
overflow-x: hidden;
height: 330px;
}
.mv{
background: url(https://greasyfork.s3.us-east-2.amazonaws.com/xv0qosox3hocgp4jni3qydurnlom) no-repeat;
background-size: 100% 100%;
width: 25px;
height: 25px;
margin-left: 4px;
}
li a {
color: #ff142e;
}
/*进度条的样式*/
progress{
width:40px;
height:15px;
}
progress::-webkit-progress-value
{
background-color:#ff0040;
}
`;
style.type='text/css';
if(style.styleSheet){
style.styleSheet.cssText=myStyle;
}else{
style.appendChild(document.createTextNode(myStyle));
}
document.getElementsByTagName('head')[0].appendChild(style);
}