DLSite Product Information Injector

Inject the information of a DLSite product to your restaurant.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 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);
            });
        }
    }
})();