- // ==UserScript==
- // @name Scribd Enhancer All-in-One (v2.3)
- // @namespace https://greasyfork.org/users/Eliminater74
- // @version 2.3
- // @description Unblur Scribd, scrape visible text/images with OCR, optional live preview, export to TXT/HTML, and dark mode. Built by Eliminater74 with full UI toggle control and persistent settings.
- // @author Eliminater74
- // @license MIT
- // @match *://*.scribd.com/*
- // @grant none
- // @icon https://s-f.scribdassets.com/favicon.ico
- // ==/UserScript==
-
- (function () {
- 'use strict';
-
- // Load Tesseract.js for OCR
- const script = document.createElement('script');
- script.src = 'https://cdn.jsdelivr.net/npm/tesseract.js@4.0.2/dist/tesseract.min.js';
- document.head.appendChild(script);
-
- const LS_KEY = 'scribdEnhancerSettings';
- const defaultSettings = {
- unblur: true,
- printableView: true,
- autoScrape: true,
- darkMode: false,
- debug: false,
- showPreview: true
- };
- const settings = { ...defaultSettings, ...JSON.parse(localStorage.getItem(LS_KEY) || '{}') };
- const saveSettings = () => localStorage.setItem(LS_KEY, JSON.stringify(settings));
-
- // Styles
- const style = document.createElement('style');
- style.textContent = `
- #se-floating-gear {
- position: fixed; bottom: 20px; right: 20px; z-index: 9999;
- width: 40px; height: 40px; border-radius: 50%; background: #333;
- color: white; font-size: 24px; text-align: center; line-height: 40px;
- cursor: pointer; box-shadow: 0 0 6px rgba(0,0,0,0.4);
- }
- #se-menu {
- position: fixed; bottom: 70px; right: 20px; z-index: 9999;
- background: #fff; border: 1px solid #ccc; padding: 10px; border-radius: 10px;
- display: none; font-family: sans-serif; box-shadow: 0 0 10px rgba(0,0,0,0.3);
- }
- #se-menu label {
- display: block; margin: 5px 0; color: #000; font-size: 14px;
- }
- #se-preview-box {
- position: fixed; top: 10px; right: 20px; bottom: 150px;
- width: 300px; background: #fff; color: #000; overflow: auto;
- z-index: 9998; border: 1px solid #999; padding: 10px; font-family: monospace;
- font-size: 12px; white-space: pre-wrap; border-radius: 10px;
- }
- body.dark-mode #se-preview-box {
- background: #222; color: #eee; border-color: #555;
- }
- div[style*="rgb(244, 221, 221)"],
- div[style*="#f4dddd"],
- div[style*="rgb(255, 244, 211)"],
- div[style*="#fff4d3"] {
- display: none !important;
- }
- body.dark-mode, html.dark-mode {
- background: #121212 !important;
- color: #e0e0e0 !important;
- }
- .dark-mode * {
- background: transparent !important;
- color: #e0e0e0 !important;
- border-color: #444 !important;
- }
- .dark-mode a { color: #66c0ff !important; }
- `;
- document.head.appendChild(style);
-
- // Gear Icon & Settings Menu
- const gear = document.createElement('div');
- gear.id = 'se-floating-gear';
- gear.textContent = '⚙';
- document.body.appendChild(gear);
-
- const menu = document.createElement('div');
- menu.id = 'se-menu';
- menu.innerHTML = Object.keys(defaultSettings).map(key => {
- const checked = settings[key] ? 'checked' : '';
- const label = key.replace(/([A-Z])/g, ' $1').replace(/^./, s => s.toUpperCase());
- return `<label><input type="checkbox" data-key="${key}" ${checked}> ${label}</label>`;
- }).join('');
- document.body.appendChild(menu);
-
- gear.addEventListener('click', () => {
- menu.style.display = menu.style.display === 'none' ? 'block' : 'none';
- });
-
- menu.addEventListener('change', e => {
- const key = e.target.dataset.key;
- settings[key] = e.target.checked;
- saveSettings();
- applyDarkMode();
- location.reload();
- });
-
- function applyDarkMode() {
- if (settings.darkMode) {
- document.documentElement.classList.add('dark-mode');
- document.body.classList.add('dark-mode');
- } else {
- document.documentElement.classList.remove('dark-mode');
- document.body.classList.remove('dark-mode');
- }
- }
-
- function unblurContent() {
- if (!settings.unblur) return;
- const clean = () => {
- document.querySelectorAll('.blurred_page, .promo_div').forEach(el => el.remove());
- document.querySelectorAll('[unselectable="on"]').forEach(el => el.removeAttribute('unselectable'));
- document.querySelectorAll('*').forEach(el => {
- const cs = getComputedStyle(el);
- if (cs.color === 'transparent') el.style.color = '#111';
- if (cs.textShadow && cs.textShadow.includes('white')) el.style.textShadow = 'none';
- el.removeAttribute('data-initial-color');
- el.removeAttribute('data-initial-text-shadow');
- });
- };
- clean();
- new MutationObserver(clean).observe(document.body, { childList: true, subtree: true });
- }
-
- function injectScrapeUI() {
- const container = document.createElement('div');
- container.id = 'scraped-book';
- container.style = 'display:none;';
- document.body.appendChild(container);
-
- const preview = document.createElement('div');
- preview.id = 'se-preview-box';
- preview.textContent = '[Preview Ready]\n';
- if (settings.showPreview) document.body.appendChild(preview);
-
- const addButton = (label, top, color, handler) => {
- const btn = document.createElement('button');
- btn.textContent = label;
- btn.style = `position:fixed;top:${top}px;left:10px;z-index:9999;padding:10px;background:${color};color:#fff;border:none;border-radius:5px;`;
- btn.onclick = handler;
- document.body.appendChild(btn);
- };
-
- addButton('📖 Start Scraping Book', 50, '#2196F3', () => scrapePages(container, preview));
- addButton('🖨️ Print to PDF', 90, '#4CAF50', () => {
- const win = window.open('', 'PrintBook');
- win.document.write(`<html><head><title>Book</title></head><body>${container.innerHTML}</body></html>`);
- win.document.close(); win.focus(); setTimeout(() => win.print(), 600);
- });
- addButton('📄 Export as TXT', 130, '#6A1B9A', () => {
- const blob = new Blob([preview.textContent], { type: 'text/plain' });
- const link = document.createElement('a');
- link.href = URL.createObjectURL(blob);
- link.download = 'scribd_extracted.txt';
- link.click();
- });
- addButton('🧾 Export as HTML', 170, '#00796B', () => {
- const blob = new Blob([`<html><body><pre>${preview.textContent}</pre></body></html>`], { type: 'text/html' });
- const link = document.createElement('a');
- link.href = URL.createObjectURL(blob);
- link.download = 'scribd_extracted.html';
- link.click();
- });
- }
-
- function scrapePages(container, preview) {
- window.scrollTo(0, 0);
- document.querySelectorAll('img[data-src], img[data-lazy], img.lazyload').forEach(img => {
- const src = img.getAttribute('data-src') || img.getAttribute('data-lazy');
- if (src) img.setAttribute('src', src);
- });
-
- const pages = document.querySelectorAll('.text_layer, .page, .reader_column, [id^="page_container"]');
- let count = 0;
-
- pages.forEach(page => {
- const clone = page.cloneNode(true);
-
- clone.querySelectorAll('img').forEach(img => {
- const src = img.getAttribute('src') || img.getAttribute('data-src') || img.getAttribute('data-lazy');
- if (src) img.setAttribute('src', src);
- });
-
- clone.querySelectorAll('*').forEach(el => {
- const bg = window.getComputedStyle(el).backgroundImage;
- if (bg && bg !== 'none') el.style.backgroundImage = bg;
- });
-
- page.querySelectorAll('img').forEach(img => {
- if (window.Tesseract) {
- window.Tesseract.recognize(img.src, 'eng').then(result => {
- const text = result.data.text.trim();
- if (text && settings.showPreview) {
- const p = document.createElement('p');
- p.textContent = '[OCR] ' + text;
- preview.textContent += p.textContent + '\n';
- container.appendChild(p);
- }
- });
- }
- });
-
- const plainText = page.innerText.trim();
- if (plainText) {
- const textNode = document.createElement('p');
- textNode.textContent = plainText;
- if (settings.showPreview) preview.textContent += plainText + '\n';
- container.appendChild(textNode);
- }
-
- container.appendChild(clone);
- count++;
- });
-
- alert(count > 0
- ? `✅ Scraped ${count} pages (text + images + OCR).`
- : `❌ No readable content found.`);
- }
-
- window.addEventListener('load', () => {
- applyDarkMode();
- unblurContent();
- if (settings.autoScrape) injectScrapeUI();
- });
- })();