Scribd Downloader

📚 Download documents from Scribd for free as PDF - Fully automated!

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Scribd Downloader
// @namespace    https://github.com/ThanhNguyxn/scribd-downloader
// @version      2.1.0
// @description  📚 Download documents from Scribd for free as PDF - Fully automated!
// @author       ThanhNguyxn
// @match        https://www.scribd.com/*
// @icon         https://www.scribd.com/favicon.ico
// @grant        GM_addStyle
// @grant        GM_setClipboard
// @grant        GM_openInTab
// @run-at       document-idle
// @license      MIT
// @homepageURL  https://github.com/ThanhNguyxn/scribd-downloader
// @supportURL   https://github.com/ThanhNguyxn/scribd-downloader/issues
// ==/UserScript==

(function() {
    'use strict';

    // ==================== CONFIG ====================
    const BUTTON_DELAY = 1500;
    const GITHUB_URL = 'https://github.com/ThanhNguyxn/scribd-downloader';
    const DONATE_URL = 'https://buymeacoffee.com/thanhnguyxn';

    // ==================== STYLES ====================
    const styles = `
        /* Main floating button - TOP RIGHT */
        #sd-floating-btn {
            position: fixed !important;
            top: 80px !important;
            right: 20px !important;
            z-index: 2147483647 !important;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
            color: white !important;
            border: none !important;
            padding: 12px 20px !important;
            border-radius: 12px !important;
            font-size: 14px !important;
            font-weight: 700 !important;
            cursor: pointer !important;
            box-shadow: 0 4px 15px rgba(102, 126, 234, 0.5) !important;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
            display: flex !important;
            align-items: center !important;
            gap: 8px !important;
            transition: all 0.3s ease !important;
            text-decoration: none !important;
        }

        #sd-floating-btn:hover {
            transform: scale(1.05) !important;
            box-shadow: 0 6px 25px rgba(102, 126, 234, 0.6) !important;
        }

        #sd-floating-btn:active {
            transform: scale(0.98) !important;
        }

        #sd-floating-btn.loading {
            background: linear-gradient(135deg, #ffa726 0%, #fb8c00 100%) !important;
            pointer-events: none !important;
        }

        #sd-popup {
            position: fixed !important;
            top: 0 !important;
            left: 0 !important;
            width: 100% !important;
            height: 100% !important;
            background: rgba(0,0,0,0.85) !important;
            z-index: 2147483647 !important;
            display: flex !important;
            justify-content: center !important;
            align-items: center !important;
            opacity: 0;
            visibility: hidden;
            transition: all 0.3s ease !important;
        }

        #sd-popup.show {
            opacity: 1 !important;
            visibility: visible !important;
        }

        #sd-popup-content {
            background: white !important;
            padding: 30px !important;
            border-radius: 20px !important;
            max-width: 420px !important;
            width: 90% !important;
            text-align: center !important;
            box-shadow: 0 25px 80px rgba(0,0,0,0.4) !important;
            transform: scale(0.9);
            transition: transform 0.3s ease !important;
        }

        #sd-popup.show #sd-popup-content {
            transform: scale(1) !important;
        }

        #sd-popup h2 {
            margin: 0 0 20px 0 !important;
            color: #333 !important;
            font-size: 22px !important;
            font-weight: 700 !important;
        }

        #sd-url-display {
            background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%) !important;
            color: #00d9ff !important;
            padding: 15px !important;
            border-radius: 10px !important;
            font-family: 'Monaco', 'Consolas', monospace !important;
            font-size: 12px !important;
            word-break: break-all !important;
            margin: 15px 0 !important;
            text-align: left !important;
            border: 2px solid #667eea !important;
            user-select: all !important;
            cursor: text !important;
        }

        .sd-btn {
            padding: 12px 24px !important;
            border: none !important;
            border-radius: 10px !important;
            font-size: 14px !important;
            font-weight: 600 !important;
            cursor: pointer !important;
            transition: all 0.2s ease !important;
            margin: 5px !important;
            text-decoration: none !important;
            display: inline-flex !important;
            align-items: center !important;
            gap: 6px !important;
        }

        .sd-btn-primary {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
            color: white !important;
        }

        .sd-btn-success {
            background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%) !important;
            color: white !important;
        }

        .sd-btn-warning {
            background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%) !important;
            color: white !important;
        }

        .sd-btn-close {
            background: #e0e0e0 !important;
            color: #333 !important;
        }

        .sd-btn:hover {
            transform: scale(1.05) !important;
            box-shadow: 0 5px 20px rgba(0,0,0,0.2) !important;
        }

        .sd-info {
            background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%) !important;
            border-left: 4px solid #4caf50 !important;
            padding: 12px 15px !important;
            margin: 15px 0 !important;
            border-radius: 0 10px 10px 0 !important;
            text-align: left !important;
            font-size: 13px !important;
            color: #2e7d32 !important;
        }

        .sd-btn-group {
            display: flex !important;
            gap: 8px !important;
            justify-content: center !important;
            flex-wrap: wrap !important;
            margin-top: 15px !important;
        }

        .sd-links {
            margin-top: 20px !important;
            padding-top: 15px !important;
            border-top: 1px solid #eee !important;
            display: flex !important;
            justify-content: center !important;
            gap: 15px !important;
        }

        .sd-link {
            color: #666 !important;
            text-decoration: none !important;
            font-size: 12px !important;
            display: flex !important;
            align-items: center !important;
            gap: 5px !important;
            transition: color 0.2s !important;
        }

        .sd-link:hover {
            color: #667eea !important;
        }

        /* Progress bar in button */
        .sd-progress-inline {
            width: 100px !important;
            height: 6px !important;
            background: rgba(255,255,255,0.3) !important;
            border-radius: 3px !important;
            overflow: hidden !important;
            margin-left: 8px !important;
        }

        .sd-progress-inline-fill {
            height: 100% !important;
            background: white !important;
            width: 0% !important;
            transition: width 0.3s ease !important;
            border-radius: 3px !important;
        }

        /* For embed page - TOP RIGHT green button */
        #sd-download-btn {
            position: fixed !important;
            top: 20px !important;
            right: 20px !important;
            z-index: 2147483647 !important;
            background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%) !important;
            color: white !important;
            border: none !important;
            padding: 14px 24px !important;
            border-radius: 12px !important;
            font-size: 15px !important;
            font-weight: 700 !important;
            cursor: pointer !important;
            box-shadow: 0 4px 15px rgba(17, 153, 142, 0.5) !important;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
            display: flex !important;
            align-items: center !important;
            gap: 8px !important;
            transition: all 0.3s ease !important;
        }

        #sd-download-btn:hover {
            transform: scale(1.05) !important;
            box-shadow: 0 6px 25px rgba(17, 153, 142, 0.6) !important;
        }

        #sd-download-btn.loading {
            background: linear-gradient(135deg, #ffa726 0%, #fb8c00 100%) !important;
        }

        #sd-progress-popup {
            position: fixed !important;
            top: 0 !important;
            left: 0 !important;
            width: 100% !important;
            height: 100% !important;
            background: rgba(0,0,0,0.9) !important;
            z-index: 2147483647 !important;
            display: flex !important;
            justify-content: center !important;
            align-items: center !important;
        }

        #sd-progress-content {
            background: white !important;
            padding: 35px !important;
            border-radius: 20px !important;
            text-align: center !important;
            min-width: 320px !important;
        }

        #sd-progress-bar {
            width: 100% !important;
            height: 10px !important;
            background: #e0e0e0 !important;
            border-radius: 10px !important;
            overflow: hidden !important;
            margin: 20px 0 !important;
        }

        #sd-progress-fill {
            height: 100% !important;
            background: linear-gradient(90deg, #667eea, #764ba2) !important;
            width: 0% !important;
            transition: width 0.3s ease !important;
            border-radius: 10px !important;
        }

        #sd-progress-text {
            color: #666 !important;
            font-size: 15px !important;
            margin-bottom: 10px !important;
        }
    `;

    // Inject styles
    const styleEl = document.createElement('style');
    styleEl.textContent = styles;
    document.head.appendChild(styleEl);

    // ==================== UTILITIES ====================

    function getDocId() {
        const url = window.location.href;
        const match = url.match(/(?:document|doc|embeds|read)\/(\d+)/);
        return match ? match[1] : null;
    }

    function isEmbed() {
        return window.location.href.includes('/embeds/');
    }

    function getEmbedUrl(id) {
        return `https://www.scribd.com/embeds/${id}/content`;
    }

    function copyText(text) {
        try {
            if (typeof GM_setClipboard === 'function') {
                GM_setClipboard(text, 'text');
                return true;
            }
        } catch(e) {}
        
        try {
            navigator.clipboard.writeText(text);
            return true;
        } catch(e) {}

        try {
            const ta = document.createElement('textarea');
            ta.value = text;
            ta.style.cssText = 'position:fixed;top:-9999px;left:-9999px';
            document.body.appendChild(ta);
            ta.select();
            document.execCommand('copy');
            ta.remove();
            return true;
        } catch(e) {}

        return false;
    }

    function sleep(ms) {
        return new Promise(r => setTimeout(r, ms));
    }

    // ==================== MAIN PAGE FUNCTIONS ====================

    function showMainButton() {
        if (document.getElementById('sd-floating-btn')) return;
        
        const docId = getDocId();
        if (!docId) return;

        const btn = document.createElement('button');
        btn.id = 'sd-floating-btn';
        btn.innerHTML = '📥 Download PDF';
        btn.onclick = startAutoDownload;
        document.body.appendChild(btn);
    }

    async function startAutoDownload() {
        const btn = document.getElementById('sd-floating-btn');
        const docId = getDocId();
        
        if (!docId) {
            alert('Cannot find document ID!');
            return;
        }

        const embedUrl = getEmbedUrl(docId);
        
        // Change button to loading state
        btn.classList.add('loading');
        btn.innerHTML = '⏳ Opening...';

        // Show quick popup then auto-open
        showAutoPopup(embedUrl);
    }

    function showAutoPopup(embedUrl) {
        const existing = document.getElementById('sd-popup');
        if (existing) existing.remove();

        const popup = document.createElement('div');
        popup.id = 'sd-popup';
        popup.innerHTML = `
            <div id="sd-popup-content">
                <h2>📚 Scribd Downloader</h2>
                
                <div class="sd-info">
                    ✨ <strong>Auto mode:</strong> Opening embed page in new tab...
                </div>

                <div id="sd-url-display">${embedUrl}</div>

                <p style="color: #666; font-size: 13px; margin: 15px 0;">
                    A new tab will open automatically.<br>
                    Click the <strong style="color: #11998e;">green button</strong> there to download!
                </p>

                <div class="sd-btn-group">
                    <button class="sd-btn sd-btn-success" id="sd-open-now">🚀 Open Now</button>
                    <button class="sd-btn sd-btn-warning" id="sd-open-incognito">🕵️ Manual (Incognito)</button>
                    <button class="sd-btn sd-btn-close" id="sd-close-btn">Close</button>
                </div>

                <div class="sd-links">
                    <a href="${GITHUB_URL}" target="_blank" class="sd-link">
                        ⭐ GitHub
                    </a>
                    <a href="${DONATE_URL}" target="_blank" class="sd-link">
                        ☕ Buy me a coffee
                    </a>
                </div>
            </div>
        `;

        document.body.appendChild(popup);
        
        requestAnimationFrame(() => {
            popup.classList.add('show');
        });

        // Auto open after 1 second
        const autoTimer = setTimeout(() => {
            openEmbedPage(embedUrl);
        }, 1500);

        // Event handlers
        document.getElementById('sd-open-now').onclick = function() {
            clearTimeout(autoTimer);
            openEmbedPage(embedUrl);
        };

        document.getElementById('sd-open-incognito').onclick = function() {
            clearTimeout(autoTimer);
            copyText(embedUrl);
            this.innerHTML = '✅ URL Copied!';
            showManualInstructions();
        };

        document.getElementById('sd-close-btn').onclick = function() {
            clearTimeout(autoTimer);
            closePopup();
        };

        popup.onclick = function(e) {
            if (e.target === popup) {
                clearTimeout(autoTimer);
                closePopup();
            }
        };
    }

    function openEmbedPage(url) {
        // Open in new tab
        if (typeof GM_openInTab === 'function') {
            GM_openInTab(url, { active: true });
        } else {
            window.open(url, '_blank');
        }
        
        // Update button
        const btn = document.getElementById('sd-floating-btn');
        if (btn) {
            btn.classList.remove('loading');
            btn.innerHTML = '✅ Opened!';
            setTimeout(() => {
                btn.innerHTML = '📥 Download PDF';
            }, 3000);
        }

        // Close popup after small delay
        setTimeout(closePopup, 500);
    }

    function showManualInstructions() {
        const content = document.getElementById('sd-popup-content');
        if (!content) return;

        content.innerHTML = `
            <h2>🕵️ Manual Mode</h2>
            
            <div class="sd-info">
                ✅ <strong>URL copied!</strong> Follow these steps:
            </div>

            <ol style="text-align: left; color: #444; line-height: 1.8; padding-left: 20px; margin: 20px 0;">
                <li>Press <strong>Ctrl+Shift+N</strong> (Incognito window)</li>
                <li>Paste the URL (<strong>Ctrl+V</strong>)</li>
                <li>Press <strong>Enter</strong></li>
                <li>Click the <strong style="color: #11998e;">green Download button</strong></li>
            </ol>

            <div class="sd-btn-group">
                <button class="sd-btn sd-btn-close" id="sd-close-btn2">Got it!</button>
            </div>

            <div class="sd-links">
                <a href="${GITHUB_URL}" target="_blank" class="sd-link">⭐ GitHub</a>
                <a href="${DONATE_URL}" target="_blank" class="sd-link">☕ Buy me a coffee</a>
            </div>
        `;

        document.getElementById('sd-close-btn2').onclick = closePopup;
    }

    function closePopup() {
        const popup = document.getElementById('sd-popup');
        const btn = document.getElementById('sd-floating-btn');
        
        if (popup) {
            popup.classList.remove('show');
            setTimeout(() => popup.remove(), 300);
        }
        
        if (btn) {
            btn.classList.remove('loading');
            btn.innerHTML = '📥 Download PDF';
        }
    }

    // ==================== EMBED PAGE FUNCTIONS ====================

    function showEmbedButton() {
        if (document.getElementById('sd-download-btn')) return;

        const btn = document.createElement('button');
        btn.id = 'sd-download-btn';
        btn.innerHTML = '⬇️ Download PDF';
        btn.onclick = startDownload;
        document.body.appendChild(btn);

        // Auto-start download after 2 seconds if opened from main script
        if (document.referrer.includes('scribd.com')) {
            setTimeout(() => {
                const autoBtn = document.getElementById('sd-download-btn');
                if (autoBtn && !autoBtn.classList.contains('loading')) {
                    // Show notification
                    autoBtn.innerHTML = '🚀 Starting...';
                    setTimeout(startDownload, 500);
                }
            }, 2000);
        }
    }

    async function startDownload() {
        const btn = document.getElementById('sd-download-btn');
        btn.classList.add('loading');
        btn.innerHTML = '⏳ Processing...';

        // Create progress popup
        const progress = document.createElement('div');
        progress.id = 'sd-progress-popup';
        progress.innerHTML = `
            <div id="sd-progress-content">
                <h2>📚 Preparing PDF...</h2>
                <div id="sd-progress-text">Loading pages...</div>
                <div id="sd-progress-bar">
                    <div id="sd-progress-fill"></div>
                </div>
                <p style="color: #888; font-size: 12px; margin-top: 15px;">
                    Please wait, this may take a moment...
                </p>
            </div>
        `;
        document.body.appendChild(progress);

        const fill = document.getElementById('sd-progress-fill');
        const text = document.getElementById('sd-progress-text');

        try {
            // Step 1: Scroll all pages
            text.textContent = '📄 Loading all pages...';
            const pages = document.querySelectorAll("[class*='page']");
            
            if (pages.length > 0) {
                for (let i = 0; i < pages.length; i++) {
                    pages[i].scrollIntoView({ behavior: 'auto', block: 'center' });
                    await sleep(350);
                    const pct = Math.round(((i + 1) / pages.length) * 50);
                    fill.style.width = pct + '%';
                    text.textContent = `📄 Loading page ${i + 1}/${pages.length}...`;
                }
            } else {
                // Fallback scroll
                const h = document.documentElement.scrollHeight;
                for (let i = 0; i <= 20; i++) {
                    window.scrollTo(0, (h / 20) * i);
                    await sleep(150);
                    fill.style.width = (i * 2.5) + '%';
                }
            }

            // Step 2: Remove junk
            fill.style.width = '60%';
            text.textContent = '🧹 Cleaning up...';
            await sleep(200);

            const junk = [
                '.toolbar_top', '.toolbar_bottom', '.promo_div',
                '[class*="blur"]', '[class*="paywall"]', '[class*="overlay"]',
                '[class*="upsell"]', '[class*="signup"]', '.ReactModalPortal',
                '[class*="banner"]', '[class*="modal"]'
            ];
            junk.forEach(sel => {
                try {
                    document.querySelectorAll(sel).forEach(el => el.remove());
                } catch(e) {}
            });

            // Step 3: Fix visibility
            fill.style.width = '80%';
            text.textContent = '✨ Optimizing...';
            await sleep(200);

            document.querySelectorAll('*').forEach(el => {
                try {
                    const s = getComputedStyle(el);
                    if (s.filter?.includes('blur')) el.style.filter = 'none';
                    if (parseFloat(s.opacity) < 1) el.style.opacity = '1';
                } catch(e) {}
            });

            // Scroll to top
            window.scrollTo(0, 0);

            // Step 4: Print
            fill.style.width = '100%';
            text.textContent = '✅ Ready! Opening print dialog...';
            await sleep(400);

            progress.remove();
            window.print();

            // Reset button
            btn.classList.remove('loading');
            btn.innerHTML = '✅ Done! Print again?';
            
            setTimeout(() => {
                btn.innerHTML = '⬇️ Download PDF';
            }, 5000);

        } catch (err) {
            console.error('Download error:', err);
            progress.remove();
            btn.classList.remove('loading');
            btn.innerHTML = '❌ Error - Try again';
            
            setTimeout(() => {
                btn.innerHTML = '⬇️ Download PDF';
            }, 3000);
        }
    }

    // ==================== INIT ====================

    function init() {
        // Check if we're on Scribd
        if (!window.location.hostname.includes('scribd.com')) return;

        setTimeout(() => {
            if (isEmbed()) {
                showEmbedButton();
            } else if (getDocId()) {
                showMainButton();
            }
        }, BUTTON_DELAY);
    }

    // Run
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

})();