DLSite Product Information Injector

Inject the information of a DLSite product to your restaurant.

当前为 2023-01-07 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         DLSite Product Information Injector
// @description  Inject the information of a DLSite product to your restaurant.
// @namespace    dl_injecter
// @version      2.0.0
// @grant        GM_xmlhttpRequest
// @match        https://arca.live/b/simya/*
// @run-at       document-end
// @noframes
// ==/UserScript==
var fullRegexs = [/(RJ)[0-9]{6}\b|(RJ)[0-9]{8}\b/gi, /(VJ)[0-9]{6}\b|(VJ)[0-9]{8}\b/gi];

var partialRegexs = [/[0-9]{8}|[0-9]{6}/gi];

var languageRegexs = [/(거|퍼)[0-9]{6}\b|(거|퍼)[0-9]{8}\b/gi];

var defaultPrefix = 'RJ';
var default2Prefix = 'VJ';

// Get matched products from text.
function getMatchedProducts(text) {
    // Preprocess the inputted text.
    text = String(text);
    text = text.replace(/https?:\/\/(arca.live\/b\/simya\/)[0-9]*/g, ""); // Ignore arca live post url.

    // Get matches from the text with regexs.
    var matches = [];

    for (let i = 0; i < fullRegexs.length; i++) {
        let tempArr = [];
        tempArr = text.match(fullRegexs[i]);

        if (tempArr == null) {
            continue;
        }

        for (let j = 0; j < tempArr.length; j++) {
            if (tempArr[j] != null) {
                matches.push(tempArr[j].toUpperCase());
            }
        }
    }

    for (let i = 0; i < languageRegexs.length; i++) {
        let tempArr = [];
        tempArr = text.match(languageRegexs[i]);

        if (tempArr == null) {
            continue;
        }

        for (let j = 0; j < tempArr.length; j++) {
            if (tempArr[j] != null) {
                // If the product code is already exist, ignore current index.
                var text_check = tempArr[j].substr(0, 1);
                tempArr[j] = tempArr[j].substr(1);

                if ("거" == text_check) {
                    matches.push(defaultPrefix.toUpperCase() + tempArr[j]);
                }

                if ("퍼" == text_check) {
                    matches.push(default2Prefix.toUpperCase() + tempArr[j]);
                }
            }
        }
    }

    for (let i = 0; i < partialRegexs.length; i++) {
        let tempArr = [];
        tempArr = text.match(partialRegexs[i]);

        if (tempArr == null) {
            continue;
        }

        for (let j = 0; j < tempArr.length; j++) {
            if (tempArr[j] != null) {
                // If the product code is already exist, ignore current index.
                if (matches.includes("RJ" + tempArr[j]) || matches.includes("VJ" + tempArr[j])) {
                    break;
                }
                if (matches.includes("거" + tempArr[j]) || matches.includes("퍼" + tempArr[j])) {
                    break;
                }

                matches.push(defaultPrefix.toUpperCase() + tempArr[j]);
            }
        }
    }

    // Remove duplicated elements.
    var result = [];

    matches.forEach((element) => {
        if (!result.includes(element)) {
            result.push(element);
        }
    });

    return result;
}

// Provide Fetch feature.
function doFetch(url, options = {
    method: 'GET',
    responseType: 'document'
}, silent = false) {
    return new Promise((resolve, reject) => {
        GM_xmlhttpRequest({
            url: url,
            method: options.method,
            responseType: options.responseType,
            headers: options.headers,
            data: options.data,
            onload: result => {
                console.debug(result)
                if (result.status == 200) {
                    resolve(result.response);
                } else {
                    if (!silent) {
                        console.log(result)
                        alert("알 수 없는 오류로 인해 데이터를 불러오지 못했습니다. " + url)
                        reject(result.status);
                    } else {
                        console.debug(result)
                        reject(result.status);
                    }
                }
            }
        });
    });
}

// Format number as bytes string.
function formatBytes(bytes, decimals = 2) {
    if (bytes === 0) {
        return '-';
    }

    const k = 1024;
    const dm = decimals < 0 ? 0 : decimals;
    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));

    return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}


