pixiv_free_fanbox

view fanbox for free

As of 2021-01-24. See the latest version.

// ==UserScript==
// @name           pixiv_free_fanbox
// @name:zh-CN        pixiv_free_fanbox
// @name:zh-TW        pixiv_free_fanbox
// @name:ja        pixiv_free_fanbox
// @name:ru        pixiv_free_fanbox
// @name:kr        pixiv_free_fanbox
// @namespace      pixiv_free_fanbox
// @description    view fanbox for free
// @description:zh-CN view fanbox for free
// @description:zh-TW view fanbox for free
// @description:ja view fanbox for free
// @description:ru view fanbox for free
// @description:kr view fanbox for free
// @include        https://*.fanbox.cc/posts/*
// @include        https://*.fanbox.cc/
// @run-at      document-start
// @grant       GM_xmlhttpRequest
// @grant         GM_registerMenuCommand
// @grant         GM_setValue
// @grant         GM_getValue
// @author      zhuzemin
// @version     1.05
// @supportURL  https://github.com/zhuzemin
// @connect-src danbooru.donmai.us
// @connect-src cse.google.com
// @connect-src yande.re
// ==/UserScript==
//config
let config = {
        'debug': false,
        'api': {
                //'google': 'https://cse.google.com/cse/element/v1?cx=a5e2ba84062470dee&q={{keyword}}&safe=off&cse_tok=AJvRUv2fkaAldGWb8xOFfG2krdXS:1611290690970&callback=google.search.cse.api6888',

                'danbooru': {
                        'base': 'https://danbooru.donmai.us',
                        'artists': '/artists.json?search[url_matches]={{keyword}}',
                        'posts': '/posts.json?tags={{keyword}}&limit=200',
                },
                'yandere': {
                        'base': 'https://yande.re',
                        'artists': '/artist.json?name={{keyword}}',
                        'posts': '/post.json?limit=100&tags={{keyword}}',
                        'artist_name': '/artist/show/{{keyword}}',
                },
        },
        'hostname': getLocation(window.location.href).hostname,
        'obj': null,
        'last_idx': 0,
}
let debug = config.debug ? console.log.bind(console) : function () {
};
config.obj = GM_getValue(config.hostname);
for (let key in config.api) {
        for (let sub_key in config.api[key]) {
                if (sub_key != 'base') {
                        config.api[key][sub_key] = config.api[key].base + config.api[key][sub_key];
                }
        }
}
class requestObject {
        constructor(url) {
                this.method = 'GET';
                this.respType = 'json';
                this.url = url;
                this.body = null;
                this.headers = {
                        'User-agent': window.navigator.userAgent,
                        'Referer': window.location.href,
                };
        }
}


function googleHandler(resp, deprecated) {
        return new Promise(
                (resolve, reject) => {
                        const raw = resp.responseText;
                        debug(raw.replace(/(\);)|(\/\*O_o\*\/)|(google.search.cse.api6888\()/g, ''));
                        const json = JSON.parse(raw.replace(/(\);)|(\/\*O_o\*\/)|(google.search.cse.api6888\()/g, '').replace('\\', '\\\\'));
                        debug(json.cursor.resultCount);
                        if (parseInt(json.cursor.resultCount) > 0) {
                                let postId = null;
                                let matches = json.results[0].contentNoFormatting.match(/\s(\d{5,8}),/);
                                if (matches != null) {
                                        postId = matches[1];
                                }
                                else {
                                        postId = json.results[0].url.match(/\d{5,8}/)[0];

                                }
                                if (postId != null) {
                                        const url = config.api.yandere.replace('{{postId}}', postId);
                                        const obj = new requestObject(url);
                                        debug(url);
                                        resolve(obj);

                                }
                        }
                });
}

function get_artist(flag) {
        return new Promise(
                (resolve, reject) => {
                        debug('get_artist');
                        let keyword = null;
                        if (flag == 'yandere') {
                                let interval = setInterval(() => {
                                        let elem = document.querySelector('h1');
                                        if (elem.textContent != '') {
                                                clearInterval(interval);
                                                keyword = encodeURIComponent(elem.textContent);
                                                const url = config.api[flag].artists.replace('{{keyword}}', keyword);
                                                const obj = new requestObject(url);
                                                debug(url);
                                                httpRequest(obj).then(
                                                        function (result) {
                                                                if (result.response.length > 0) {
                                                                        let json = result.response[0];
                                                                        save_obj(flag, json)
                                                                        resolve('suc');

                                                                }
                                                                else {
                                                                        reject('error');
                                                                }
                                                        });
                                        }
                                }, 1000);
                        }
                        else if (flag == 'danbooru') {
                                keyword = encodeURIComponent(window.location.href);
                                const url = config.api[flag].artists.replace('{{keyword}}', keyword);
                                const obj = new requestObject(url);
                                debug(url);
                                httpRequest(obj).then(
                                        function (result) {
                                                if (result.response.length > 0) {
                                                        let json = result.response[0];
                                                        save_obj(flag, json)
                                                        resolve('suc');

                                                }
                                                else {
                                                        reject('error');
                                                }
                                        });
                        }
                });
}

