Greasy Fork

Greasy Fork is available in English.

网易云音乐直接下载

在单曲页面显示歌词、翻译、封面、MV、歌曲下载链接并以高音质试听。同时支持歌单、专辑等页面直接下载单曲、封面、歌词(压缩包)。如遇到错误,请阅读附加信息后进行反馈。

当前为 2020-09-13 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name              网易云音乐直接下载
// @namespace         https://www.ocrosoft.com/
// @description       在单曲页面显示歌词、翻译、封面、MV、歌曲下载链接并以高音质试听。同时支持歌单、专辑等页面直接下载单曲、封面、歌词(压缩包)。如遇到错误,请阅读附加信息后进行反馈。
// @match             *://music.163.com/*
// @grant             GM.xmlHttpRequest
// @grant             GM_xmlhttpRequest
// @version           4.0.1
// @author            ocrosoft
// @connect           126.net
// @connect           163.com
// @connect           172.*
// @license           GPLV2
// @compatible        Chrome
// @compatible        FireFox
// @require           https://cdn.bootcss.com/crypto-js/3.1.9/core.min.js
// @require           https://cdn.bootcss.com/crypto-js/3.1.9/crypto-js.js
// @require           https://cdn.bootcss.com/crypto-js/3.1.9/aes.min.js
// @require           https://cdn.bootcss.com/crypto-js/3.1.9/enc-utf8.js
// @require           https://cdn.bootcss.com/crypto-js/3.1.9/enc-base64.min.js
// @require           https://cdn.bootcss.com/jszip/3.1.5/jszip.min.js
// @require           https://cdn.bootcss.com/FileSaver.js/1.3.8/FileSaver.js
// ==/UserScript==
var GM__xmlHttpRequest;
if("undefined" != typeof(GM_xmlhttpRequest)){
    GM__xmlHttpRequest = GM_xmlhttpRequest;
} else {
    GM__xmlHttpRequest = GM.xmlHttpRequest;
}

