Amazon Product Copier

Copies Amazon product information to the clipboard with the press of a button.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Amazon Product Copier
// @description  Copies Amazon product information to the clipboard with the press of a button.
// @author       Tim Macy
// @license      AGPL-3.0-or-later
// @version      1.1
// @namespace    TimMacy.AmazonProductCopier
// @include      /^https:\/\/www\.amazon\..+?\/.*/
// @icon         https://www.google.com/s2/favicons?sz=64&domain=amazon.com
// @run-at       document-end
// @noframes
// @homepageURL  https://github.com/TimMacy/AmazonProductCopier
// @supportURL   https://github.com/TimMacy/AmazonProductCopier/issues
// ==/UserScript==

/************************************************************************
*                                                                       *
*                    Copyright © 2025 Tim Macy                          *
*                    GNU Affero General Public License v3.0             *
*                    Version: 1.1 - Amazon Product Copier               *
*                                                                       *
*             Visit: https://github.com/TimMacy                         *
*                                                                       *
************************************************************************/

(async function () {
    'use strict';
    // CSS
    const styleSheet = document.createElement('style');
    styleSheet.textContent = `
        #amazonCopierBtn {
            position: fixed;
            left: 20px;
            bottom: 20px;
            z-index: 9999;
            padding: 4px 8px;
            margin-left: 8px;
            font-size: 12px;
            font-weight: 500;
            cursor: pointer;
            border-radius: 2px;
            border: 1px solid #a88734;
            background: var(--__dChw-LmGoMXsPxT,#ffd814);
            color: var(--__dChw-LmGoMXsw4B,#0f1111);
        }

        #amazonCopierBtn:hover {
            background: var(--__dChw-LmGofIsPxT,#ffce12);
        }

        .CentAnni-force-visibility #productDetails_feature_div div.a-expander-content,
        .CentAnni-force-visibility #productDetailsWithModules_feature_div div.a-expander-content,
        .CentAnni-force-visibility #centerCol #nutritionalInfoAndIngredients_feature_div div.a-expander-content.a-expander-section-content,
        .CentAnni-force-visibility div#productDescription_feature_div div[data-a-expander-name="toggle_description"] div.a-expander-content.a-expander-extend-content {
            display: block !important;
        }

        .CentAnni-force-visibility div#productOverview_feature_div span.a-expander-prompt,
        .CentAnni-force-visibility div#productDescription_feature_div span.a-expander-prompt {
            display: none;
        }

        .CentAnni-overlay {
            position: fixed;
            display: flex;
            z-index: 2053;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            align-items: center;
            justify-content: center;
            background-color: rgba(0, 0, 0, .5);
            backdrop-filter: blur(5px);
        }

        .CentAnni-notification {
            background: hsl(0, 0%, 7%);
            padding: 20px 30px;
            border-radius: 8px;
            border: 1px solid hsl(0, 0%, 18.82%);
            max-width: 80%;
            text-align: center;
            font-family: -apple-system, "Roboto", "Arial", sans-serif;
            font-size: 16px;
            color: white;
            -webkit-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
        }
    `;

    // append css
    (document.head
        ? Promise.resolve(document.head)
        : new Promise(resolve => {
            document.readyState === 'loading'
                ? document.addEventListener('DOMContentLoaded', () => resolve(document.head), { once: true })
                : resolve(document.head);
        })
    ).then(head => {
        if (head)
            head.appendChild(styleSheet);
        else {
            document.documentElement.appendChild(styleSheet);
            console.error("AmazonProductCopier: Failed to find head element. Using backup to append stylesheet.");
        }
    });

    // elements to copy
    const productTitleSelector = "#productTitle";
    const infoSelectors = [
        '#social-proofing-faceout-title-tk_bought',
        'div[id^="corePriceDisplay_"] > div.aok-relative > span:first-child',
        'div[id^="corePriceDisplay_"] > div.a-spacing-small span.aok-offscreen',
        'div#productOverview_feature_div',
        'div#featurebullets_feature_div',
        'div.a-expander-collapsed-height.a-row.a-expander-container.a-spacing-medium.a-expander-partial-collapse-container',
        '#centerCol #nutritionalInfoAndIngredients_feature_div',
        '.a-cardui.brand-snapshot-card-container',
        'div#productDetails_feature_div',
        'div#productDetailsWithModules_feature_div',
        'div#importantInformation_feature_div',
        'div#productDescription_feature_div:first-child',
        'div#detailBulletsWithExceptions_feature_div',
        'div#detailBulletsReverseInterleaveContainer_feature_v2',
        '#detailBullets2_feature_div',
        '#reviewsMedley #averageCustomerReviewsAnchor',
        'span[data-hook="rating-out-of-text"]',
        'span[data-hook="total-review-count"]',
        '#histogramTable',
        '#reviewsMedley .cm_cr_grid_center_right div[data-csa-c-item-id="cr-product-insights-cards"] h3',
        '#reviewsMedley .cm_cr_grid_center_right div[data-csa-c-item-id="cr-product-insights-cards"] div[data-testid="overall-summary"]',
        '#reviewsMedley .cm_cr_grid_center_right div[data-csa-c-item-id="cr-product-insights-cards"] div[data-testid="ai-disclaimer"] > span:first-child',
        '#reviewsMedley .cm_cr_grid_center_right div[data-csa-c-item-id="cr-product-insights-cards"] div[data-csa-c-item-id="cr-product-insights-cards-popover"] div[id^="rh_controls_aspect_"]'
    ];

    // build content
    const buildContent = () => {
        const titleNode = document.querySelector(productTitleSelector);
        const rawTitle = titleNode?.textContent?.trim();
        const title = rawTitle ? rawTitle.replace(/\s+/g, " ") : "";
        if (!title) return "";

        const rawBlocks = infoSelectors.flatMap(selector => Array.from(document.querySelectorAll(selector))).map(node => node?.innerText?.trim()).filter(text => text && text.length > 0);
        const fixedBlocks = rawBlocks.map(block => {
            if (!block.includes('5 star') || !block.includes('3 star')) return block;

            const lines = block.split('\n').map(line => line.trim()).filter(line => line.length > 0);
            if (lines.length % 2 !== 0) return block;

            const merged = [];
            for (let i = 0; i < lines.length; i += 2) merged.push(lines[i] + ' ' + lines[i + 1]);
            return merged.join('\n');
        });

        const infoText = fixedBlocks.join('\n\n');
        if (!infoText) return "";

        const header = `<product: ${title}>\n`;
        const footer = `\n</product: ${title}>`;
        return header + infoText + footer;
    };

    // copy to clipboard
    const copyToClipboard = async () => {
        document.body.classList.add("CentAnni-force-visibility");
        const payload = buildContent();
        if (payload) {
            await navigator.clipboard.writeText(payload);
            showNotification('Content Copied to Clipboard.');
        }
        document.body.classList.remove("CentAnni-force-visibility");
    };

    // function to display the notification
    function showNotification(message) {
        const overlay = document.createElement('div');
        overlay.classList.add('CentAnni-overlay');

        const modal = document.createElement('div');
        modal.classList.add('CentAnni-notification');
        modal.textContent = message;

        overlay.appendChild(modal);
        document.body.appendChild(overlay);

        setTimeout(() => { overlay.remove(); }, 600);
    }

    // add the button
    const addButton = () => {
        const titleSection = document.querySelector("#titleSection") || document.querySelector("#buybox");
        if (!titleSection || document.querySelector("#amazonCopierBtn")) return;

        const btn = document.createElement("button");
        btn.id = "amazonCopierBtn";
        btn.type = "button";
        btn.textContent = "Copy Content";
        btn.addEventListener("click", () => { copyToClipboard(); });

        document.body.appendChild(btn);
    };

    document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", addButton, { once: true }) : addButton();
})();