// Convert a array to a string.
function arr2str(arr, opt = ", ") {
    let idx = arr.indexOf("")

    while (idx > -1) {
        arr.splice(idx, 1)
        idx = arr.indexOf("")
    }

    return String(arr).replace(/"/g, '').replace('[', "").replace(']', "").replace(/,/g, opt)
}

// Get a product information from DLSite.
function getProductInformation(productCode) {
    return new Promise((resolve, reject) => {
        const url = `https://www.dlsite.com/maniax/api/=/product.json?workno=${productCode}&locale=ko-KR`
        doFetch(url, {
                method: 'GET',
                responseType: 'application/json'
            })
            .then(result => {
                const json = JSON.parse(result)[0];

                if (json) {
                    const processedJson = Object()

                    processedJson.thumbnailImage = [`<img width='180' height='120' src='https:${json.image_main.url}' alt='이미지'>`]

                    processedJson.workType = [`${json.work_type} / ${json.work_type_string}`]
                    processedJson.title = [`<a target='_blank' href='https://www.dlsite.com/home/work/=/product_id/${productCode}.html'>${json.work_name}</a>`]
                    processedJson.intro = [json.intro_s]
                    processedJson.maker = [json.maker_name]

                    if (json.genres) {
                        processedJson.genres = []

                        json.genres.forEach(genre => {
                            processedJson.genres.push(genre.name)
                        })

                        processedJson.genres = [arr2str(processedJson.genres)]
                    }

                    processedJson.fileInfo = [`${json.file_type}(${formatBytes(json.contents_file_size)})`]

                    processedJson.workUrl = [`<a target='_blank' href='https://www.dlsite.com/home/work/=/product_id/${productCode}.html'>${productCode}</a>`]

                    processedJson.circleUrl = [`<a target='_blank' href='https://www.dlsite.com/home/circle/profile/=/maker_id/${json.circle_id}.html'>${processedJson.maker}</a>`]

                    resolve(processedJson);
                } else {
                    reject(new Error("Request is failed."));
                }
            })
    });
}

function createProductElement(productCode) {
    return new Promise((resolve, reject) => {
        getProductInformation(productCode).then(function(data) {
            var html = '<table width="100%" border="0">' +
                '<tbody>' +
                '<tr><!-- 첫번째 줄 시작 -->' +
                `<td style="width: 1px; white-space: nowrap;" rowspan="2">${data.thumbnailImage}</td>` +
                '<td valign="center">' +
                '<div>' +
                `<div><span class="badge badge-success" style="display:inline-block; margin:0px 0px 0px 10px">${data.workUrl}</span><span class="badge badge-success" style="display:inline-block; margin:0px 4px 0px 4px">${data.workType}</span>${data.title}</div>` +
                `<div style="display:block; margin:0px 0px 0px 16px; font-size: 0.8375em; opacity: 0.8;"><li>설명 : ${data.intro}</li></div>` +
                `<div style="display:block; margin:0px 0px 0px 16px; font-size: 0.8375em; opacity: 0.8;"><li>서클 : ${data.circleUrl}</li></div>` +
                `<div style="display:block; margin:0px 0px 0px 16px; font-size: 0.8375em; opacity: 0.8;"><li>태그 : ${data.genres}</li></div>` +
                `<div style="display:block; margin:0px 0px 0px 16px; font-size: 0.8375em; opacity: 0.8;"><li>파일(용량) : ${data.fileInfo}</li></div>` +
                `<div></div>` +
                '</div>' +
                '</td>' +
                '</tr><!-- 첫번째 줄 끝 -->' +
                '</tbody>' +
                '</table>';

            resolve(html);
        });
    });
}

(function() {
    'use strict';

    // Get article wrapper element.
    let articleView = document.getElementsByClassName("article-view")[0];
    let articleWrapper = document.getElementsByClassName("article-wrapper")[0];

    // Add the headline.
    let contentsElement = document.createElement('p');
    contentsElement.innerHTML = '<span class="ion-android-archive" style="display:inline-block; width:20px; margin:4px 0px 0px 12px;"></span>게시글에서 언급된 게임 목록';

    articleView.insertBefore(contentsElement, articleWrapper);

    // Get product codes in article.
    var products = [];
    products = getMatchedProducts(articleWrapper.innerText);

    console.log(products);

    if (products.length > 0) {
        // Insert elements into article-view element.
        for (var i = 0; i < products.length; i++) {
            createProductElement(products[i]).then(function(html) {
                let contentsElement = document.createElement('div');
                contentsElement.setAttribute("class", "alert alert-info");
                contentsElement.innerHTML = html;

                articleView.insertBefore(contentsElement, articleWrapper);
            });
        }
    }
})();