├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | temp 3 | *.pdf -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Enrico 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bSmart-downloader 2 | Download your books from bSmart as offline pdf 3 | 4 | **NOW WITH DIGIBOOK24 (EdiErmes) SUPPORT!!!** 5 | 6 | 7 | ## How to use 8 | 9 | ### Installation 10 | (NOTE: this assumes you already have [node.js](https://nodejs.org/)) 11 | 1. Download and extract the repo 12 | 2. Open a terminal window in the folder where you extracted the repo 13 | 3. Run `npm i` to install all the required dependencies 14 | 15 | ### Usage 16 | 17 | 1. Open a terminal window in the folder where you extracted the repo 18 | 2. Run `node index.js` 19 | 3. Select the platform you'd like to download from 20 | 4. Open [bSmart](https://my.bsmart.it) or [digibook24](https://my.digibook24.com/) in your browser, then open the dev tools (F12) and go to the storage(Firefox) or application(Chromium) tab, there click on `Cookie`, then `https://my.bsmart.it` (or `https://my.digibook24.com/`), then copy in the terminal the cookie called `_bsw_session_v1_production` (without any quotation marks) 21 | 5. Input the id of the book you'd like to download, either from the list or from the url, after `/books/`. It's ususally a 4 digit number 22 | 6. Press enter and the script will start working, a file will be saved in the same folder as the one with the `index.js` with the name of the book, containing the full book downloaded. 23 | 24 | NOTE: some times this doesn't work flawlessly and/or the script crashes saying that `_this.catalog.Pages is not a function` , I've tryed to fix all the issues I've had but in some cases you might need to do a manual download and merge, for this please download [pdftk](https://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/) and run `node index.js --pdftk` 25 | 26 | Further options are available, run `node index.js --help` for more info. 27 | 28 | Enjoy 29 | 30 | Remember that you are responsible for what you are doing on the internet and even tho this script exists it might not be legal in your country to create personal backups of books. 31 | 32 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 33 | 34 | MIT licence 35 | 36 | I may or may not update it depending on my needs tho I'm open to pullup requests ecc. 37 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const prompt = require('prompt-sync')({sigint: true}); 2 | const fetch = require('node-fetch'); 3 | const msgpack = require('msgpack-lite'); 4 | const aesjs = require('aes-js'); 5 | const PDFDocument = require('pdf-lib').PDFDocument; 6 | const fs = require('fs'); 7 | const sanitize = require("sanitize-filename"); 8 | const yargs = require('yargs/yargs'); 9 | const md5 = require('md5'); 10 | const { spawn } = require('child_process'); 11 | const path = require('path'); 12 | 13 | const argv = yargs(process.argv.slice(2)) 14 | .option('site', { 15 | describe: 'The site to download from, currently either bsmart or digibook24', 16 | type: 'string', 17 | default: null 18 | }) 19 | .option('siteUrl', { 20 | describe: 'This overwrites the base url for the site, useful in case a new platform is added', 21 | type: 'string', 22 | default: null 23 | }) 24 | .option('cookie', { 25 | describe: 'Input "_bsw_session_v1_production" cookie', 26 | type: 'string', 27 | default: null 28 | }) 29 | .option('bookId', { 30 | describe: 'Book id', 31 | type: 'string', 32 | default: null 33 | }) 34 | .option('downloadOnly', { 35 | describe: 'Downloads the pages as individual pdfs and will provide a command that can be used to merge them with pdftk', 36 | type: 'boolean', 37 | default: false 38 | }) 39 | .option('pdftk', { 40 | describe: 'Downloads the pages as individual pdfs and merges them with pdftk', 41 | type: 'boolean', 42 | default: false 43 | }) 44 | .option('pdftkPath', { 45 | describe: 'Path to pdftk executable', 46 | type: 'string', 47 | default: 'pdftk' 48 | }) 49 | .option('checkMd5', { 50 | describe: 'Checks the md5 hash of the downloaded pages', 51 | type: 'boolean', 52 | default: false 53 | }) 54 | .option('output', { 55 | describe: 'Output filename', 56 | type: 'string', 57 | default: null 58 | }) 59 | .option('resources', { 60 | describe: 'Download resources of the book instrad of the book it self', 61 | type: 'boolean', 62 | default: false 63 | }) 64 | .help() 65 | .argv; 66 | 67 | 68 | let key = null; 69 | 70 | async function decryptFile(file) { 71 | 72 | return new Promise(async (resolve, reject) => { 73 | try { 74 | let header = msgpack.decode(file.slice(0, 256)); 75 | 76 | let firstPart = file.slice(256, header.start); 77 | let secondPart = new Uint8Array(file.slice(header.start)); 78 | 79 | var aesCbc = new aesjs.ModeOfOperation.cbc(key, firstPart.slice(0, 16)); 80 | var decryptedFirstPart = aesCbc.decrypt(firstPart.slice(16)); 81 | 82 | for(let i=16;i>0;i--){ 83 | if (decryptedFirstPart.slice(decryptedFirstPart.length-i).every(e=>e==i)) { 84 | decryptedFirstPart = decryptedFirstPart.slice(0, decryptedFirstPart.length-i); 85 | break; 86 | } 87 | } 88 | 89 | let result = new Uint8Array(decryptedFirstPart.length + secondPart.length); 90 | result.set(decryptedFirstPart); 91 | result.set(secondPart, decryptedFirstPart.length); 92 | resolve(result); 93 | } catch (e) { 94 | reject({e, file}) 95 | } 96 | 97 | }); 98 | } 99 | 100 | async function fetchEncryptionKey() { 101 | let page = await fetch('https://my.bsmart.it/'); 102 | let text = await page.text(); 103 | let script = text.match(/