超星学习通课件PDF下载助手「2026」

自动转换预览图URL并下载PDF文件(带时间命名)

// ==UserScript==
// @name         超星学习通课件PDF下载助手「2026」
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  自动转换预览图URL并下载PDF文件(带时间命名)
// @author       MOFAN---QQ3078329197
// @match        *://s3.ananas.chaoxing.com/sv-w*/doc/*/thumb/1.png
// @match        *://s3.cldisk.com/sv-w*/doc/*/thumb/1.png
// @match        *://s3.cldisk.com/sv-w*/doc/*/pdf/*.pdf
// @match        *://s3.ananas.chaoxing.com/sv-w*/doc/*/pdf/*.pdf
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    // 第一部分:URL转换功能
    const imagePattern = /^(https:\/\/s3\.(?:ananas\.chaoxing\.com|cldisk\.com)\/sv-w\d+\/doc\/.+)\/([a-f0-9]{32})\/thumb\/1\.png$/i;
    const imageMatch = window.location.href.match(imagePattern);

    // 如果是预览图URL,转换到PDF页面
    if (imageMatch) {
        const basePath = imageMatch[1];
        const docId = imageMatch[2];
        const newUrl = `${basePath}/${docId}/pdf/${docId}.pdf`;
        window.location.replace(newUrl);
        return; // 停止后续执行
    }

    // 第二部分:PDF下载功能
    const pdfPattern = /^(https:\/\/s3\.(?:ananas\.chaoxing\.com|cldisk\.com)\/sv-w\d+\/doc\/.+\/pdf\/[a-f0-9]{32}\.pdf)$/i;
    const pdfMatch = window.location.href.match(pdfPattern);

    // 如果是PDF页面URL,设置下载UI
    if (pdfMatch) {
        // 添加UI样式
        const style = document.createElement('style');
        style.textContent = `
            #cx-pdf-download-overlay {
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background: rgba(255,255,255,0.98);
                z-index: 999999;
                display: flex;
                flex-direction: column;
                align-items: center;
                justify-content: center;
                font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
                text-align: center;
                color: #2c3e50;
            }

            .download-card {
                width: 85%;
                max-width: 500px;
                background: white;
                border-radius: 20px;
                padding: 35px 30px;
                box-shadow: 0 15px 40px rgba(0, 0, 0, 0.15);
                border-top: 4px solid #3498db;
                position: relative;
                overflow: hidden;
                transform: scale(0.95);
                animation: cardEntrance 0.6s cubic-bezier(0.23, 1, 0.32, 1) forwards;
            }

            @keyframes cardEntrance {
                0% { transform: translateY(30px) scale(0.95); opacity: 0; }
                100% { transform: translateY(0) scale(1); opacity: 1; }
            }

            .download-decoration {
                position: absolute;
                top: -50px;
                right: -50px;
                width: 150px;
                height: 150px;
                background: radial-gradient(circle, rgba(52, 152, 219, 0.1) 0%, rgba(0,0,0,0) 70%);
                border-radius: 50%;
            }

            .download-icon {
                width: 85px;
                height: 85px;
                margin: 0 auto 15px;
                background: linear-gradient(135deg, #3498db, #2c81c9);
                border-radius: 50%;
                display: flex;
                align-items: center;
                justify-content: center;
                position: relative;
                box-shadow: 0 8px 25px rgba(52, 152, 219, 0.25);
                animation: pulse 2s infinite;
            }

            .download-icon svg {
                width: 35px;
                height: 35px;
                fill: white;
            }

            .download-title {
                font-size: 26px;
                font-weight: 700;
                color: #2c3e50;
                margin-bottom: 10px;
            }

            .download-subtitle {
                font-size: 16px;
                color: #7f8c8d;
                margin-bottom: 25px;
                line-height: 1.4;
            }

            .file-info-section {
                background: rgba(52, 152, 219, 0.07);
                border-radius: 15px;
                padding: 18px;
                margin: 20px 0;
                border: 1px dashed rgba(52, 152, 219, 0.15);
            }

            .file-name {
                font-size: 18px;
                font-weight: 600;
                word-break: break-all;
                margin: 10px 0;
                color: #2c3e50;
                padding: 10px;
                background: rgba(255, 255, 255, 0.9);
                border-radius: 10px;
                box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
            }

            .source-info {
                font-size: 14px;
                color: #7f8c8d;
                margin-top: 12px;
            }

            .countdown-section {
                margin: 25px 0;
            }

            .countdown-number {
                font-size: 50px;
                font-weight: 800;
                color: #3498db;
                margin: 15px 0;
                text-shadow: 0 4px 10px rgba(52, 152, 219, 0.2);
            }

            .countdown-label {
                font-size: 16px;
                margin-bottom: 20px;
                color: #7f8c8d;
            }

            .progress-bar {
                width: 100%;
                height: 6px;
                background: #e3eaef;
                border-radius: 4px;
                overflow: hidden;
                margin: 25px 0;
            }

            .progress-fill {
                height: 100%;
                background: linear-gradient(90deg, #3498db, #2ecc71);
                border-radius: 4px;
                width: 0%;
                transition: width 0.5s ease;
            }

            .status-message {
                margin-top: 20px;
                padding: 8px 18px;
                background: rgba(52, 152, 219, 0.1);
                border-radius: 16px;
                display: inline-block;
                font-size: 14px;
                color: #3498db;
                font-weight: 500;
            }

            .error-message {
                background: rgba(231, 76, 60, 0.1);
                color: #e74c3c;
            }

            @keyframes pulse {
                0% { box-shadow: 0 0 0 0 rgba(52, 152, 219, 0.5); }
                70% { box-shadow: 0 0 0 15px rgba(52, 152, 219, 0); }
                100% { box-shadow: 0 0 0 0 rgba(52, 152, 219, 0); }
            }

            .time-format-info {
                background: rgba(241, 196, 15, 0.1);
                border-radius: 8px;
                padding: 8px 15px;
                font-size: 13px;
                color: #e67e22;
                margin-top: 10px;
                display: inline-block;
            }

            /* 广告区域样式 */
            .ad-container {
                display: flex;
                justify-content: center;
                gap: 10px;
                margin: 15px 0;
                width: 100%;
            }

            .ad-text {
                font-size: 14px;
                color: #3498db;
                padding: 8px 12px;
                background: rgba(241, 196, 15, 0.15);
                border-radius: 30px;
                transition: all 0.3s;
                flex: 1;
                max-width: 220px;
                text-align: center;
            }

            .ad-button {
                background: linear-gradient(135deg, #2ecc71, #27ae60);
                color: white;
                border: none;
                border-radius: 24px;
                padding: 9px 20px;
                font-size: 14px;
                font-weight: 600;
                cursor: pointer;
                transition: all 0.3s;
                box-shadow: 0 4px 10px rgba(46, 204, 113, 0.35);
                text-decoration: none;
                white-space: nowrap;
            }

            .ad-button:hover {
                transform: translateY(-2px);
                box-shadow: 0 6px 15px rgba(46, 204, 113, 0.45);
                background: linear-gradient(135deg, #27ae60, #2ecc71);
            }
        `;
        document.head.appendChild(style);

        // 生成时间格式的文件名
        function getTimeBasedFilename() {
            const now = new Date();
            const year = now.getFullYear();
            const month = now.getMonth() + 1;
            const day = now.getDate();
            const hours = now.getHours();
            const minutes = now.getMinutes();
            const seconds = now.getSeconds();

            return `${year}.${month}.${day}.${hours}.${minutes}.${seconds}.pdf`;
        }

        // 创建下载UI
        function createDownloadUI() {
            if (document.getElementById('cx-pdf-download-overlay')) return;

            const overlay = document.createElement('div');
            overlay.id = 'cx-pdf-download-overlay';

            // 生成新的基于时间的文件名
            const timeBasedFilename = getTimeBasedFilename();

            overlay.innerHTML = `
                <div class="download-card">
                    <div class="download-decoration"></div>

                    <div class="download-icon">
                        <svg viewBox="0 0 24 24">
                            <path d="M5,20H19V18H5M19,9H15V3H9V9H5L12,17L19,9Z"/>
                        </svg>
                    </div>

                    <h1 class="download-title">超星文档下载器</h1>
                    <p class="download-subtitle">PDF将按当前时间自动命名</p>

                    <div class="file-info-section">
                        <div class="source-info">即将下载的文件:</div>
                        <div class="file-name" id="time-based-filename">${timeBasedFilename}</div>
                        <div class="source-info">来源:${getFileSource()}</div>
                        <div class="time-format-info">文件名格式:年.月.日.时.分.秒.pdf</div>
                    </div>
                    <!-- 广告区域 -->

                    <div class="ad-container">

                        <div class="ad-text">刷课可以添加QQ3078329197</div>

                        <div class="ad-text">超低价格超级稳超级快</div>

                        <a href="https://qm.qq.com/q/ujXdqqP0sM" target="_blank" class="ad-button">点击立即添加</a>

                    </div>

                    <div class="countdown-section">
                        <div class="countdown-number" id="countdown-timer">3</div>
                        <div class="countdown-label">下载将在 <span id="seconds-remaining">3</span> 秒后开始</div>

                        <div class="progress-bar">
                            <div class="progress-fill" id="download-progress"></div>
                        </div>
                    </div>

                    <div class="status-message" id="status-message">准备中...</div>
                </div>
            `;

            document.body.appendChild(overlay);

            // 创建隐藏的下载链接
            const downloadLink = document.createElement('a');
            downloadLink.id = 'direct-download-link';
            downloadLink.href = window.location.href;
            downloadLink.download = timeBasedFilename;
            downloadLink.style.display = 'none';
            document.body.appendChild(downloadLink);
        }

        // 获取文件来源
        function getFileSource() {
            const hostname = window.location.hostname;
            if (hostname.includes('ananas.chaoxing.com')) {
                return '超星学习平台';
            } else if (hostname.includes('cldisk.com')) {
                return '超星云盘';
            }
            return '超星文档服务';
        }

        // 更新下载状态
        function updateStatus(message) {
            const statusEl = document.getElementById('status-message');
            if (statusEl) {
                statusEl.textContent = message;
            }
        }

        // 启动倒计时
        function startCountdown() {
            const timerEl = document.getElementById('countdown-timer');
            const secondsEl = document.getElementById('seconds-remaining');
            const progressEl = document.getElementById('download-progress');

            let seconds = 3;
            const countdown = setInterval(() => {
                seconds--;

                if (timerEl) timerEl.textContent = seconds;
                if (secondsEl) secondsEl.textContent = seconds;

                const progress = 100 - (seconds / 3 * 100);
                if (progressEl) progressEl.style.width = `${progress}%`;

                updateStatus(`${seconds}秒后自动下载...`);

                if (seconds <= 0) {
                    clearInterval(countdown);
                    triggerDownload();
                }
            }, 1000);
        }

        // 触发下载
        function triggerDownload() {
            updateStatus('正在启动下载...');

            try {
                // 使用隐藏的下载链接
                const downloadLink = document.getElementById('direct-download-link');
                if (downloadLink) {
                    // 在触发前更新时间
                    const newFilename = getTimeBasedFilename();
                    downloadLink.download = newFilename;
                    document.getElementById('time-based-filename').textContent = newFilename;

                    downloadLink.click();
                    updateStatus('下载已启动!请查看浏览器下载');

                    // 2秒后关闭弹窗
                    setTimeout(() => {
                        const overlay = document.getElementById('cx-pdf-download-overlay');
                        if (overlay) {
                            overlay.style.opacity = '0';
                            overlay.style.transform = 'scale(0.95)';
                            setTimeout(() => overlay.remove(), 400);
                        }
                    }, 1500);
                } else {
                    throw new Error('无法找到下载链接');
                }
            } catch (e) {
                updateStatus('错误: ' + e.message);
                console.error('下载错误:', e);
            }
        }

        // 主函数
        function main() {
            // 创建UI
            createDownloadUI();

            // 启动3秒倒计时
            setTimeout(() => {
                startCountdown();
            }, 500);
        }

        // 延迟执行以确保页面加载
        setTimeout(main, 800);
    }
})();