// 参考 https://github.com/darknessomi/musicbox/blob/master/NEMbox/api.py
function _WEAPI() {
    function _REQUEST() {
        let _base_url = 'https://music.163.com';
        let _send_request = function(url, method, headers, data) {
            return new Promise(function(resolve, reject) {
                GM__xmlHttpRequest({
                    method: method,
                    url: url,
                    headers: headers,
                    data: data,
                    onreadystatechange: function(res) {
                        if (res.readyState == 4) {
                            if (res.status == 200) {
                                resolve(res.response);
                                return;
                            }
                            reject(res.status);
                        }
                    }
                });
            });
        };
        let _headers = {
            'Accept':'*/*',
            'Accept-Encoding':'gzip,deflate,sdch',
            'Accept-Language':'zh-CN,zh;q=0.8,gl;q=0.6,zh-TW;q=0.4',
            'Connection':'keep-alive',
            'Content-Type':'application/x-www-form-urlencoded',
            'Host':'music.163.com',
            'Origin':'http://music.163.com',
            'Referer':'http://music.163.com/',
            'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36',
            'Cookie': document.cookie + ';os=pc'
        };

        this.sendRequest = function(path, data) {
            return _send_request(_base_url + path, 'POST', _headers, data);
        };
        this.sendRequestWrapped = function(path, data, response_process) {
            let sendRequest = this.sendRequest;
            return new Promise(function(resolve) {
                sendRequest(path, data)
                    .then(function(response) {
                    let data = JSON.parse(response);
                    if (response_process) {
                        data = response_process(data);
                    }
                    resolve(data);
                })
                    .catch(function(status) {
                    resolve([]);
                });
            });
        };
        this.sendRequestWithMethod = function(path, method, data) {
            return _send_request(_base_url + path, method, _headers, data);
        }
    }
    function _ENCRYPT() {
        let _MODULUS = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7' +
            'b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280' +
            '104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932' +
            '575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b' +
            '3ece0462db0a22b8e7';
        let _NONCE = '0CoJUm6Qyw8W8jud';
        let _PUBKEY = '010001';
        let _create_key = function() {
            return (Math.random().toString(16).substring(2) + Math.random().toString(16).substring(2)).substring(0,16);
        };
        let _rsaEncrypt = function(text, pubKey, modulus) {
            setMaxDigits(256);
            var keys = new RSAKeyPair(pubKey, '', modulus);
            var encText = encryptedString(keys, text);
            return encText;
        }
        let _aesEncrypt = function (text, secKey){
            secKey = CryptoJS.enc.Utf8.parse(secKey);
            text = CryptoJS.enc.Utf8.parse(text);
            var encrypted = CryptoJS.AES.encrypt(text, secKey, {
                iv: CryptoJS.enc.Utf8.parse('0102030405060708'),
                mode: CryptoJS.mode.CBC
            });
            encrypted = encrypted.toString();
            return encrypted;
        }

        this.encryptedId = function(id) {
            throw('not impl!');
        };
        this.encryptedRequest = function(map) {
            let text = JSON.stringify(map);
            let secret = _create_key();
            let params = _aesEncrypt(_aesEncrypt(text, _NONCE), secret);
            let encseckey = _rsaEncrypt(secret, _PUBKEY, _MODULUS);
            return 'params=' + encodeURIComponent(params) + '&encSecKey=' + encodeURIComponent(encseckey);
        };
    };

    let REQUEST = new _REQUEST();
    let ENCREYPT = new _ENCRYPT();

    let QENC = function(map) {
        return ENCREYPT.encryptedRequest(map);
    }
    let QREQ = function(path, data, response_process) {
        return REQUEST.sendRequestWrapped(path, data, response_process);
    }

    this.songsDetail = function(ids) {
        let path = '/weapi/v3/song/detail';
        let c = [];
        for (let i = 0; i < ids.length; ++i) {
            c.push({'id': ids[i]});
        }
        let map = {
            'ids': ids,
            'c': JSON.stringify(c)
        }
        let data = QENC(map);
        return QREQ(path, data, function(data) {
            try {
                return data.songs;
            } catch (e) {
                return [];
            }
        });
    }
    this.songsUrl = function(ids) {
        let path = '/weapi/song/enhance/player/url';
        let data = QENC({'ids': ids, 'br': 320000});
        return QREQ(path, data, function(data) {
            try {
                return data.data;
            } catch (e) {
                return [];
            }
        });
    };
    this.songLyric = function(music_id) {
        let path = '/weapi/song/lyric';
        let data = QENC({'os': 'osx', 'id': music_id, 'lv': '-1', 'kv': '-1', 'tv': '-1'});
        return QREQ(path, data);
    }
    this.mvUrl = function(mv_id) {
        return new Promise(function(resolve, reject) {
            let path = '/api/mv/detail?id=' + mv_id + '&type=mp4';
            REQUEST.sendRequestWithMethod(path, 'GET', null).then(function(data) {
                try {
                    data = JSON.parse(data).data;
                    resolve(data);
                } catch (e) {
                    resolve(null);
                }
            }).catch (function (status) {
                resolve(null);
            });
        });
    };
    this.playlistDetail = function(playlist_id) {
        let path = '/weapi/v3/playlist/detail';
        let data = QENC({'id': playlist_id, 'total': 'true', 'limit': 1000, 'n': 1000, 'offest': 10});
        return QREQ(path, data, function(data) {
            try {
                return data.playlist;
            } catch (e) {
                return null;
            }
        });
    };
    this.album = function(album_id) {
        let path = '/weapi/v1/album/' + album_id;
        return QREQ(path, QENC({}), function(data) {
            try {
                return {
                    'album': data.album,
                    'songs': data.songs
                };
            } catch (e) {
                return null;
            }
        });
    };

    this.login = function(username, password) {
        throw('not impl!');
        return new Promise(function(resolve, reject) {
            let path = '';
            let data = '';
            if (username.match(/^[0-9]+$/)) {
                path = '/weapi/login/cellphone';
                data = encrypted_request({'phone': username, 'password': password, 'rememberLogin': 'true'});
            } else {
                path = '/weapi/login';
                let client_token = '1_jVUMqWEPke0/1/Vu56xCmJpo5vP1grjn_SOVVDzOc78w8OKLVZ2JH7IfkjSXqgfmh';
                data = encrypted_request({'phone': username, 'password': password, 'rememberLogin': 'true', 'clientToken': client_token});
            }
            WEAPI._send_request(path, data)
                .then(function(response) {
                resolve(true);
            })
                .catch(function(status) {
                resolve(false);
            });
        });
    };

    this.test = function() {
        function funcLog(data) {
            console.log(data);
        }
        this.songsUrl([1333340512, 1308363066]).then(funcLog);
        this.playlistDetail(4945521505).then(funcLog);
        this.songLyric(1333340512).then(funcLog);
        this.songsDetail([1341532699]).then(funcLog);
        this.mvUrl(10847631).then(funcLog);
        this.album(75292754).then(funcLog);
    }
}
let WEAPI = new _WEAPI();
//WEAPI.test();

