Skip to content

Instantly share code, notes, and snippets.

@ccloli
Last active May 27, 2020 03:33
Show Gist options
  • Save ccloli/c2089aa9f674f9f70ecd to your computer and use it in GitHub Desktop.
Save ccloli/c2089aa9f674f9f70ecd to your computer and use it in GitHub Desktop.
Hook XHR to hack NE
var originalXMLHttpRequest = XMLHttpRequest;
var fakeXMLHttpRequest = function(){
var __this__ = this;
var _this = new originalXMLHttpRequest();
var _this_proto = _this.constructor.prototype;
Object.keys(originalXMLHttpRequest).forEach(function(elem){
if (typeof originalXMLHttpRequest[elem] === 'function') {
__this__[elem] = function(){
_this[elem].apply(_this, arguments);
};
}
else {
var property = {};
var originalProperty = Object.getOwnPropertyDescriptor(originalXMLHttpRequest, elem);
property.get = function(){ return _this[elem]; };
if (originalProperty.set) property.set = function(val){ return _this[elem] = val; };
Object.defineProperty(__this__, elem, property);
}
});
Object.keys(_this_proto).forEach(function(elem){
if (elem in __this__) return;
if (elem === 'responseText') return;
if (typeof _this[elem] === "function") {
if (elem === 'open') { // add requestURL support
__this__[elem] = function(){
_this[elem].apply(_this, arguments);
__this__.requestURL = arguments[1];
};
}
else {
__this__[elem] = function(){
_this[elem].apply(_this, arguments);
};
}
}
else {
var property = {};
var originalProperty = Object.getOwnPropertyDescriptor(_this_proto, elem);
property.get = function(){ return _this[elem]; };
if (originalProperty.set) property.set = function(val){ return _this[elem] = val; };
Object.defineProperty(__this__, elem, property);
}
});
// modified example
Object.defineProperty(__this__, 'responseText', {get: function(){ return _this.responseText.replace(/123/, 'xyz'); }});
this.originalXMLHttpRequest = _this;
}
window.XMLHttpRequest = fakeXMLHttpRequest;
/*
example:
var xhr = new XMLHttpRequest();
xhr.open('GET', '/example');
xhr.send();
xhr.originalXMLHttpRequest.responseText; // 1234567890
xhr.responseText; // xyz4567890
*/
// ==UserScript==
// @name 网易云音乐高音质支持
// @version 2.1
// @description 去除网页版网易云音乐仅可播放低音质(96Kbps)的限制,强制播放高音质版本
// @match *://music.163.com/*
// @include *://music.163.com/*
// @author 864907600cc
// @icon https://secure.gravatar.com/avatar/147834caf9ccb0a66b2505c753747867
// @run-at document-start
// @grant none
// @namespace http://ext.ccloli.com
// ==/UserScript==
// getTrackURL 源码来自 Chrome 扩展程序 网易云音乐增强器(Netease Music Enhancement) by wanmingtom@gmail.com
// 菊苣这个加密算法你是怎么知道的 _(:3
var getTrackURL = function getTrackURL (dfsId) {
var byte1 = '3' + 'g' + 'o' + '8' + '&' + '$' + '8' + '*' + '3'
+ '*' + '3' + 'h' + '0' + 'k' + '(' + '2' + ')' + '2';
var byte1Length = byte1.length;
var byte2 = dfsId + '';
var byte2Length = byte2.length;
var byte3 = [];
for (var i = 0; i < byte2Length; i++) {
byte3[i] = byte2.charCodeAt(i) ^ byte1.charCodeAt(i % byte1Length);
};
byte3 = byte3.map(function(i) {
return String.fromCharCode(i)
}).join('');
results = CryptoJS.MD5(byte3).toString(CryptoJS.enc.Base64);
results = results.replace(/\//g, '_').replace(/\+/g, '-');
// 使用 p1 cdn 解决境外用户无法播放的问题
var url = 'http://p1.music.126.net/' + results + '/' + byte2 + '.mp3';
return url;
}
var modifyURL = function modifyURL(data, parentKey) {
console.log('API 返回了 ' + data.length + ' 首曲目');
console.log('施放魔法!变变变!');
data.forEach(function(elem){
// 部分音乐没有高音质
if (!parentKey) elem.mp3Url = getTrackURL(elem.hMusic ? elem.hMusic.dfsId : elem.mMusic ? elem.mMusic.dfsId : elem.lMusic.dfsId);
else elem[parentKey].mp3Url = getTrackURL(elem[parentKey].hMusic ? elem.hMusic[parentKey].dfsId : elem[parentKey].mMusic ? elem.mMusic[parentKey].dfsId : elem.lMusic[parentKey].dfsId);
});
return data;
}
// 重新编写脚本,改用 hook xhr 的形式替换 URL 链接
var originalXMLHttpRequest = window.XMLHttpRequest;
var fakeXMLHttpRequest = function(){
var __this__ = this;
var _this = new originalXMLHttpRequest();
var _this_proto = _this.constructor.prototype;
Object.keys(originalXMLHttpRequest).forEach(function(elem){
if (typeof originalXMLHttpRequest[elem] === 'function') {
__this__[elem] = function(){
_this[elem].apply(_this, arguments);
};
}
else {
var property = {};
var originalProperty = Object.getOwnPropertyDescriptor(originalXMLHttpRequest, elem);
property.get = function(){ return _this[elem]; };
if (originalProperty.set) property.set = function(val){ return _this[elem] = val; };
Object.defineProperty(__this__, elem, property);
}
});
Object.keys(_this_proto).forEach(function(elem){
if (elem in __this__) return;
if (elem === 'responseText') return;
if (typeof _this[elem] === 'function') {
if (elem === 'open') { // add requestURL support
__this__[elem] = function(){
//console.log(elem, arguments);
if (arguments[1].indexOf('/enhance/player/') >= 0) {
// 对新版 api 请求旧的 api 接口
__this__.ping = new fakeXMLHttpRequest();
__this__.ping.open(arguments[0], arguments[1].replace('/enhance/player/url', '/detail'), false); // 不使用异步 xhr 以阻断原 xhr 请求
__this__.ping.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
}
_this[elem].apply(_this, arguments);
__this__.requestURL = arguments[1];
/*
if (arguments[1].indexOf('/enhance/player/')) {
arguments[1] = arguments[1].replace('/enhance/player/url', '/detail');
}*/
};
}
else if (elem === 'send') { // add requestURL support
__this__[elem] = function(){
//console.log(elem, arguments);
//console.log(__this__.ping)
if (__this__.requestURL.indexOf('/enhance/player/') >= 0 && __this__.ping) __this__.ping.send(arguments[0]);
_this[elem].apply(_this, arguments);
};
}
else {
__this__[elem] = function(){
//console.log(elem, arguments);
_this[elem].apply(_this, arguments);
};
}
}
else {
var property = {};
var originalProperty = Object.getOwnPropertyDescriptor(_this_proto, elem);
property.get = function(){ /*console.log(elem);*/ return _this[elem]; };
if (originalProperty.set) property.set = function(val){ return _this[elem] = val; };
Object.defineProperty(__this__, elem, property);
}
});
Object.defineProperty(__this__, 'responseText', {
get: function(){
//console.log(_this.responseText);
//console.log('Request URL: ' + __this__.requestURL);
try {
if (__this__.requestURL.indexOf('/weapi/') < 0) return _this.responseText;
var action = __this__.requestURL.split('/weapi/')[1].split('?')[0].split('/');
switch (action[0]) {
case 'album':
var res = JSON.parse(_this.responseText);
modifyURL(res.album.songs);
return JSON.stringify(res);
break;
case 'song':
if (action[1] !== 'detail') {
if (action[1] !== 'enhance' && action[2] !== 'player' && action[3] !== 'url') return _this.responseText;
var res = JSON.parse(_this.responseText);
/*window.ping = __this__.ping;
console.log(__this__.ping);*/
if (__this__.ping) res.data[0].url = JSON.parse(__this__.ping.responseText).songs[0].mp3Url; // 替换旧版 api 的 url
return JSON.stringify(res);
}
var res = JSON.parse(_this.responseText);
modifyURL(res.songs);
return JSON.stringify(res);
break;
case 'playlist':
if (action[1] !== 'detail') return _this.responseText;
var res = JSON.parse(_this.responseText);
modifyURL(res.result.tracks);
return JSON.stringify(res);
break;
case 'dj':
if (action[2] === 'byradio') {
var res = JSON.parse(_this.responseText);
modifyURL(res.programs, 'mainSong');
return JSON.stringify(res);
}
else if (action[2] === 'detail') {
var res = JSON.parse(_this.responseText);
res.program = modifyURL([res.program], 'mainSong')[0];
return JSON.stringify(res);
}
else return _this.responseText;
break;
case 'v3':
switch (action[1]){
// http://music.163.com/weapi/v3/playlist/detail
case 'playlist':
if (action[2] !== 'detail') return _this.responseText;
var res = JSON.parse(_this.responseText);
res.privileges.forEach(function(elem){
var q = elem.pl || elem.dl || elem.fl || Math.min(elem.maxbr, 320000) || 320000;
elem.st = 0;
elem.pl = q;
elem.dl = q;
elem.fl = q;
});
if (res.privileges.length < res.playlist.trackIds.length && res.playlist.trackIds.length === res.playlist.tracks.length) {
// 对超过 1000 的播放列表补充播放信息(需魔改 core.js)
for (var i = res.privileges.length; i < res.playlist.trackIds.length; i++) {
var q = res.playlist.tracks.h ? res.playlist.tracks.h.br : res.playlist.tracks.m ? res.playlist.tracks.m.br : res.playlist.tracks.l ? res.playlist.tracks.l.br : res.playlist.tracks.a ? res.playlist.tracks.a.br : 320000;
res.privileges.push({
cp: 1,
cs: false,
dl: q,
fee: 0,
fl: q,
id: res.playlist.trackIds[i].id,
maxbr: q,
payed: 0,
pl: q,
sp: 7,
st: 0,
subp: 1
});
}
}
console.log(res);
return JSON.stringify(res);
break;
}
break;
default:
return _this.responseText;
}
}
catch (error) {
// 以防 api 转换失败也能正常返回数据
console.error('转换出错!', error);
return _this.responseText;
}
}
});
this.originalXMLHttpRequest = _this;
}
window.XMLHttpRequest = fakeXMLHttpRequest;
// ==UserScript==
// @name 百度网盘自定义密码
// @version 1.0
// @description 自定义百度网盘加密分享密码
// @match *://pan.baidu.com/*
// @include *://pan.baidu.com/*
// @match *://yun.baidu.com/*
// @include *://yun.baidu.com/*
// @author 864907600cc
// @icon https://secure.gravatar.com/avatar/147834caf9ccb0a66b2505c753747867
// @run-at document-start
// @grant none
// @namespace http://ext.ccloli.com
// ==/UserScript==
var originalXMLHttpRequest = window.XMLHttpRequest;
var fakeXMLHttpRequest = function(){
var __this__ = this;
var _this = new originalXMLHttpRequest();
var _this_proto = _this.constructor.prototype;
Object.keys(originalXMLHttpRequest).forEach(function(elem){
if (typeof originalXMLHttpRequest[elem] === 'function') {
__this__[elem] = function(){
_this[elem].apply(_this, arguments);
};
}
else {
var property = {};
var originalProperty = Object.getOwnPropertyDescriptor(originalXMLHttpRequest, elem);
property.get = function(){ return _this[elem]; };
if (originalProperty.set) property.set = function(val){ return _this[elem] = val; };
Object.defineProperty(__this__, elem, property);
}
});
Object.keys(_this_proto).forEach(function(elem){
if (elem in __this__) return;
if (typeof _this[elem] === 'function') {
if (elem === 'open') { // add requestURL support
__this__[elem] = function(){
_this[elem].apply(_this, arguments);
__this__.requestURL = arguments[1];
};
}
else if (elem === 'send') { // add requestURL support
__this__[elem] = function(){
if (__this__.requestURL.indexOf('/share/set') >= 0) {
console.log(arguments[0]);
if (arguments[0].indexOf('&pwd=') >= 0) {
var passed = 1;
var pwd = prompt('请输入分享密码(仅限 4 字节,不能大于或小于 4 字节,一个汉字为 3 字节)\n若不输入则使用默认生成的密码\n注意:私密链接结果页的密码可能与输入的密码不一致,以输入的密码为准');
if (pwd) arguments[0] = [arguments[0].split('&pwd=')[0], encodeURIComponent(pwd)].join('&pwd=');
}
}
_this[elem].apply(_this, arguments);
};
}
else {
__this__[elem] = function(){
//console.log(elem, arguments);
_this[elem].apply(_this, arguments);
};
}
}
else {
var property = {};
var originalProperty = Object.getOwnPropertyDescriptor(_this_proto, elem);
property.get = function(){ return _this[elem]; };
if (originalProperty.set) property.set = function(val){ return _this[elem] = val; };
Object.defineProperty(__this__, elem, property);
}
});
this.originalXMLHttpRequest = _this;
};
window.XMLHttpRequest = fakeXMLHttpRequest;
@ccloli
Copy link
Author

ccloli commented Dec 19, 2015

User Script 只用作实例展示,故不再更新。如需要后续更新请前往 https://github.com/FirefoxBar/userscript/tree/master/163_Music_High_Quality_Support

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment