Greasy Fork

Greasy Fork is available in English.

网易云音乐直接下载

在单曲页面显示歌词、翻译、封面、歌曲下载链接并以高音质试听,同时支持歌单、专辑、等页面直接下载单曲。该脚本受 GPL V2.0 许可证保护。脚本仍在测试,如遇到错误,请反馈网址。

当前为 2017-10-20 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name        网易云音乐直接下载
// @namespace   https://www.ocrosoft.com/
// @description 在单曲页面显示歌词、翻译、封面、歌曲下载链接并以高音质试听,同时支持歌单、专辑、等页面直接下载单曲。该脚本受 GPL V2.0 许可证保护。脚本仍在测试,如遇到错误,请反馈网址。
// @match       *://music.163.com/*
// @grant       GM_xmlhttpRequest
// @version     2.0
// ==/UserScript==
/**
 * 修改自 http://greasyfork.icu/zh-CN/scripts/10548-%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%E4%B8%8B%E8%BD%BD
 * API 源码: https://github.com/Ocrosoft/NetEase_OutChain
 * 此脚本开源,API开源,但请不要在未经允许的情况下在其他程序中调用此脚本中的API,在非程序调用且小流量的个人使用场景下可以使用。
**/

/**
 *
 */

(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) & 0x00ff;
                            }
                            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;
        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 api = {

        detailUrl: function(songIds) {
            var tpl = (location.href.indexOf('https') == -1 ? 'http://music.ocrosoft.com/GetMusicLinkt.php?id=${songIds}': 'https://www.ocrosoft.com/outchain/GetMusicLinkt.php?id=${songIds}');

            return tpl.replace('${songIds}', songIds.join(','));
        },

        detail: function(songIds, callback, username=null, password=null) {
            var script = document.createElement('script');
            script.innerHTML = 'var returnMusicData;';
            document.body.appendChild(script);
            returnMusicData = function(data) {
                callback(data);
            };
            script = document.createElement('script');
            var url = this.detailUrl(songIds) + '&callback=returnMusicData';
            if(username) url += '&username=' + username + '&password=' + password;
            script.setAttribute('src', url);
            document.body.appendChild(script);
        },

        mediaUrl: function(songId) {
            return (location.href.indexOf('https') == -1 ? 'http': 'https') + '://music.163.com/api/song/lyric?os=pc&id=' + songId + '&lv=-1&kv=-1&tv=-1';
        },

        media: function(songId, callback) {
            var req = new XMLHttpRequest();
            req.open('GET', this.mediaUrl(songId), true);
            req.onload = function() {
                callback(JSON.parse(this.responseText));
            };
            req.send();
        },

    };

    var innerFrame = document.querySelector('iframe');
    var tit, cov, dl;
    var fileName;

    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').querySelector('img');
            dl = innerFrameDoc.querySelector('.u-btni-dl');
            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 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();
            });
            lyricLink.setAttribute('download', fileName + 'lrc');
            tlyricLink.setAttribute('download', fileName.substring(0, fileName.length - 1) + '(翻译).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],
                       function(result) {
                mp3Link.src = result.mp3Url;
            });
            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); // 封面
            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');
                dl.querySelector('i').innerText = '下载';
            });
            link.addEventListener('error',
                                  function() {
                    var username = prompt('无法加载歌曲。付费歌曲请输入会员手机号');
                    if(username !== null){
                        var password = prompt('请输入密码。(不会记录,仅用于模拟登陆)');
                        var _self = this;
                        if(password !== null){
                            api.detail([location.href.match(/id=([0-9]+)/)[1]],
                                       function(result) {
                                _self.src = result.mp3Url;
                            }, username, password);
                        }
                    }
                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;
                         var downloadButtons = (innerFrameDoc).querySelectorAll('span.icn-dl');

                         this.replaceAction(innerFrameDoc, downloadButtons);
                     },
                     replaceAction: function(innerFrameDoc, downloadButtons) {
                         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');
                                     var ele = this;
                                     var nameAndAuthor = this.previousElementSibling.getAttribute('data-res-name') + ' - ' + this.previousElementSibling.getAttribute('data-res-author');
                                     api.detail([id],
                                                function(res) {
                                         var audio = document.createElement('audio');
                                         audio.setAttribute('src', res.mp3Url);
                                         audio.setAttribute('id', 'wyyyydda');
                                         audio.addEventListener('canplay',
                                                                function() {
                                             var a = document.createElement('a');
                                             a.setAttribute('href', this.src);
                                             a.setAttribute('download', nameAndAuthor + '.mp3');
                                             a.setAttribute('id', 'tmp');
                                             downloader.BindAnthor(a);
                                             ele.parentNode.appendChild(document.createElement('br'));
                                             ele.parentNode.appendChild(a);
                                             a.click();
                                         });

                                         audio.addEventListener('error',
                                                                function() {
                                             var _id = id;
                                             var username = prompt('无法加载歌曲。付费歌曲请输入会员手机号。');
                                             if(username !== null){
                                                 var password = prompt('请输入密码。(不会记录,仅用于模拟登陆)');
                                                 var _self = this;
                                                 if(password !== null){
                                                     api.detail([_id],
                                                                function(result) {
                                                         _self.src = result.mp3Url;
                                                     }, username, password);
                                                 }
                                             }
                                         });
                                     });
                                 });
                             }
                         }
                     }
                 },
                ];

    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);