function save_obj(flag, json) {
        config.obj.src[flag] = {
                'artist': json,
                'posts': null,
        };
        GM_setValue(config.hostname, config.obj);
}

function get_posts(flag) {
        debug('get_posts');
        return new Promise(
                (resolve, reject) => {
                        let count = 0;
                        const keyword = config.obj.src[flag].artist.name;
                        const url = config.api[flag].posts.replace('{{keyword}}', keyword);
                        const obj = new requestObject(url);
                        httpRequest(obj).then(
                                (result) => {
                                        config.obj.src[flag].posts = result.response;
                                        config.obj.update = Date.now();
                                        config.obj.suc++;
                                        debug('get posts finish');
                                        GM_setValue(config.hostname, config.obj);
                                        resolve('suc');
                                },
                                () => {
                                        debug('get posts finish');
                                        config.obj.suc++;
                                }
                        );
                });
}


function unlock() {
        debug('unlock');
        const day = 60 * 60 * 24 * 1000;
        debug(day);
        let pair = 0;
        let running = false;
        let interval = setInterval(() => {
                if (config.obj.suc == Object.keys(config.api).length && !running) {
                        running = true;
                        let parent = null;
                        for (let xpath of ['//*[@id="root"]/div[5]/div[1]/div[2]/div[3]/div/div/div[1]', '/html/body/div/div[5]/div[1]/div/div[3]/div/div/div[1]']) {
                                parent = getElementByXpath(xpath);
                                if (parent != null && parent.childNodes.length > 3) {
                                        debug(parent.childNodes.length);
                                        for (let i = 0; i < parent.childNodes.length; i++) {
                                                let child = parent.childNodes[i];
                                                debug(child.className);
                                                if (child.className == '') {
                                                        if (i >= config.last_idx) {
                                                                child.addEventListener('click', () => {
                                                                        postsHandler();
                                                                });
                                                        }
                                                        let info = child.firstChild.lastChild.firstChild.firstChild;
                                                        const href = child.firstChild.lastChild.href;
                                                        debug(href);
                                                        if (config.last_idx < parent.childNodes.length) {
                                                                debug(info.textContent);
                                                                debug(info.querySelectorAll('div')[1].lastChild.textContent);
                                                                const date = new Date(info.querySelectorAll('div')[1].lastChild.textContent);
                                                                debug(date);
                                                                let files = [];
                                                                for (let key in config.obj.src) {
                                                                        const posts = config.obj.src[key].posts;
                                                                        for (let item of posts) {
                                                                                let created_date = null;
                                                                                if (key == 'yandere') {
                                                                                        debug(item.created_at * 1000);
                                                                                        created_date = new Date(item.created_at * 1000);
                                                                                }
                                                                                else if (key == 'danbooru') {
                                                                                        created_date = new Date(item.created_at);
                                                                                }
                                                                                debug(created_date);
                                                                                if (item.source == href || (created_date > date && created_date - date < day)) {
                                                                                        let file_url = null;
                                                                                        let large_file_url = null;
                                                                                        if (key == 'danbooru') {
                                                                                                file_url = item.large_file_url; //api was reverse
                                                                                                large_file_url = item.file_url;
                                                                                                debug(file_url);
                                                                                        }
                                                                                        else if (key == 'yandere') {
                                                                                                file_url = item.sample_url;
                                                                                                large_file_url = item.jpeg_url;
                                                                                                debug(file_url);
                                                                                        }
                                                                                        files.push({
                                                                                                'file_url': file_url,
                                                                                                'large_file_url': large_file_url
                                                                                        });
                                                                                }
                                                                        }
                                                                }
                                                                if (files.length > 0) {
                                                                        debug(files);
                                                                        config.obj.pair.push({
                                                                                'href': href,
                                                                                'files': files
                                                                        });
                                                                }
                                                        }
                                                        for (let item of config.obj.pair) {
                                                                if (item.href == href) {
                                                                        let icon = info.lastChild;
                                                                        icon.innerHTML = '&#128273;&#128275;';
                                                                        break;
                                                                }
                                                        }
                                                }
                                        }
                                        if (Object.keys(config.obj.pair).length > pair) {
                                                GM_setValue(config.hostname, config.obj);
                                                pair = Object.keys(config.obj.pair).length;
                                        }
                                        config.last_idx = parent.childNodes.length;
                                        break;
                                }
                        }
                        running = false;
                }
        }, 1000);
}

