Greasy Fork

来自缓存

Greasy Fork is available in English.

唱吧下载器

在听歌页面,提供该用户的所有歌曲下载

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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);
}