(function (root) {
    'use strict';
    /*
     * @opentdoor 的跨域下载接口
     */
    function Downloader() {
        // request
        function FileRequest(url, progress, callback) {
            var req = GM__xmlHttpRequest({
                method: 'GET',
                url: url,
                onprogress: function (res) {
                    if (progress) progress(res);
                },
                overrideMimeType: 'text/plain;charset=x-user-defined',
                onreadystatechange: function (res) {
                    if (res.readyState == 4) {
                        if (res.status == 200) {
                            var str = res.response;
                            var ta1 = [
                            ];
                            for (var i = 0; i < str.length; i++) {
                                ta1[i] = str.charCodeAt(i) & 255;
                            }
                            var ua8 = new Uint8Array(ta1);
                            var blob = new Blob([ua8]);
                            callback(blob, res.status);
                        } else {
                            callback(null, res.status);
                        }
                    }
                }
            });
        } //save file

        function SaveFile(blob, filename) {
            if (root.navigator.msSaveBlob) {
                root.navigator.msSaveBlob(blob, filename);
            } else {
                var anchor = root.document.createElement('a');
                var url = root.URL.createObjectURL(blob);
                anchor.download = filename;
                anchor.href = url;
                var evt = root.document.createEvent('MouseEvents');
                evt.initEvent('click', true, true);
                anchor.dispatchEvent(evt);
                root.URL.revokeObjectURL(url);
            }
        } //interface

        function FileDownload(url, filename, downloading, success, error) {
            FileRequest(url, downloading, function (blob, status) {
                if (status == 200) {
                    SaveFile(blob, filename);
                    if (typeof success == 'function') success();
                } else {
                    if (typeof error == 'function') error(status);
                }
            });
        }
        this.FileDownload = FileDownload;
        this.FileRequest = FileRequest;
        var anthorEvents = {
            onprogress: function (res) {
                if (this.anchor.getAttribute('data-res-action') == 'downloadDirect') {
                    if (res.lengthComputable) {
                        this.anchor.querySelector('i').innerText = '下载:' + (res.loaded * 100 / res.total).toFixed(2) + '%';
                    } else {
                        this.anchor.querySelector('i').innerText = '下载:' + anthorEvents.calcLength(res.loaded);
                    }
                } else {
                    if (res.lengthComputable) {
                        this.anchor.innerText = '下载:' + (res.loaded * 100 / res.total).toFixed(2) + '%';
                    } else {
                        this.anchor.innerText = '下载:' + anthorEvents.calcLength(res.loaded);
                    }
                }
            },
            calcLength: function (b) {
                b = Number(b) / 1024;
                if (b < 1024) {
                    return b.toFixed(1) + 'KB';
                }
                b = b / 1024;
                if (b < 1024) {
                    return b.toFixed(2) + 'MB';
                }
                b = b / 1024;
                return b.toFixed(3) + 'GB';
            },
            onsuccess: function () {
                this.anchor.innerHTML = this.Html;
                this.doing = false;
                if (this.anchor.id == 'tmp') {
                    this.anchor.previousElementSibling.remove();
                    this.anchor.remove();
                }
            },
            onerror: function () {
                this.anchor.innerHTML = '下载失败';
                this.handler = setTimeout(function (t) {
                    t.anchor.innerHTML = t.Html;
                    t.doing = false;
                }, 2000, this);
            },
            onAnthorClick: function (e) {
                e = e || event;
                var a = this.anchor;
                var ex = /([\w\s]+)(\.\w)(\?.*)?$/i.exec(a.href || '');
                var name = a.download || a.title;
                if (ex) {
                    if (!name && ex.length > 1) name = ex[1];
                    if (name && name.indexOf('.') == - 1 && ex.length > 2) name += ex[2];
                }
                if (!name || !a.href) return;
                e.preventDefault();
                if (this.doing) return;
                this.doing = true;
                FileDownload(a.href, name, anthorEvents.onprogress.bind(this), anthorEvents.onsuccess.bind(this), anthorEvents.onerror.bind(this));
            }
        };
        //interface
        function BindAnthor(a) {
            var env = {
                Html: a.innerHTML,
                anchor: a
            };
            a.addEventListener('click', anthorEvents.onAnthorClick.bind(env), true);
        }
        this.BindAnthor = BindAnthor;
    }
    var downloader = new Downloader();

    var innerFrame = document.querySelector('iframe');
    var tit, // 标题
        cov, // 封面
        dl, // 下载按钮
        fileName, // 文件名
        mvId = '', // mvID
        allDownloadButton, // 歌单/专辑页的下载按钮
        downloadPics; // 歌单/专辑页的歌曲封面下载按钮

    // api这个东西的调用方先不改,有空再说。内部实现替换成WEAPI。
    var api = {
        detail: function(songIds, callback) {
            WEAPI.songsUrl(songIds).then(function(data) {
                if (data) {
                    let br = data[0].br;
                    let url = data[0].url;
                    innerFrame.contentWindow.document.querySelector('#wyyyydda').setAttribute('data-br', br);
                    innerFrame.contentWindow.document.querySelector('#wyyyydda').src = url;
                }
            });
        },
        media: function (songId, callback, index) {
            WEAPI.songLyric(songId).then(function(data) {
                if (data) {
                    callback(data, index);
                }
            });
        },
        mv: function (mvId, callback) {
            WEAPI.mvUrl(mvId).then(function(data) {
                var brs = [240, 480, 720, 1080];
                for (var i = brs.length - 1; i >= 0; --i) {
                    if (data.brs[brs[i]]) {
                        callback(data.brs[brs[i]], brs[i]);
                        break;
                    }
                }
            });
        },
    };
    var pages = [
        {
            url: 'http://music.163.com/#/song?id=',
            handler: function () {
                var innerFrameDoc = innerFrame.contentWindow.document;
                var albumNode = innerFrameDoc.querySelectorAll('p.des.s-fc4') [1];
                tit = innerFrameDoc.querySelector('.tit');
                cov = innerFrameDoc.querySelector('.u-cover > img');
                dl = innerFrameDoc.querySelector('.u-btni-dl');
                // TODO 当artist多个的时候文件名中只包含第一个(不多网易UWP也是这么干的先这样吧)
                fileName = tit.querySelector('.f-ff2').innerText + ' - ' + tit.parentNode.nextElementSibling.querySelector('.s-fc7').innerText + '.';
                var parentNode = albumNode.parentNode;
                var songId = location.href.match(/id=([0-9]+)/) [1];
                var mvHref = innerFrameDoc.querySelector('a[title="播放mv"]');
                if(mvHref) {
                    mvId = mvHref.href.split('=')[1];
                }
                var downloadLine = this.createDownloadLine(songId);
                parentNode.insertBefore(downloadLine, albumNode.nextElementSibling);
            },
            createDownloadLine: function (songId) {
                var disableStyle = function (link) {
                    link.text += '(无)';
                    link.style.color = 'gray';
                    link.style.textDecoration = 'none';
                    link.style.cursor = 'auto';
                };
                var mp3Link = this.createMP3Link();
                var lyricLink = this.createLink('歌词');
                var tlyricLink = this.createLink('翻译');
                var coverLink = this.createLink('封面');
                var mp3Help = this.createLink('如何下载歌曲?');
                var showPlayer = this.createLink('歌曲试听');
                mp3Help.addEventListener('click', function () {
                    alert('点击下方的下载按钮之后等待即可。');
                });
                showPlayer.addEventListener('click', function () {
                    var player = innerFrame.contentWindow.document.querySelector('#wyyyydda');
                    player.setAttribute('controls', 'true');
                    player.play();
                });
                showPlayer.id = 'wyyyysp';
                lyricLink.setAttribute('download', fileName + 'lrc');
                tlyricLink.setAttribute('download', fileName + 'lrc');
                coverLink.setAttribute('download', fileName + 'jpg');
                coverLink.href = cov.getAttribute('data-src');
                downloader.BindAnthor(coverLink);
                downloader.BindAnthor(dl);
                dl.href = 'javascript:;';
                dl.setAttribute('data-res-action', 'downloadDirect');
                dl.querySelector('i').innerText = '下载(稍候)';
                api.detail([songId]);
                api.media(songId, function (result) {
                    if (result.lrc && result.lrc.lyric) {
                        lyricLink.href = 'data:text/plain;charset=utf-8,' + encodeURIComponent(result.lrc.lyric);
                    } else {
                        disableStyle(lyricLink);
                    }
                    if (result.tlyric && result.tlyric.lyric) {
                        tlyricLink.href = 'data:text/plain;charset=utf-8,' + encodeURIComponent(result.tlyric.lyric);
                    } else {
                        disableStyle(tlyricLink);
                    }
                });
                var container = this.createLineContainer('下载');
                container.appendChild(lyricLink); // 原文lrc
                container.appendChild(tlyricLink); // 翻译lrc
                container.appendChild(coverLink); // 封面
                if (mvId != '') { // MV下载,没有不显示
                    var el = this.createLink('MV');
                    el.setAttribute('download', fileName + 'mp4');
                    api.mv(mvId, function(result, br) {
                        el.innerHTML += '(' + br + 'P)';
                        el.href = result;
                    });
                    downloader.BindAnthor(el);
                    container.appendChild(el);
                }
                container.appendChild(mp3Help); // 帮助
                container.appendChild(showPlayer); // 试听
                container.appendChild(document.createElement('br'));
                container.appendChild(mp3Link); // audio标签
                return container;
            },
            createLink: function (label) {
                var link = document.createElement('a');
                link.innerHTML = label;
                link.className = 's-fc7';
                link.style.marginRight = '10px';
                link.href = 'javascript:;';
                return link;
            },
            createMP3Link: function () {
                var link = document.createElement('audio');
                link.setAttribute('id', 'wyyyydda');
                link.style.marginTop = '10px';
                link.addEventListener('canplay', function () {
                    dl.href = this.src;
                    dl.setAttribute('download', fileName + 'mp3');
                    var br = '(' + parseInt(parseInt(innerFrame.contentWindow.document.querySelector('#wyyyydda').getAttribute('data-br')) / 1000) + 'K)';
                    dl.querySelector('i').innerText = '下载' + br;
                });
                link.addEventListener('error', function () {
                    //alert('无法加载此歌曲。可能此歌曲需要付费或版权受限。');
                    dl.querySelector('i').innerText = '无法下载';
                });
                return link;
            },
            createLineContainer: function (label) {
                var container = document.createElement('p');
                container.className = 'desc s-fc4';
                container.innerHTML = label + ':';
                container.style.margin = '10px 0';
                return container;
            },
        },
        {
            url: [
                'http://music.163.com/#/playlist?id=', // 歌单
                'http://music.163.com/#/artist?id=', // 歌手
                'http://music.163.com/#/discover/toplist', // 榜单
                'http://music.163.com/#/album?id=', // 专辑
                'http://music.163.com/#/discover/recommend/taste' // 每日推荐
            ],
            handler: function () {
                var innerFrameDoc = innerFrame.contentWindow.document;
                allDownloadButton = innerFrameDoc.querySelector('.u-btni-dl');
                var downloadButtons = (innerFrameDoc).querySelectorAll('span.icn-dl');

                var cover = innerFrameDoc.querySelector('.cover>img'); // 封面图片<img>
                var mask = innerFrameDoc.querySelector('.cover>span'); // 封面图片后一个元素,用来加<a></a>
                if (cover && (location.href.indexOf('#/playlist') != -1 || location.href.indexOf('#/album') != -1)) {
                    var title = innerFrameDoc.querySelector('.tit>h2').innerText; // 专辑名称/歌单名称
                    var url = cover.src.split('?')[0]; // 封面的链接
                    mask.outerHTML = '<a download="' + title + '.jpg" href=" ' + url +  '">' + mask.outerHTML + '</a>';
                    mask = innerFrameDoc.querySelector('.cover>a');
                    downloader.BindAnthor(mask);

                    allDownloadButton.addEventListener('click', this.downloadLyricsZip);
                    allDownloadButton.setAttribute('data-res-action', 'downloadLyrics');
                    allDownloadButton.childNodes[0].innerText = '歌词下载';

                    if (location.href.indexOf('#/playlist') != -1) {
                        downloadPics = allDownloadButton.cloneNode(true);
                        downloadPics.addEventListener('click', this.downloadAlbumImageZip);
                        downloadPics.setAttribute('data-res-action', 'downloadLyrics');
                        downloadPics.childNodes[0].innerText = '歌单中歌曲封面下载';
                        downloadPics.title = '点击左侧图片下载歌单封面';
                        allDownloadButton.parentNode.insertBefore(downloadPics, allDownloadButton.nextSibling);
                    }
                }

                var audio = document.createElement('audio');
                audio.setAttribute('id', 'wyyyydda');
                audio.addEventListener('canplay', function () {
                    var a = document.createElement('a');
                    a.setAttribute('href', this.src);
                    a.setAttribute('download', this.getAttribute('data-fileName') + '.mp3');
                    a.setAttribute('id', 'tmp');
                    downloader.BindAnthor(a);
                    var _cele = innerFrameDoc.querySelector('[downloading]');
                    _cele.parentNode.appendChild(document.createElement('br'));
                    _cele.parentNode.appendChild(a);
                    _cele.removeAttribute('downloading');
                    a.click();
                });
                audio.addEventListener('error', function () {
                    alert('无法加载此歌曲。可能此歌曲需要付费或版权受限。');
                });
                innerFrameDoc.querySelector('body').appendChild(audio);

                this.replaceAction(innerFrameDoc, downloadButtons);
            },
            replaceAction: function (innerFrameDoc, downloadButtons) {
                innerFrame.contentWindow.document.querySelectorAll('.js-dis').forEach(function(ele){
                    ele.className = ele.className.replace('js-dis','');
                    ele.querySelectorAll('b[title]').forEach(function(b){
                        b.style.color='#aeaeae';
                    });
                });
                for (var i = 0; i < downloadButtons.length; i++) {
                    if (downloadButtons[i].getAttribute('data-res-action') == 'download') {
                        downloadButtons[i].setAttribute('data-res-action', 'downloadDirect');
                        downloadButtons[i].addEventListener('click', function () {
                            var id = this.getAttribute('data-res-id');
                            innerFrame.contentWindow.document.querySelector('#wyyyydda').setAttribute('data-fileName',this.previousElementSibling.getAttribute('data-res-name') + ' - ' + this.previousElementSibling.getAttribute('data-res-author'));
                            innerFrame.contentWindow.document.querySelector('#wyyyydda').setAttribute('data-res-id', id);
                            this.setAttribute('downloading','true');
                            api.detail([id], null);
                        });
                    }
                }
            },
            downloadLyricsZip: function(){
                function artistListToString(ar) {
                    let str = '';
                    if (ar == null) {
                        return str;
                    }
                    for (let i = 0; i < ar.length; ++i) {
                        if (str != '') {
                            str += ' & ';
                        }
                        str += ar[i].name;
                    }
                    str = str.trim();
                    return str;
                }
                if (location.href.indexOf('#/playlist') != -1 || location.href.indexOf('#/album') != -1) {
                    let playListId = location.href.split('id=')[1];
                    let isPlayList = location.href.indexOf('#/playlist') != -1;
                    function onComplete(result) {
                        let initialText = allDownloadButton.childNodes[0].innerText;
                        let failedText = '歌词下载失败...';
                        try {
                            if (!result) {
                                return;
                            }
                            var zip = new JSZip();
                            var tracks = isPlayList ? result.tracks : result.songs;
                            let zipFileName = '';
                            if (isPlayList) {
                                zipFileName = result.creator.nickname + ' - ' + result.name;
                            } else {
                                zipFileName = artistListToString(result.album.artists);
                                if (zipFileName != '') {
                                    zipFileName += ' - ';
                                }
                                zipFileName += result.album.name;
                            }
                            zipFileName += ' - 歌词';
                            zipFileName = zipFileName.replace(/[\/:*?"<>|]*/g, '').trim();
                            if (isPlayList) {
                                if (result.description) {
                                    zip.file("歌单简介.txt", result.description.replace(/\n/g, '\r\n'));
                                }
                            } else {
                                if (result.album.description) {
                                    zip.file("专辑简介.txt", result.album.description.replace(/\n/g, '\r\n'));
                                }
                            }
                            zip.file("说明.txt", "lyrics:.lrc格式的滚动歌词\r\nnlyrics:.txt格式的歌词\r\ntlyrics:.lrc格式的翻译后的滚动歌词\r\n\r\n记事本打开可能不换行,请尝试notepad++等软件\r\n\r\n由\"网易云音乐直接下载\"脚本下载。\r\n脚本链接:http://greasyfork.icu/zh-CN/scripts/33046"); // 不要删除好不好(✺ω✺)
                            if (isPlayList) {
                                zip.file("歌单链接.url", "[{000214A0-0000-0000-C000-000000000046}]\nProp3=19,2\n[InternetShortcut]\nIDList=\nURL=http://music.163.com/#/playlist?id=" + playListId);
                            } else {
                                zip.file("专辑链接.url", "[{000214A0-0000-0000-C000-000000000046}]\nProp3=19,2\n[InternetShortcut]\nIDList=\nURL=http://music.163.com/#/album?id=" + playListId);
                            }

                            function downloadLyricAsync(index) {
                                let fileName = artistListToString(tracks[index].ar);
                                if (fileName != '') {
                                    fileName += ' - ';
                                }
                                fileName += tracks[index].name;
                                // 删除文件中不允许的字符
                                fileName = fileName.replace(/[\/:*?"<>|]*/g, '').trim();

                                allDownloadButton.childNodes[0].innerText = '正在下载:' + index + '/' + tracks.length;
                                function onComplete(data) {
                                    if (data.lrc && data.lrc.lyric) {
                                        // 这句保留两位小数
                                        zip.file('lyrics\\' + fileName + ".lrc", data.lrc.lyric.replace(/\[(\d\d.\d\d.\d\d)\d\]/g, '[$1]'));
                                        // 这句保留原来的格式(原来几位就几位)
                                        //zip.file('lyrics\\' + fileName + ".lrc", result.lrc.lyric);
                                        zip.file('nlyrics\\' + fileName + ".txt", data.lrc.lyric.replace(/\[.*]/g, '')); // :: /\[\d\d:\d\d.\d{2,3}]/g
                                    }
                                    if (data.tlyric && data.tlyric.lyric) {
                                        zip.file('tlyrics\\' + fileName + ".lrc", data.tlyric.lyric.replace(/\[(\d\d.\d\d.\d\d)\d\]/g, '[$1]'));
                                    }
                                    allDownloadButton.childNodes[0].innerText = index + '/' + tracks.length;
                                    if (index == tracks.length - 1) {
                                        zip.generateAsync({type:"blob"})
                                            .then(function(content) {
                                            saveAs(content, zipFileName + ".zip");
                                            allDownloadButton.childNodes[0].innerText = initialText;
                                        });
                                    } else {
                                        downloadLyricAsync(index + 1);
                                    }
                                }

                                api.media(tracks[index].id, onComplete);
                            }

                            downloadLyricAsync(0);
                        } catch(e) {
                            alert(failedText);
                            allDownloadButton.childNodes[0].innerText = initialText;
                        }
                    }
                    if (isPlayList) {
                        WEAPI.playlistDetail(playListId).then(onComplete);
                    } else {
                        WEAPI.album(playListId).then(onComplete);
                    }
                }
            },
            downloadAlbumImageZip: function() {
                function artistListToString(ar) {
                    let str = '';
                    if (ar == null) {
                        return str;
                    }
                    for (let i = 0; i < ar.length; ++i) {
                        if (str != '') {
                            str += ' & ';
                        }
                        str += ar[i].name;
                    }
                    str = str.trim();
                    return str;
                }
                try {
                    if (location.href.indexOf('#/playlist') != -1) {
                        var playListId = location.href.split('id=')[1];
                        let initialText = downloadPics.childNodes[0].innerText;
                        WEAPI.playlistDetail(playListId).then(function (result) {
                            try{
                                if (result == null) {
                                    throw("封面下载失败...");
                                }
                                var zip = new JSZip();
                                if (result.description)
                                    zip.file("歌单介绍.txt", result.description.replace(/\n/g, '\r\n'));
                                zip.file("说明.txt", "由\"网易云音乐直接下载\"脚本下载。\r\n脚本链接:http://greasyfork.icu/zh-CN/scripts/33046");
                                zip.file("歌单链接.url", "[{000214A0-0000-0000-C000-000000000046}]\nProp3=19,2\n[InternetShortcut]\nIDList=\nURL=http://music.163.com/#/playlist?id=" + playListId);
                                var tracks = result.tracks;
                                var zipFileName = result.creator.nickname + ' - ' + result.name + ' - 封面';
                                zipFileName = zipFileName.replace(/[\/:*?"<>|]*/g, '').trim();

                                function downloadAlbumImageAsync(index) {
                                    let fileName = artistListToString(tracks[index].ar);
                                    if (fileName != '') {
                                        fileName += ' - ';
                                    }
                                    fileName += tracks[index].name;
                                    // 删除文件中不允许的字符
                                    fileName = fileName.replace(/[\/:*?"<>|]*/g, '').trim();
                                    let url = tracks[index].al.picUrl;
                                    function onProgress(progress) {
                                        downloadPics.childNodes[0].innerText = '正在下载:' + index + '/' + tracks.length;
                                    };
                                    function onComplete(blob, status) {
                                        if (status == 200) {
                                            zip.file('pics\\' + fileName + url.match('\.[a-zA-Z]+$')[0], blob);
                                        }
                                        if (index == tracks.length - 1) {
                                            zip.generateAsync({type:"blob"})
                                                .then(function(content) {
                                                saveAs(content, zipFileName + ".zip");
                                                downloadPics.childNodes[0].innerText = initialText;
                                            });
                                        } else {
                                            downloadAlbumImageAsync(index + 1);
                                        }
                                    }

                                    if (tracks[index].al && url && url != '') {
                                        downloader.FileRequest(url, onProgress, onComplete);
                                    } else {
                                        onComplete(null, 404);
                                    }
                                }

                                downloader.FileRequest(result.coverImgUrl, null, function(blob, status) {
                                    if (status == 200) {
                                        zip.file(result.name + ' - ' + result.creator.nickname + result.coverImgUrl.match('\.[a-zA-Z]+$')[0], blob);
                                    }
                                    downloadAlbumImageAsync(0);
                                });
                            } catch (e) {
                                alert('封面下载失败...');
                                downloadPics.childNodes[0].innerText = initialText;
                            }
                        });
                    }
                }
                catch(e) {
                    alert('封面下载失败...');
                    downloadPics.childNodes[0].innerText = initialText;
                }
            }
        },
    ];
    function matchPagesURL(href, urls) {
        var ret = false;
        var t = location.href.split('://') [1];
        if (Array.isArray(urls)) {
            urls.forEach(function (ele) {
                if (t.indexOf(ele.split('://') [1]) === 0) {
                    ret = true;
                    return;
                }
            });
        } else {
            if (t.indexOf(urls.split('://') [1]) === 0) {
                ret = true;
            }
        }
        return ret;
    }
    if (innerFrame) {
        innerFrame.addEventListener('load', function () {
            var i,
                page;
            for (i = 0; i < pages.length; i += 1) {
                page = pages[i];
                if (matchPagesURL(location.href, page.url)) {
                    page.handler();
                }
            }
        });
    }
}) (window);