Greasy Fork

来自缓存

Greasy Fork is available in English.

阿里云盘分享链接批量打开

从粘贴的文本中提取阿里云盘分享链接和提取码,并提供批量打开及自动输入提取码功能。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         阿里云盘分享链接批量打开
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  从粘贴的文本中提取阿里云盘分享链接和提取码,并提供批量打开及自动输入提取码功能。
// @author       Gogo & Claude 3.5 Sonnet
// @match        https://www.aliyundrive.com/*
// @match        https://www.alipan.com/*
// @grant        GM_setClipboard
// @grant        GM_openInTab
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT
// ==/UserScript==

/*
 * 阿里云盘分享链接批量打开脚本
 * 版本: 1.0
 * 作者: Gogo & Claude 3.5 Sonnet
 * 创建日期: 2024-09-28
 * 最后修改: 2024-09-28
 *
 * 功能:
 * - 从粘贴的文本中提取阿里云盘分享链接和提取码
 * - 批量打开链接并自动填充提取码
 * - 显示分享链接打开状态(成功/失败)
 *
 * 更新日志:
 * 1.0 (2024-09-28) - 初始版本
 *   - 实现基本的链接提取和批量打开功能
 *   - 添加自动填充提取码功能
 *   - 添加链接状态显示功能
 */
