Amazon Product Copier

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

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

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

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

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴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();
})();