Greasy Fork is available in English.

豆瓣FM 红心歌单恢复播放

豆瓣FM网页版 可以解锁红心歌单里不能放的歌曲

// ==UserScript==
// @name         豆瓣FM 红心歌单恢复播放
// @namespace    http://thatwind.com/
// @version      0.2
// @description  豆瓣FM网页版 可以解锁红心歌单里不能放的歌曲
// @icon         data:image/svg+xml;charset=utf-8;base64,PHN2ZyBzdHlsZT0nd2lkdGg6IDFlbTsgaGVpZ2h0OiAxZW07dmVydGljYWwtYWxpZ246IG1pZGRsZTtmaWxsOiBjdXJyZW50Q29sb3I7b3ZlcmZsb3c6IGhpZGRlbjsnIHZpZXdCb3g9JzAgMCAxMDI0IDEwMjQnIHZlcnNpb249JzEuMScgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJz48cGF0aCBkPSdNNTEwLjgxMzQ3NiA5MTEuOTY4NDM3Yy0zLjk0OTk2MiAwLTcuODU0ODk4LTEuMDEwMDAzLTExLjQzNTQ0Ny0yLjkzODkzNS00LjQxMDQ1LTIuNDgxNTE4LTEwOS4zOTQ1MDMtNjEuMjMyNTk2LTIxNS45ODQxMjMtMTU3LjQ2NDMxMS02My4wNjYzNjEtNTYuOTEyMTk4LTExMy40MTIwMDMtMTE1LjExMTcxNC0xNDkuNzIzLTE3My4wODA5ODctNDYuMjc4LTczLjkwODI5LTY5LjcwNDU0OC0xNDcuNjU0ODk4LTY5LjcwNDU0OC0yMTkuMTk4MzI4IDAtMTM2LjE3MjM3OSAxMTAuNzcwODQ5LTI0Ni45NDIyMDUgMjQ2Ljk0MTE4Mi0yNDYuOTQyMjA1IDQ2LjA5Njg3NCAwIDk1LjA2NDEyMyAxNy4yNzEzNjIgMTM3Ljg3MzExNCA0OC42NDQ5MDcgMjUuMDA5NjAzIDE4LjMyNzQxNCA0Ni4zMjQwNDggNDAuNDQ0MTMxIDYyLjAzMjgyMiA2My45MTc3NTIgMTUuNzEyODY3LTIzLjQ3MzYyMSAzNy4wMjUyNjUtNDUuNTkwMzM4IDYyLjAxNDQwMi02My45MTc3NTIgNDIuODA3OTY4LTMxLjM3MzU0NSA5MS43NzUyMTYtNDguNjQ0OTA3IDEzNy44OTE1MzQtNDguNjQ0OTA3IDEzNi4xNTE5MTMgMCAyNDYuOTQzMjI4IDExMC43Njk4MjYgMjQ2Ljk0MzIyOCAyNDYuOTQyMjA1IDAgNzEuNTQzNDMtMjMuNDcxNTc0IDE0NS4yOTAwMzctNjkuNzI3MDYxIDIxOS4xOTgzMjgtMzYuMjQxNDEyIDU3Ljk2ODI1LTg2LjYzMTA1NiAxMTYuMTY3NzY2LTE0OS42NTQ0MzggMTczLjA4MDk4Ny0xMDYuNTY4MTMxIDk2LjIzMTcxNS0yMTEuNTc0Njk2IDE1NC45ODI3OTQtMjE1Ljk4MzA5OSAxNTcuNDY0MzExLTMuNTgxNTcyIDEuOTI4OTMyLTcuNDg0NDYyIDIuOTM4OTM1LTExLjQzNjQ3IDIuOTM4OTM1TDUxMC44MTM0NzYgOTExLjk2ODQzN3onIGZpbGw9JyNmZjJjNTYnPjwvcGF0aD48L3N2Zz4=
// @author       allFull
// @match        *://douban.fm/*
// @match        *://www.douban.fm/*
// @match        *://fm.douban.com/*
// @grant        unsafeWindow
// @run-at       document-start
// ==/UserScript==