(function() {
    'use strict';

    let textarea, resultDiv;

    // 创建触发按钮
    function createTriggerButton() {
        const button = document.createElement('button');
        button.textContent = '批量打开';
        button.style.position = 'fixed';
        button.style.bottom = '20px';
        button.style.right = '20px';
        button.style.zIndex = '10000';
        button.style.padding = '10px 15px';
        button.style.backgroundColor = '#2196F3';
        button.style.color = 'white';
        button.style.border = 'none';
        button.style.borderRadius = '4px';
        button.style.cursor = 'pointer';
        button.style.fontSize = '14px';
        button.style.fontWeight = 'bold';
        button.style.boxShadow = '0 2px 5px rgba(0,0,0,0.2)';
        button.style.transition = 'background-color 0.3s';
        button.addEventListener('mouseover', () => button.style.backgroundColor = '#1976D2');
        button.addEventListener('mouseout', () => button.style.backgroundColor = '#2196F3');
        document.body.appendChild(button);
        return button;
    }

    // 创建UI元素
    function createUI() {
        const container = document.createElement('div');
        container.style.position = 'fixed';
        container.style.bottom = '80px';
        container.style.right = '20px';
        container.style.zIndex = '9999';
        container.style.backgroundColor = '#FFFFFF';
        container.style.padding = '20px';
        container.style.borderRadius = '8px';
        container.style.width = '650px';
        container.style.maxHeight = '80vh';
        container.style.overflowY = 'auto';
        container.style.display = 'none';
        container.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)';
        container.style.fontFamily = 'Arial, sans-serif';

        textarea = document.createElement('textarea');
        textarea.style.width = '100%';
        textarea.style.height = '200px';
        textarea.style.marginBottom = '15px';
        textarea.style.padding = '10px';
        textarea.style.border = '1px solid #E0E0E0';
        textarea.style.borderRadius = '4px';
        textarea.style.resize = 'vertical';
        textarea.placeholder = '粘贴包含阿里云盘链接的文本';

        const buttonContainer = document.createElement('div');
        buttonContainer.style.display = 'flex';
        buttonContainer.style.justifyContent = 'space-between';
        buttonContainer.style.marginBottom = '15px';

        const leftButtonGroup = document.createElement('div');
        leftButtonGroup.style.display = 'flex';

        const rightButtonGroup = document.createElement('div');

        const createButton = (text, color) => {
            const button = document.createElement('button');
            button.textContent = text;
            button.style.padding = '8px 12px';
            button.style.backgroundColor = color;
            button.style.color = 'white';
            button.style.border = 'none';
            button.style.borderRadius = '4px';
            button.style.cursor = 'pointer';
            button.style.fontSize = '14px';
            button.style.fontWeight = 'bold';
            button.style.transition = 'background-color 0.3s';
            return button;
        };

        const extractButton = createButton('提取链接', '#4CAF50');
        const openAllButton = createButton('打开全部', '#2196F3');
        const resetButton = createButton('重置', '#F44336');

        extractButton.style.marginRight = '10px';

        [extractButton, openAllButton, resetButton].forEach(button => {
            button.addEventListener('mouseover', () => button.style.opacity = '0.8');
            button.addEventListener('mouseout', () => button.style.opacity = '1');
        });

        leftButtonGroup.appendChild(extractButton);
        leftButtonGroup.appendChild(openAllButton);
        rightButtonGroup.appendChild(resetButton);

        buttonContainer.appendChild(leftButtonGroup);
        buttonContainer.appendChild(rightButtonGroup);

        resultDiv = document.createElement('div');
        resultDiv.style.marginTop = '15px';

        container.appendChild(textarea);
        container.appendChild(buttonContainer);
        container.appendChild(resultDiv);

        document.body.appendChild(container);

        return { container, textarea, extractButton, resetButton, openAllButton, resultDiv };
    }

    // 提取链接和提取码
    function extractLinks(text) {
        const lines = text.split('\n');
        const linkLines = lines.filter(line =>
                                       line.includes('aliyundrive.com/s/') ||
                                       line.includes('alipan.com/s/')
                                      );

        const links = linkLines.map(line => {
            const match = line.match(/(https?:\/\/[^\s]+)/);
            return match ? match[1] : null;
        }).filter(link => link !== null);

        const results = links.map(link => {
            const linkIndex = text.indexOf(link);
            const afterLink = text.slice(linkIndex + link.length);
            const codeMatch = afterLink.match(/提取码:?\s*([a-zA-Z0-9]{4})/i);

            return {
                link: link,
                code: codeMatch ? codeMatch[1] : '无'
            };
        });

        return results;
    }

    // 打开链接
    function openLink(link, code, index) {
        GM_setValue('lastOpenedLink', link);
        GM_setValue('lastOpenedCode', code);
        GM_setValue('lastOpenedIndex', index);
        GM_setValue('linkStatus', '');  // 重置状态
        return new Promise((resolve) => {
            GM_openInTab(link, { active: true, insert: true, setParent: true });
            // 给足够的时间让新标签页加载并自动填充提取码
            setTimeout(resolve, 5000);
        });
    }

    // 保存数据到localStorage
    function saveData(text, links) {
        GM_setValue('extractorText', text);
        GM_setValue('extractorLinks', JSON.stringify(links));
    }

    // 从localStorage加载数据
    function loadData() {
        const text = GM_getValue('extractorText', '');
        const links = JSON.parse(GM_getValue('extractorLinks', '[]'));
        return { text, links };
    }

    // 新增:保存链接状态的函数
    function saveLinkStatus(index, status) {
        let linkStatuses = JSON.parse(GM_getValue('linkStatuses', '{}'));
        linkStatuses[index] = status;
        GM_setValue('linkStatuses', JSON.stringify(linkStatuses));
    }

    // 新增:获取链接状态的函数
    function getLinkStatus(index) {
        let linkStatuses = JSON.parse(GM_getValue('linkStatuses', '{}'));
        return linkStatuses[index];
    }

    // 更新UI
    function updateUI(textarea, resultDiv, text, links) {
        textarea.value = text;
        let resultHTML = '<ul style="list-style-type: none; padding: 0; margin: 0;">';
        links.forEach((item, index) => {
            const status = getLinkStatus(index);
            let backgroundColor = '#F5F5F5'; // 默认灰色
            if (status === 'success') {
                backgroundColor = '#C8E6C9'; // 浅绿色
            } else if (status === 'failed') {
                backgroundColor = '#FFCDD2'; // 浅红色
            }
            resultHTML += `
            <li id="link-item-${index}" style="margin-bottom: 10px; padding: 10px; background-color: ${backgroundColor}; border-radius: 4px;">
                <div style="display: flex; justify-content: space-between; align-items: center;">
                    <div style="flex-grow: 1; display: flex; justify-content: space-between; align-items: center;">
                        <span>
                            <span style="font-weight: bold;">链接 ${index + 1}:</span>
                            <span style="word-break: break-all; margin-left: 5px;">${item.link}</span>
                        </span>
                        <span style="margin-left: 10px;">提取码: ${item.code}</span>
                    </div>
                    <button class="open-copy-btn" data-index="${index}" style="background-color: #2196F3; color: white; border: none; padding: 5px 10px; border-radius: 4px; cursor: pointer; font-size: 12px; white-space: nowrap; margin-left: 10px;">一键打开</button>
                </div>
            </li>`;
        });
        resultHTML += '</ul>';
        resultDiv.innerHTML = resultHTML;

        // 为每个"一键打开"按钮添加事件监听器
        document.querySelectorAll('.open-copy-btn').forEach(btn => {
            btn.addEventListener('click', (e) => {
                const index = e.target.getAttribute('data-index');
                const item = links[index];
                openLink(item.link, item.code, index);
            });
            btn.addEventListener('mouseover', () => btn.style.opacity = '0.8');
            btn.addEventListener('mouseout', () => btn.style.opacity = '1');
        });
    }

    // 模拟用户输入
    function simulateUserInput(input, text) {
        return new Promise((resolve) => {
            input.focus();
            input.click();

            let i = 0;
            function typeChar() {
                if (i < text.length) {
                    const char = text[i];

                    input.dispatchEvent(new KeyboardEvent('keydown', { key: char, bubbles: true }));
                    input.dispatchEvent(new KeyboardEvent('keypress', { key: char, bubbles: true }));

                    const propDescriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
                    propDescriptor.set.call(input, input.value + char);

                    input.dispatchEvent(new Event('input', { bubbles: true }));
                    input.dispatchEvent(new KeyboardEvent('keyup', { key: char, bubbles: true }));

                    i++;
                    setTimeout(typeChar, 50 + Math.random() * 50);
                } else {
                    setTimeout(() => {
                        input.dispatchEvent(new Event('blur', { bubbles: true }));
                        input.dispatchEvent(new MouseEvent('click', { bubbles: true }));
                        input.focus();
                        input.dispatchEvent(new Event('input', { bubbles: true }));
                        resolve();
                    }, 100);
                }
            }
            setTimeout(typeChar, 500);
        });
    }

    // 自动填充提取码
    function autoFillCode() {
        const lastOpenedLink = GM_getValue('lastOpenedLink', '');
        const lastOpenedCode = GM_getValue('lastOpenedCode', '');

        if (window.location.href.startsWith(lastOpenedLink) && lastOpenedCode !== '') {
            const fillCode = async () => {
                // 检查是否已经在分享页面
                if (document.querySelector('button.button--WC7or.primary--NVxfK.medium--Pt0UL.btn-save--SqM8z')) {
                    GM_setValue('linkStatus', 'success');
                    return;
                }

                // 检查分享是否已失效
                if (document.querySelector('div.share-error-tips--2G2l6')) {
                    GM_setValue('linkStatus', 'failed');
                    return;
                }

                const codeInputs = document.querySelectorAll('input[placeholder="请输入提取码"], input.ant-input[placeholder="请输入提取码"]');
                if (codeInputs.length > 0) {
                    for (const input of codeInputs) {
                        await simulateUserInput(input, lastOpenedCode);
                        await waitForButtonEnabled();
                        const submitButton = document.querySelector('button.button--2ImPj[type="submit"]');
                        if (submitButton && submitButton.getAttribute('data-is-disabled') !== 'true') {
                            submitButton.click();
                            // 等待页面加载
                            setTimeout(() => {
                                if (document.querySelector('button.button--WC7or.primary--NVxfK.medium--Pt0UL.btn-save--SqM8z')) {
                                    GM_setValue('linkStatus', 'success');
                                } else {
                                    GM_setValue('linkStatus', 'failed');
                                }
                            }, 2000);
                        }
                    }
                } else {
                    setTimeout(fillCode, 1000);
                }
            };

            setTimeout(fillCode, 1000);
        }

        // 添加这一段来更新 UI
        setTimeout(() => {
            const links = JSON.parse(GM_getValue('extractorLinks', '[]'));
            updateUI(textarea, resultDiv, textarea.value, links);
        }, 3000);  // 给予足够的时间让状态更新
    }

    // 等待按钮变为可点击状态
    function waitForButtonEnabled() {
        return new Promise((resolve) => {
            const checkButton = () => {
                const submitButton = document.querySelector('button.button--2ImPj[type="submit"]');
                if (submitButton && submitButton.getAttribute('data-is-disabled') !== 'true') {
                    resolve();
                } else {
                    setTimeout(checkButton, 100);
                }
            };
            checkButton();
        });
    }

    // 更新链接状态
    function updateLinkStatus(index, status) {
        const linkItem = document.getElementById(`link-item-${index}`);
        if (linkItem) {
            if (status === 'success') {
                linkItem.style.backgroundColor = '#C8E6C9';  // 浅绿色
            } else if (status === 'failed') {
                linkItem.style.backgroundColor = '#FFCDD2';  // 浅红色
            }
            saveLinkStatus(index, status);
        } else {
        }
    }

    // 打开所有链接
    async function openAllLinks(links) {
        for (let i = 0; i < links.length; i++) {
            const item = links[i];
            await openLink(item.link, item.code, i);
        }
    }

    // 主函数
    function main() {
        const triggerButton = createTriggerButton();
        const { container, extractButton, resetButton, openAllButton } = createUI();

        // 加载保存的数据
        const { text, links } = loadData();
        updateUI(textarea, resultDiv, text, links);

        // 检查是否需要自动打开UI
        const lastOpenedLink = GM_getValue('lastOpenedLink', '');
        if (window.location.href === lastOpenedLink) {
            container.style.display = 'block';
        }

        triggerButton.addEventListener('click', () => {
            container.style.display = container.style.display === 'none' ? 'block' : 'none';
        });

        extractButton.addEventListener('click', () => {
            const text = textarea.value;
            try {
                const extractedLinks = extractLinks(text);
                updateUI(textarea, resultDiv, text, extractedLinks);
                saveData(text, extractedLinks);
            } catch (error) {
                console.error('Error during link extraction:', error);
                alert('提取链接时发生错误,请查看控制台以获取更多信息。');
            }
        });

        resetButton.addEventListener('click', () => {
            textarea.value = '';
            resultDiv.innerHTML = '';
            saveData('', []);
            GM_setValue('linkStatuses', '{}'); // 清除所有保存的链接状态
            GM_setValue('lastOpenedLink', ''); // 清除最后打开的链接
            GM_setValue('lastOpenedCode', ''); // 清除最后使用的提取码
            GM_setValue('lastOpenedIndex', ''); // 清除最后打开的链接索引
            GM_setValue('linkStatus', ''); // 清除链接状态
        });

        openAllButton.addEventListener('click', async () => {
            const links = JSON.parse(GM_getValue('extractorLinks', '[]'));
            await openAllLinks(links);
            // 所有链接打开后,更新 UI
            updateUI(textarea, resultDiv, textarea.value, links);
        });

        // 自动填充提取码
        autoFillCode();

        // 监听 URL 变化
        let lastUrl = location.href;
        new MutationObserver(() => {
            const url = location.href;
            if (url !== lastUrl) {
                lastUrl = url;
                autoFillCode();
            }
        }).observe(document, { subtree: true, childList: true });

        // 添加定时器来检查链接状态
        setInterval(() => {
            const linkStatus = GM_getValue('linkStatus', '');
            const lastOpenedIndex = GM_getValue('lastOpenedIndex', '');
            if (linkStatus && lastOpenedIndex !== '') {
                updateLinkStatus(lastOpenedIndex, linkStatus);
                // 重置状态
                GM_setValue('linkStatus', '');
                GM_setValue('lastOpenedIndex', '');
            }
        }, 1000);
    }

    // 运行脚本
    main();
})();