Amazon Video ASIN Display

Show ASINs for episodes and movies/seasons on Amazon Prime Video

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name         Amazon Video ASIN Display
// @namespace    [email protected]
// @version      0.0.2
// @description  Show ASINs for episodes and movies/seasons on Amazon Prime Video
// @author       ReiDoBrega
// @license      MIT
// @match        https://www.amazon.com/*
// @match        https://www.amazon.co.uk/*
// @match        https://www.amazon.de/*
// @match        https://www.amazon.co.jp/*
// @match        https://www.primevideo.com/*
// @run-at       document-idle
// @grant        none
// ==/UserScript==

(function() {
    "use strict";

    let style = document.createElement("style");
    let styleText = document.createTextNode(`
.x-episode-asin {
    margin: 0.5em 0;
    color: #ff0000;
    cursor: pointer; /* Add cursor pointer */
}

.x-page-asin {
    margin: 0.5em 0 1em 0;
    color: #ff0000;
    cursor: pointer; /* Add cursor pointer */
}`);
    style.appendChild(styleText);
    document.head.appendChild(style);

    function addTitleASIN() {
        try {
            let match = document.body.innerHTML.match(/"pageTitleId":"([^"]+)"/);
            if (!match) {
                return;
            }

            let asin = match[1];

            let asinEl = document.querySelector(".x-page-asin");
            if (!asinEl) {
                asinEl = document.createElement("div");
                asinEl.className = "x-page-asin";
            }

            asinEl.textContent = asin;
            asinEl.addEventListener("click", function() {
                copyToClipboard(asin);
            });

            let after = document.querySelector(".dv-dp-node-synopsis, .av-synopsis");
            if (!after) {
                return;
            }

            after.parentNode.insertBefore(asinEl, after.nextSibling);
        } catch (e) {
        }
    }

    function addEpisodeASINs() {
        try {
            document.querySelectorAll("[id^='selector-'], [id^='av-episode-expand-toggle-']").forEach(el => {
                if (el.parentNode.querySelector(".x-episode-asin")) {
                    return; // ASIN already added
                }

                let asin = el.id.replace(/^(?:selector|av-episode-expand-toggle)-/, "");

                let asinEl = document.createElement("div");
                let text = document.createTextNode(asin);
                asinEl.className = "x-episode-asin";
                asinEl.appendChild(text);
                asinEl.addEventListener("click", function() {
                    copyToClipboard(asin);
                });

                let epTitle = el.parentNode.querySelector("[data-automation-id^='ep-title']");
                if (!epTitle) {
                    return;
                }

                epTitle.parentNode.insertBefore(asinEl, epTitle.nextSibling);
            });
        } catch (e) {
        }
    }

    function addASINs() {
        addTitleASIN();
        addEpisodeASINs();
    }

    function debounce(fn, delay) {
        let timeoutID;
        return function() {
            if (timeoutID) {
                clearTimeout(timeoutID);
            }
            timeoutID = setTimeout(() => {
                fn();
                timeoutID = null;
            }, delay);
        };
    }

    const debouncedAddASINs = debounce(addASINs, 300);

    addASINs();
    setTimeout(addEpisodeASINs, 100);

    new MutationObserver(function(mutationsList, observer) {
        debouncedAddASINs();
    }).observe(document.body, { childList: true, subtree: true });

    function copyToClipboard(text) {
        const input = document.createElement("textarea");
        input.value = text;
        document.body.appendChild(input);
        input.select();
        document.execCommand("copy");
        document.body.removeChild(input);
    }

})();