├── .gitignore ├── images ├── 1.jpg ├── 2.jpg └── 3.jpg ├── package-lock.json ├── package.json ├── readme.md ├── src ├── icons │ ├── icon.ico │ ├── loadingscribd.gif │ └── scribd.png ├── index.css ├── index.html └── index.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /.vscode 2 | /node_modules 3 | /out -------------------------------------------------------------------------------- /images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dialguiba/scribd-downloader/91094aaa18006c20e51d256bafd92db493c44b5e/images/1.jpg -------------------------------------------------------------------------------- /images/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dialguiba/scribd-downloader/91094aaa18006c20e51d256bafd92db493c44b5e/images/2.jpg -------------------------------------------------------------------------------- /images/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dialguiba/scribd-downloader/91094aaa18006c20e51d256bafd92db493c44b5e/images/3.jpg -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scribd-downloader", 3 | "productName": "scribd-downloader", 4 | "version": "1.0.0", 5 | "description": "Download documents from scribd", 6 | "main": "src/index.js", 7 | "scripts": { 8 | "start": "electron-forge start", 9 | "package": "electron-forge package", 10 | "make-squirrel": "electron-forge make", 11 | "make-deb": "electron-forge make --targets @electron-forge/maker-deb", 12 | "make-zip": "electron-forge make --targets @electron-forge/maker-zip", 13 | "make-rpm": "electron-forge make --targets @electron-forge/maker-rpm", 14 | "publish": "electron-forge publish", 15 | "lint": "echo \"No linting configured\"" 16 | }, 17 | "keywords": [], 18 | "author": { 19 | "name": "dialguiba", 20 | "email": "dialguiba@gmail.com" 21 | }, 22 | "license": "MIT", 23 | "config": { 24 | "forge": { 25 | "packagerConfig": {}, 26 | "makers": [ 27 | { 28 | "name": "@electron-forge/maker-squirrel", 29 | "config": { 30 | "name": "scribd_downloader" 31 | } 32 | }, 33 | { 34 | "name": "@electron-forge/maker-zip", 35 | "platforms": [ 36 | "darwin" 37 | ] 38 | }, 39 | { 40 | "name": "@electron-forge/maker-deb", 41 | "config": {} 42 | }, 43 | { 44 | "name": "@electron-forge/maker-rpm", 45 | "config": {} 46 | } 47 | ] 48 | } 49 | }, 50 | "dependencies": { 51 | "electron-builder": "^22.8.1", 52 | "electron-squirrel-startup": "^1.0.0", 53 | "image-size": "^0.9.1", 54 | "pdfkit": "^0.11.0", 55 | "puppeteer": "^5.3.0", 56 | "puppeteer-cluster": "^0.22.0" 57 | }, 58 | "devDependencies": { 59 | "@electron-forge/cli": "^6.0.0-beta.53", 60 | "@electron-forge/maker-deb": "^6.0.0-beta.53", 61 | "@electron-forge/maker-rpm": "^6.0.0-beta.53", 62 | "@electron-forge/maker-squirrel": "^6.0.0-beta.53", 63 | "@electron-forge/maker-zip": "^6.0.0-beta.53", 64 | "electron": "10.1.2" 65 | } 66 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # **SCRIBD DOWNLOADER** 2 | 3 | YOU CAN DOWNLOAD THE APPLICATION FROM HERE 4 | [Download](https://drive.google.com/file/d/1uYsorrljOM7uLkiHN_ylX-ao0B0NtSHd/view?usp=sharing) 5 | 6 | ![1](/images/1.jpg) 7 | 8 | ![2](/images/2.jpg) 9 | 10 | ![3](/images/3.jpg) 11 | 12 | ## **Also, if you want to support me you can do it here :) :** 13 | 14 | [![coffee](https://img.buymeacoffee.com/api/?name=dialguiba&size=200&bg-image=bmc)](https://www.buymeacoffee.com/dialguiba) 15 | -------------------------------------------------------------------------------- /src/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dialguiba/scribd-downloader/91094aaa18006c20e51d256bafd92db493c44b5e/src/icons/icon.ico -------------------------------------------------------------------------------- /src/icons/loadingscribd.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dialguiba/scribd-downloader/91094aaa18006c20e51d256bafd92db493c44b5e/src/icons/loadingscribd.gif -------------------------------------------------------------------------------- /src/icons/scribd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dialguiba/scribd-downloader/91094aaa18006c20e51d256bafd92db493c44b5e/src/icons/scribd.png -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, 3 | Arial, sans-serif; 4 | margin: auto; 5 | max-width: 38rem; 6 | padding: 2rem; 7 | } 8 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Docs Downloader 5 | 9 | 10 | 11 | 18 | 19 |
20 |
21 | 22 | 29 |
30 | 33 |
34 |
35 |
36 | 37 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const puppeteer = require("puppeteer"); 2 | const { app, BrowserWindow, Menu, ipcMain } = require("electron"); 3 | const url = require("url"); 4 | const { Cluster } = require("puppeteer-cluster"); 5 | const PDFDocument = require("pdfkit"); 6 | var sizeOf = require("image-size"); 7 | const path = require("path"); 8 | const fs = require("fs"); 9 | const tempDir = "downloads/temp"; 10 | const downloadsDir = "downloads"; 11 | 12 | // Handle creating/removing shortcuts on Windows when installing/uninstalling. 13 | if (require("electron-squirrel-startup")) { 14 | // eslint-disable-line global-require 15 | app.quit(); 16 | } 17 | 18 | const createWindow = () => { 19 | // Create the browser window. 20 | const mainWindow = new BrowserWindow({ 21 | width: 800, 22 | height: 600, 23 | }); 24 | 25 | // and load the index.html of the app. 26 | mainWindow.loadFile(path.join(__dirname, "index.html")); 27 | 28 | // Open the DevTools. 29 | mainWindow.webContents.openDevTools(); 30 | }; 31 | 32 | // This method will be called when Electron has finished 33 | // initialization and is ready to create browser windows. 34 | // Some APIs can only be used after this event occurs. 35 | // Listen for app to be ready 36 | app.on("ready", function () { 37 | // Create new window 38 | mainWindow = new BrowserWindow({ 39 | width: 530, 40 | height: 313, 41 | webPreferences: { 42 | nodeIntegration: true, 43 | }, 44 | }); 45 | // Load html in window 46 | mainWindow.loadURL( 47 | url.format({ 48 | pathname: path.join(__dirname, "index.html"), 49 | protocol: "file:", 50 | slashes: true, 51 | }) 52 | ); 53 | // Quit app when closed 54 | mainWindow.on("closed", function () { 55 | app.quit(); 56 | }); 57 | 58 | // Build menu from template 59 | const mainMenu = Menu.buildFromTemplate(mainMenuTemplate); 60 | // Insert menu 61 | Menu.setApplicationMenu(mainMenu); 62 | }); 63 | 64 | // Catch document:get 65 | ipcMain.on("document:get", function (e, idbook, docName) { 66 | /* let anyError = getDocument(idbook); 67 | anyError.then((e) => { 68 | console.log("finished"); 69 | mainWindow.webContents.send("state:finished"); 70 | }); */ 71 | 72 | getDocument(idbook, docName).then(() => { 73 | mainWindow.webContents.send("state:finished"); 74 | }); 75 | 76 | //mainWindow.webContents.send("item:add", item); 77 | //addWindow.close(); 78 | // Still have a reference to addWindow in memory. Need to reclaim memory (Grabage collection) 79 | //addWindow = null; 80 | }); 81 | 82 | // Create menu template 83 | const mainMenuTemplate = [ 84 | // Each object is a dropdown 85 | { 86 | label: "File", 87 | submenu: [ 88 | { 89 | label: "Quit", 90 | accelerator: process.platform == "darwin" ? "Command+Q" : "Ctrl+Q", 91 | click() { 92 | app.quit(); 93 | }, 94 | }, 95 | ], 96 | }, 97 | ]; 98 | 99 | // If OSX, add empty object to menu 100 | if (process.platform == "darwin") { 101 | mainMenuTemplate.unshift({}); 102 | } 103 | 104 | // Quit when all windows are closed, except on macOS. There, it's common 105 | // for applications and their menu bar to stay active until the user quits 106 | // explicitly with Cmd + Q. 107 | app.on("window-all-closed", () => { 108 | if (process.platform !== "darwin") { 109 | app.quit(); 110 | } 111 | }); 112 | 113 | app.on("activate", () => { 114 | // On OS X it's common to re-create a window in the app when the 115 | // dock icon is clicked and there are no other windows open. 116 | if (BrowserWindow.getAllWindows().length === 0) { 117 | createWindow(); 118 | } 119 | }); 120 | 121 | let getDocument = async (idbook, docName) => { 122 | try { 123 | if (!fs.existsSync(downloadsDir)) { 124 | fs.mkdirSync(downloadsDir); 125 | if (!fs.existsSync(tempDir)) { 126 | fs.mkdirSync(tempDir); 127 | } 128 | } 129 | // 130 | 131 | const browser = await puppeteer.launch({ 132 | headless: true, 133 | args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"], 134 | }); 135 | const initPage = await browser.newPage(); 136 | await initPage.goto(`https://es.scribd.com/embeds/${idbook}/content?start_page=1`); 137 | //!Numero de paginas 138 | const elementPages = await initPage.$(".total_pages"); 139 | const textPages = await initPage.evaluate((elementPages) => elementPages.textContent, elementPages); 140 | const pagesNumber = textPages.match(/\d+/)[0]; 141 | console.log(pagesNumber); 142 | 143 | // Create a cluster with 2 workers 144 | const cluster = await Cluster.launch({ 145 | concurrency: Cluster.CONCURRENCY_CONTEXT, 146 | maxConcurrency: 3, 147 | headless: true, 148 | args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"], 149 | }); 150 | 151 | /* doc.pipe(fs.createWriteStream("output.pdf")); */ 152 | // Define a task (in this case: screenshot of page) 153 | await cluster.task(async ({ page, data: url }) => { 154 | await page.setViewport({ 155 | width: 800, 156 | height: 800, 157 | deviceScaleFactor: 2.5, 158 | }); 159 | await page.goto(url, { waitUntil: "networkidle2" }); 160 | 161 | //!Eliminate 162 | await page.evaluate(() => { 163 | let example = document.querySelector(".toolbar_drop"); 164 | let example3 = document.querySelector("#fb-root"); 165 | let example4 = document.querySelector(".mobile_overlay"); 166 | let example5 = document.querySelector("#font_preload_bed"); 167 | let example6 = document.querySelector(".osano-cm-window__dialog"); 168 | 169 | example.parentNode.removeChild(example); 170 | example3.parentNode.removeChild(example3); 171 | example4.parentNode.removeChild(example4); 172 | example5.parentNode.removeChild(example5); 173 | example6.parentNode.removeChild(example6); 174 | }); 175 | //elemento 176 | let actualPage = url.match(/start_page=(\d+)/)[1]; 177 | let selector = `#page${actualPage}`; 178 | const rect = await page.evaluate((selector) => { 179 | const element = document.querySelector(selector); 180 | if (!element) { 181 | return null; 182 | } 183 | var { x, y, width, height } = element.getBoundingClientRect(); 184 | 185 | return { left: x, top: y, width, height, id: element.id }; 186 | }, selector); 187 | 188 | const path = `${tempDir}/${actualPage}.png`; 189 | 190 | await page.waitForSelector(`#page${actualPage}`).then(async () => { 191 | await page.screenshot({ 192 | path, 193 | clip: { 194 | x: rect.left, 195 | y: rect.top, 196 | width: rect.width, 197 | height: rect.height, 198 | }, 199 | }); 200 | }); 201 | }); 202 | 203 | for (i = 1; i <= pagesNumber; i++) { 204 | cluster.queue(`https://es.scribd.com/embeds/${idbook}/content?start_page=${i}&view_mode=slideshow`); 205 | } 206 | 207 | // Shutdown after everything is done 208 | await cluster.idle(); 209 | await cluster.close(); 210 | 211 | createPdf(pagesNumber, docName); 212 | deleteTemp(); 213 | } catch (e) { 214 | console.log(e); 215 | } 216 | }; 217 | 218 | function createPdf(numberPages, fileName) { 219 | var dimensions = sizeOf("downloads/temp/1.png"); 220 | let width = dimensions.width; 221 | let height = dimensions.height; 222 | // Create a document 223 | let doc; 224 | 225 | if (width > height) { 226 | doc = new PDFDocument({ layout: "landscape" }); 227 | } else { 228 | doc = new PDFDocument({ layout: "portrait" }); 229 | } 230 | 231 | // Pipe its output somewhere, like to a file or HTTP response 232 | // See below for browser usage 233 | doc.pipe(fs.createWriteStream(`downloads/${fileName}.pdf`)); 234 | 235 | doc.text("").moveDown(8); 236 | doc 237 | .text("Gracias por usar mi programa", { 238 | align: "center", 239 | }) 240 | .fontSize(12); 241 | doc.text("Desarrollado por: dialguiba", { align: "center" }).fontSize(12).moveDown(8); 242 | 243 | doc.text("Si este servicio de descarga te sirvió y gustas apoyar puedes hacerlo en:", { align: "center" }).fontSize(15).moveDown(1); 244 | doc 245 | .text( 246 | "https://www.buymeacoffee.com/dialguiba", 247 | 248 | { align: "center" } 249 | ) 250 | .fontSize(15) 251 | .moveDown(4); 252 | doc 253 | .text("Si tienes algún problema o sugerencia: dialguiba1994@gmail.com", { 254 | align: "center", 255 | }) 256 | .fontSize(15) 257 | .moveDown(1); 258 | 259 | // Add an image, constrain it to a given size, and center it vertically and horizontally 260 | /* if (width > height) { 261 | doc.image("downloads/temp/1.png", { 262 | fit: [680, 460], 263 | //fit: [500, 550], 264 | //width: 800, 265 | align: "center", 266 | valign: "center", 267 | }); 268 | } else { 269 | doc.image("downloads/temp/1.png", { 270 | fit: [460, 720], 271 | align: "center", 272 | valign: "center", 273 | }); 274 | } */ 275 | 276 | //deleteFile("./1.png"); 277 | 278 | for (i = 1; i <= numberPages; i++) { 279 | if (width > height) { 280 | doc.addPage().image(`downloads/temp/${i}.png`, { 281 | fit: [680, 460], 282 | //fit: [500, 550], 283 | //width: 800, 284 | align: "center", 285 | valign: "center", 286 | }); 287 | } else { 288 | doc.addPage().image(`downloads/temp/${i}.png`, { 289 | fit: [460, 720], 290 | align: "center", 291 | valign: "center", 292 | }); 293 | } 294 | } 295 | let finished = "finished"; 296 | // Finalize PDF file 297 | doc.save(); 298 | doc.end(); 299 | return finished; 300 | } 301 | 302 | function deleteTemp() { 303 | const directory = "downloads/temp/"; 304 | 305 | fs.readdir(directory, (err, files) => { 306 | if (err) throw err; 307 | 308 | for (const file of files) { 309 | fs.unlink(path.join(directory, file), (err) => { 310 | if (err) throw err; 311 | }); 312 | } 313 | }); 314 | } 315 | --------------------------------------------------------------------------------