EdX Transcript Copier

Adds a button to the EdX video player to copy subtitles. Runs inside the video iframe.

// ==UserScript==
// @name         EdX Transcript Copier
// @namespace    http://tampermonkey.net/
// @version      1.5
// @description  Adds a button to the EdX video player to copy subtitles. Runs inside the video iframe.
// @author       Yang Chyi-Jen
//
// @match        *://courses.edx.org/*
//
// @grant        GM_setClipboard
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';

    console.log("Subtitle Copier (v1.5): Script running inside iframe.");

    // --- Style for the button ---
    GM_addStyle(`
        #subtitle-copier-btn-iframe {
            all: revert;
            position: absolute; /* Positioned relative to the menu */
            display: block;
            top: 8px;
            right: 8px;
            z-index: 99999; /* High z-index to appear on top */
        }
        /* Ensure the parent has relative positioning for our button */
        .subtitles-menu {
            position: relative !important;
        }
    `);

    /**
     * Finds the subtitle list items, extracts their text, and copies to clipboard.
     * @param {HTMLElement} subtitlesMenu - The .subtitles-menu element.
     * @returns {boolean} - True if text was successfully copied, false otherwise.
     */
    function copySubtitles(subtitlesMenu) {
        const listItems = subtitlesMenu.querySelectorAll('ol > li');
        if (listItems.length < 3) {
            console.log("Subtitle Copier: Not enough items to copy (< 3).");
            return false;
        }
        const relevantItems = Array.from(listItems).slice(1, -1);
        const textToCopy = relevantItems
            .map(item => item.textContent.trim())
            .join('\n');

        if (textToCopy) {
            GM_setClipboard(textToCopy);
            console.log("Subtitles copied to clipboard.");
            return true;
        }
        console.log("Subtitle Copier: No text found to copy.");
        return false;
    }

    /**
     * Creates the "Copy" button and adds it to the subtitles menu.
     * @param {HTMLElement} subtitlesMenu - The .subtitles-menu element.
     */
    function addCopyButton(subtitlesMenu) {
        // Prevent adding multiple buttons
        if (subtitlesMenu.querySelector('#subtitle-copier-btn-iframe')) {
            return;
        }

        const btn = document.createElement('button');
        btn.id = 'subtitle-copier-btn-iframe';
        btn.textContent = 'Copy';

        btn.addEventListener('click', (e) => {
            e.preventDefault();
            e.stopPropagation(); // Prevent the click from closing the menu

            if (copySubtitles(subtitlesMenu)) {
                btn.textContent = 'Copied!';
                setTimeout(() => { btn.textContent = 'Copy'; }, 2000);
            } else {
                btn.textContent = 'Failed';
                setTimeout(() => { btn.textContent = 'Copy'; }, 2000);
            }
        });

        subtitlesMenu.appendChild(btn);
        console.log("Subtitle Copier: Button added to menu.");
    }


    // --- Main Execution ---
    // Observe the document body for the subtitles menu to appear.
    const observer = new MutationObserver((mutationsList, obs) => {
        const subtitlesMenu = document.querySelector('.subtitles-menu');
        if (subtitlesMenu) {
            addCopyButton(subtitlesMenu);
            // We don't disconnect, as the menu might be recreated.
        }
    });

    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址