(function() {

    executePageFunc(function(){
        return new Promise(function(resolve){

            (function(e, a) { for(var i in a) e[i] = a[i]; }(window, /******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// identity function for calling harmony imports with the correct context
/******/ 	__webpack_require__.i = function(value) { return value; };
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 3);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
    value: true
});

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

exports.configEvent = configEvent;
exports.hook = hook;
exports.unHook = unHook;
/*
 * author: wendux
 * email: 824783146@qq.com
 * source code: https://github.com/wendux/Ajax-hook
 */

// Save original XMLHttpRequest as _rxhr
var realXhr = "_rxhr";

function configEvent(event, xhrProxy) {
    var e = {};
    for (var attr in event) {
        e[attr] = event[attr];
    } // xhrProxy instead
    e.target = e.currentTarget = xhrProxy;
    return e;
}

function hook(proxy) {
    // Avoid double hookAjax
    window[realXhr] = window[realXhr] || XMLHttpRequest;

    XMLHttpRequest = function XMLHttpRequest() {
        var xhr = new window[realXhr]();
        // We shouldn't hookAjax XMLHttpRequest.prototype because we can't
        // guarantee that all attributes are on the prototype。
        // Instead, hooking XMLHttpRequest instance can avoid this problem.
        for (var attr in xhr) {
            var type = "";
            try {
                type = _typeof(xhr[attr]); // May cause exception on some browser
            } catch (e) {}
            if (type === "function") {
                // hookAjax methods of xhr, such as `open`、`send` ...
                this[attr] = hookFunction(attr);
            } else {
                Object.defineProperty(this, attr, {
                    get: getterFactory(attr),
                    set: setterFactory(attr),
                    enumerable: true
                });
            }
        }
        var that = this;
        xhr.getProxy = function () {
            return that;
        };
        this.xhr = xhr;
    };

    // Generate getter for attributes of xhr
    function getterFactory(attr) {
        return function () {
            var v = this.hasOwnProperty(attr + "_") ? this[attr + "_"] : this.xhr[attr];
            var attrGetterHook = (proxy[attr] || {})["getter"];
            return attrGetterHook && attrGetterHook(v, this) || v;
        };
    }

    // Generate setter for attributes of xhr; by this we have an opportunity
    // to hookAjax event callbacks (eg: `onload`) of xhr;
    function setterFactory(attr) {
        return function (v) {
            var xhr = this.xhr;
            var that = this;
            var hook = proxy[attr];
            // hookAjax  event callbacks such as `onload`、`onreadystatechange`...
            if (attr.substring(0, 2) === 'on') {
                that[attr + "_"] = v;
                xhr[attr] = function (e) {
                    e = configEvent(e, that);
                    var ret = proxy[attr] && proxy[attr].call(that, xhr, e);
                    ret || v.call(that, e);
                };
            } else {
                //If the attribute isn't writable, generate proxy attribute
                var attrSetterHook = (hook || {})["setter"];
                v = attrSetterHook && attrSetterHook(v, that) || v;
                this[attr + "_"] = v;
                try {
                    // Not all attributes of xhr are writable(setter may undefined).
                    xhr[attr] = v;
                } catch (e) {}
            }
        };
    }

    // Hook methods of xhr.
    function hookFunction(fun) {
        return function () {
            var args = [].slice.call(arguments);
            if (proxy[fun]) {
                var ret = proxy[fun].call(this, args, this.xhr);
                // If the proxy return value exists, return it directly,
                // otherwise call the function of xhr.
                if (ret) return ret;
            }
            return this.xhr[fun].apply(this.xhr, args);
        };
    }

    // Return the real XMLHttpRequest
    return window[realXhr];
}

function unHook() {
    if (window[realXhr]) XMLHttpRequest = window[realXhr];
    window[realXhr] = undefined;
}

/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
    value: true
});
exports.proxy = proxy;
exports.unProxy = unProxy;

var _xhrHook = __webpack_require__(0);

var events = ['load', 'loadend', 'timeout', 'error', 'readystatechange', 'abort']; /*
                                                                                    * author: wendux
                                                                                    * email: 824783146@qq.com
                                                                                    * source code: https://github.com/wendux/Ajax-hook
                                                                                    */

var eventLoad = events[0],
    eventLoadEnd = events[1],
    eventTimeout = events[2],
    eventError = events[3],
    eventReadyStateChange = events[4],
    eventAbort = events[5];

var singleton,
    prototype = 'prototype';

function proxy(proxy) {
    if (singleton) throw "Proxy already exists";
    return singleton = new Proxy(proxy);
}

function unProxy() {
    singleton = null;
    (0, _xhrHook.unHook)();
}

function trim(str) {
    return str.replace(/^\s+|\s+$/g, '');
}

function getEventTarget(xhr) {
    return xhr.watcher || (xhr.watcher = document.createElement('a'));
}

function triggerListener(xhr, name) {
    var xhrProxy = xhr.getProxy();
    var callback = 'on' + name + '_';
    var event = (0, _xhrHook.configEvent)({ type: name }, xhrProxy);
    xhrProxy[callback] && xhrProxy[callback](event);
    var evt;
    if (typeof Event === 'function') {
        evt = new Event(name, { bubbles: false });
    } else {
        // https://stackoverflow.com/questions/27176983/dispatchevent-not-working-in-ie11
        evt = document.createEvent('Event');
        evt.initEvent(name, false, true);
    }
    getEventTarget(xhr).dispatchEvent(evt);
}

function Handler(xhr) {
    this.xhr = xhr;
    this.xhrProxy = xhr.getProxy();
}

Handler[prototype] = Object.create({
    resolve: function resolve(response) {
        var xhrProxy = this.xhrProxy;
        var xhr = this.xhr;
        xhrProxy.readyState = 4;
        xhr.resHeader = response.headers;
        xhrProxy.response = xhrProxy.responseText = response.response;
        xhrProxy.statusText = response.statusText;
        xhrProxy.status = response.status;
        triggerListener(xhr, eventReadyStateChange);
        triggerListener(xhr, eventLoad);
        triggerListener(xhr, eventLoadEnd);
    },
    reject: function reject(error) {
        this.xhrProxy.status = 0;
        triggerListener(this.xhr, error.type);
        triggerListener(this.xhr, eventLoadEnd);
    }
});

function makeHandler(next) {
    function sub(xhr) {
        Handler.call(this, xhr);
    }

    sub[prototype] = Object.create(Handler[prototype]);
    sub[prototype].next = next;
    return sub;
}

var RequestHandler = makeHandler(function (rq) {
    var xhr = this.xhr;
    rq = rq || xhr.config;
    xhr.withCredentials = rq.withCredentials;
    xhr.open(rq.method, rq.url, rq.async !== false, rq.user, rq.password);
    for (var key in rq.headers) {
        xhr.setRequestHeader(key, rq.headers[key]);
    }
    xhr.send(rq.body);
});

var ResponseHandler = makeHandler(function (response) {
    this.resolve(response);
});

var ErrorHandler = makeHandler(function (error) {
    this.reject(error);
});

function Proxy(proxy) {
    var onRequest = proxy.onRequest,
        onResponse = proxy.onResponse,
        onError = proxy.onError;

    function handleResponse(xhr, xhrProxy) {
        var handler = new ResponseHandler(xhr);
        if (!onResponse) return handler.resolve();
        var ret = {
            response: xhrProxy.response,
            status: xhrProxy.status,
            statusText: xhrProxy.statusText,
            config: xhr.config,
            headers: xhr.resHeader || xhr.getAllResponseHeaders().split('\r\n').reduce(function (ob, str) {
                if (str === "") return ob;
                var m = str.split(":");
                ob[m.shift()] = trim(m.join(':'));
                return ob;
            }, {})
        };
        onResponse(ret, handler);
    }

    function onerror(xhr, xhrProxy, e) {
        var handler = new ErrorHandler(xhr);
        var error = { config: xhr.config, error: e };
        if (onError) {
            onError(error, handler);
        } else {
            handler.next(error);
        }
    }

    function preventXhrProxyCallback() {
        return true;
    }

    function errorCallback(xhr, e) {
        onerror(xhr, this, e);
        return true;
    }

    function stateChangeCallback(xhr, xhrProxy) {
        if (xhr.readyState === 4 && xhr.status !== 0) {
            handleResponse(xhr, xhrProxy);
        } else if (xhr.readyState !== 4) {
            triggerListener(xhr, eventReadyStateChange);
        }
        return true;
    }

    return (0, _xhrHook.hook)({
        onload: preventXhrProxyCallback,
        onloadend: preventXhrProxyCallback,
        onerror: errorCallback,
        ontimeout: errorCallback,
        onabort: errorCallback,
        onreadystatechange: function onreadystatechange(xhr) {
            return stateChangeCallback(xhr, this);
        },
        open: function open(args, xhr) {
            var _this = this;
            var config = xhr.config = { headers: {} };
            config.method = args[0];
            config.url = args[1];
            config.async = args[2];
            config.user = args[3];
            config.password = args[4];
            config.xhr = xhr;
            var evName = 'on' + eventReadyStateChange;
            if (!xhr[evName]) {
                xhr[evName] = function () {
                    return stateChangeCallback(xhr, _this);
                };
            }

            var defaultErrorHandler = function defaultErrorHandler(e) {
                onerror(xhr, _this, (0, _xhrHook.configEvent)(e, _this));
            };
            [eventError, eventTimeout, eventAbort].forEach(function (e) {
                var event = 'on' + e;
                if (!xhr[event]) xhr[event] = defaultErrorHandler;
            });

            // 如果有请求拦截器,则在调用onRequest后再打开链接。因为onRequest最佳调用时机是在send前,
            // 所以我们在send拦截函数中再手动调用open,因此返回true阻止xhr.open调用。
            //
            // 如果没有请求拦截器,则不用阻断xhr.open调用
            if (onRequest) return true;
        },
        send: function send(args, xhr) {
            var config = xhr.config;
            config.withCredentials = xhr.withCredentials;
            config.body = args[0];
            if (onRequest) {
                // In 'onRequest', we may call XHR's event handler, such as `xhr.onload`.
                // However, XHR's event handler may not be set until xhr.send is called in
                // the user's code, so we use `setTimeout` to avoid this situation
                var req = function req() {
                    onRequest(config, new RequestHandler(xhr));
                };
                config.async === false ? req() : setTimeout(req);
                return true;
            }
        },
        setRequestHeader: function setRequestHeader(args, xhr) {
            // Collect request headers
            xhr.config.headers[args[0].toLowerCase()] = args[1];
            return true;
        },
        addEventListener: function addEventListener(args, xhr) {
            var _this = this;
            if (events.indexOf(args[0]) !== -1) {
                var handler = args[1];
                getEventTarget(xhr).addEventListener(args[0], function (e) {
                    var event = (0, _xhrHook.configEvent)(e, _this);
                    event.type = args[0];
                    event.isTrusted = true;
                    handler.call(_this, event);
                });
                return true;
            }
        },
        getAllResponseHeaders: function getAllResponseHeaders(_, xhr) {
            var headers = xhr.resHeader;
            if (headers) {
                var header = "";
                for (var key in headers) {
                    header += key + ': ' + headers[key] + '\r\n';
                }
                return header;
            }
        },
        getResponseHeader: function getResponseHeader(args, xhr) {
            var headers = xhr.resHeader;
            if (headers) {
                return headers[(args[0] || '').toLowerCase()];
            }
        }
    });
}

/***/ }),
/* 2 */,
/* 3 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.ah = undefined;

var _xhrHook = __webpack_require__(0);

var _xhrProxy = __webpack_require__(1);

// ah(ajax hook)
/*
 * author: wendux
 * email: 824783146@qq.com
 * source code: https://github.com/wendux/Ajax-hook
 */

var ah = exports.ah = {
  proxy: _xhrProxy.proxy,
  unProxy: _xhrProxy.unProxy,
  hook: _xhrHook.hook,
  unHook: _xhrHook.unHook
};

/***/ })
/******/ ])));

            //ajaxhook.min.js used to hook the ajax of the page to change the data.

            resolve();
        })
    },[],function(){
        unsafeWindow.ah.hook({
            responseText: {
                getter: ajaxReParse
            },
            response: {
                getter:ajaxReParse
            }
        });

        function ajaxReParse(v){
            var temp=JSON.parse(v);
            if(temp&&temp.songs){
                for(let song of temp.songs){
                    song.is_douban_playable=true;
                    song.status=0;
                    song.playable=true;
                    song.is_royal=true;
                    song.is_site_song=true;
                    if(song.item_info){
                        song.item_info.comment="";
                    }
                }
            }
            if(temp&&temp[0]&&(temp[0].is_douban_playable!==undefined)){
                for(let song of temp){
                    song.is_douban_playable=true;
                    song.status=0;
                    song.playable=true;
                    song.is_royal=true;
                    song.is_site_song=true;
                }
            }
            if(temp&&temp.songlist){
                for(let song of temp.songlist.songs){
                    song.is_douban_playable=true;
                    song.status=0;
                    song.playable=true;
                    song.is_royal=true;
                    song.is_site_song=true;
                }
            }
            return JSON.stringify(temp);
        }
    })

    function executePageFunc(func, argus = [], after) {
        if (window.executePageFuncIndexor === undefined) {
            window.executePageFuncIndexor = 0;
        }
        if (!document.querySelector("#zd_executePageFuncPromiseResult_ele")) {
            document.documentElement.insertAdjacentHTML("beforeend","<span id='zd_executePageFuncPromiseResult_ele' style='display:none;'></span>");
        }
        if (after) {
            var promiseSessionStorageName = "zd_excutePageFuncPromise_result_" + (executePageFuncIndexor++) + "_" + (new Date()).getTime();
            var observer = new MutationObserver(function (mutationsList) {
                if (document.querySelector("#zd_executePageFuncPromiseResult_ele").getAttribute("data-result-name") == promiseSessionStorageName) {
                    after();
                    var result;
                    try {
                        result = JSON.parse(sessionStorage[promiseSessionStorageName]);
                    } catch (e) {
                        result = null;
                    }
                    after(result);
                    observer.disconnect();
                }
            });
            observer.observe(document.querySelector("#zd_executePageFuncPromiseResult_ele"), {
                "attributes": true,
                "attributeFilter": ["data-result-name"]
            });
        }
        var ele = document.createElement("script");
        ele.innerHTML = "sessionStorage.zd_executePageFuncTempResult=JSON.stringify((" + func.toString() + ")(...JSON.parse('" + JSON.stringify(argus) + "'))" +
            (after ? (".then(function(result){sessionStorage." + promiseSessionStorageName + "=JSON.stringify(result);document.getElementById('zd_executePageFuncPromiseResult_ele').setAttribute('data-result-name','" + promiseSessionStorageName + "')})") : "") +
            ");";
        document.documentElement.appendChild(ele);
        var result;
        try {
            result = JSON.parse(sessionStorage.zd_executePageFuncTempResult);
        } catch (e) {
            result = null;
        }
        return result;
    }

})();