function insert(element, file_url, large_file_url) {
        if (file_url.includes('yande.re')) {
                let obj = new requestObject(file_url);
                obj.respType = 'blob';
                obj.headers['referer'] = 'https://yande.re';
                httpRequest(obj).then((result) => {
                        let url = get_url(result.response);
                        insert(element, url, large_file_url);
                });
        }
        else {

                let img = document.createElement('img');
                img.style.display = 'block';
                img.style.marginLeft = 'auto';
                img.style.marginRight = 'auto';
                img.src = file_url;
                img.alt = large_file_url;
                img.referrerPolicy = 'no-referrer';
                img.addEventListener('click', function () {
                        let div = document.querySelector('#free_fanbox');
                        div.firstChild.src = this.alt;
                        div.style.visibility = 'visible';
                });
                const newImg = img.cloneNode(true);
                let div = document.createElement('div');
                div.id = 'free_fanbox';
                div.style = `
width:100%;
height:100%;
z-index:1000;
visibility: hidden;
background:white;
margin: auto;
display: block;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
`;
                div.append(newImg);
                div.addEventListener('click', function () {
                        this.style.visibility = 'hidden';
                })
                element.appendChild(img);
                element.appendChild(div);
                if (img.width > img.height) {

                        img.style.width = '720px';
                } else {

                        img.style.height = '731px';
                }
        }
}

function postsHandler() {
        debug('posts');
        config.last_idx = 0;
        debug(config.obj.pair.length);
        if (config.obj != null && config.obj.pair.length > 0) {
                $Wait('article').then(function (resolve) {
                        for (let item of config.obj.pair) {
                                debug(item.href);
                                if (item.href == window.location.href) {
                                        resolve.removeChild(resolve.lastChild);
                                        for (let file of item.files.reverse()) {
                                                insert(resolve, file.file_url, file.large_file_url);
                                        }
                                        break;
                                }
                        }
                });
        }
}

let init = function () {
        if (window.self === window.top) {
                if (/^https:\/\/\w+.fanbox.cc\/$/.test(window.location.href)) {
                        if (config.obj == null) {
                                config.obj = {
                                        src: {},
                                        update: null,
                                        pair: null,
                                        suc: 0,
                                }
                                for (let key in config.api) {
                                        debug(key);
                                        if (key == 'yandere') {
                                                get_artist(key).then(
                                                        () => {
                                                                const keyword = config.obj.src[key].artist.alias_id;
                                                                const url = config.api[key].artist_name.replace('{{keyword}}', keyword);
                                                                const obj = new requestObject(url);
                                                                debug(url);
                                                                httpRequest(obj).then(
                                                                        function (resolve) {
                                                                                let alias = resolve.finalUrl.match(/title=(.+)/)[1];
                                                                                config.obj.src[key].artist['origin_name'] = config.obj.src[key].artist.name;
                                                                                config.obj.src[key].artist.name = alias;
                                                                                debug(alias);
                                                                                get_posts(key);
                                                                        }
                                                                );

                                                        }
                                                );
                                        }
                                        else if (key == 'danbooru') {
                                                get_artist(key).then(
                                                        () => {
                                                                get_posts(key);
                                                        }
                                                );
                                        }
                                }
                        }
                        else {
                                const hour = 60 * 60 * 1000;
                                if (Date.now() - new Date(config.obj.update) > hour) {
                                        config.obj.suc = 0;
                                        for (let key in config.api) {
                                                get_posts(key);
                                        }
                                }
                        }
                        unlock();
                }
                else {
                        postsHandler();
                }
        }
}
window.addEventListener('DOMContentLoaded', init);
/**
 * Create a user setting prompt
 * @param {string} varName
 * @param {any} defaultVal
 * @param {string} menuText
 * @param {string} promtText
 * @param {function} func
 */
function setUserPref(varName, defaultVal, menuText, promtText, func = null) {
        GM_registerMenuCommand(menuText, function () {
                var val = prompt(promtText, GM_getValue(varName, defaultVal));
                if (val === null) { return; }// end execution if clicked CANCEL
                GM_setValue(varName, val);
                if (func != null) {
                        func(val);
                }
        });
}
function httpRequest(object, timeout = 10000) {
        return new Promise(
                (resolve, reject) => {
                        GM_xmlhttpRequest({
                                method: object.method,
                                url: object.url,
                                headers: object.headers,
                                responseType: object.respType,
                                data: object.body,
                                timeout: timeout,
                                onload: function (responseDetails) {
                                        debug(responseDetails);
                                        //Dowork
                                        resolve(responseDetails);
                                },
                                ontimeout: function (responseDetails) {
                                        debug(responseDetails);
                                        //Dowork
                                        reject(responseDetails);

                                },
                                ononerror: function (responseDetails) {
                                        debug(responseDetails);
                                        //Dowork
                                        reject(responseDetails);

                                }
                        });
                }
        )
}
function $Wait(Selector, Timeout = 15000) {
        return new Promise(resolve => {
                let Interval = setInterval(() => {
                        let DQS = document.querySelector(Selector);
                        if (DQS) {
                                resolve(DQS);
                                clearInterval(Interval);
                        }
                }, 0);
                setTimeout(() => {
                        resolve(null);
                        clearInterval(Interval);
                }, Timeout);
        });
}

function getElementByXpath(path) {
        return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
}

function getLocation(href) {
        let l = document.createElement("a");
        l.href = href;
        return l;
};

function get_url(blob) {
        var urlCreator = window.URL || window.webkitURL;
        var imageUrl = urlCreator.createObjectURL(blob);
        return imageUrl;
}