Skip to content

Instantly share code, notes, and snippets.

@6801318d8d
Forked from 101arrowz/README.md
Last active January 11, 2025 15:14
Show Gist options
  • Save 6801318d8d/35ef4e81e7d291e9c19fdfb1f5d8aac8 to your computer and use it in GitHub Desktop.
Save 6801318d8d/35ef4e81e7d291e9c19fdfb1f5d8aac8 to your computer and use it in GitHub Desktop.
Download a McGraw Hill Education eTextbook

Download a McGraw Hill Education eTextbook

If you purchase a textbook from McGraw Hill, the website to view it is clunky and only works on some devices. You can't go to specific page numbers, the search is super slow, etc. That's why I wrote this script to download the textbook as an ePub file for your own viewing.

Using this script is 100% legal. McGraw Hill publicly hosts their ebooks online in order for their web client to download it. Moreover, to use it, you must already have purchased the book you would like to download, so it is legally yours to use as you please. However, it IS illegal to use this for piracy purposes. DO NOT DISTRIBUTE ANY TEXTBOOKS YOU DOWNLOAD USING THIS SCRIPT.

Instructions

  1. Open your textbook in the McGraw-Hill Connect website (how you normally open it) in a private/incognito window. Use Chrome if possible; this won't work at all in Firefox.
  2. Type javascript: into the address bar (note that you CANNOT copy-paste it in).
  3. Copy-paste the following into the address bar AFTER the javascript: part:
var x=new XMLHttpRequest();x.onload=function(){eval(x.responseText)};x.open('GET','');x.send();
  1. Press ENTER.
  2. Follow the instructions that appear on screen. Be patient! The download takes between 10 and 40 minutes depending on internet speed.
  3. Your textbook will download on its own.

If you found this tutorial useful, please give it a star. Thanks!

~101arrowz

// Source code for the downloader.
import('https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.5/jszip.min.js').then(async () => {
alert('This is the McGraw-Hill Education Textbook Downloader. Click OK to start downloading the files.');
let url;
const cors_server="0.0.0.0:8080/";
const IMPORT_URL = (await (await fetch('https://player-api.mheducation.com/lti', { credentials: 'include' })).json()).custom_epub_url;
//
const epub = new JSZip();
const metaInf = epub.folder('META-INF');
url = IMPORT_URL + 'META-INF/container.xml';
console.log("Downloading "+url);
containerString = await (await fetch(url, { credentials: 'include' })).text();
console.log("containerString="+containerString);
metaInf.file('container.xml', containerString);
const container = new DOMParser().parseFromString(containerString, 'application/xml');
console.log("Container=\n"+container);
const root_path = container.querySelector('container > rootfiles > rootfile').getAttribute("full-path");
console.log("root_path="+root_path);
const prefix = root_path.split("/")[0]
const epubData = epub.folder(prefix);
url = IMPORT_URL + root_path;
console.log("Downloading "+url);
const opfString = await (await fetch(url, { credentials: 'include' })).text();
epubData.file('content.opf', opfString);
const opf = new DOMParser().parseFromString(opfString, 'application/xml');
// Loop over EPUB files
let headers;
for (let item of opf.querySelector('manifest').children) {
const href = item.getAttribute('href');
try {
url = cors_server + IMPORT_URL + prefix + "/" + href;
console.log("Downloading " + url);
const data = await (await fetch(url, { credentials: 'include' })).arrayBuffer();
epubData.file(href, data);
console.log('Finished downloading', href);
} catch(e) {
throw `Failed to download ${href}: ${e}`;
}
}
// Compression
alert('Finished downloading data! Click OK to start compression.');
window.__savedTextbook = epub;
let highestPercent = 0;
const data = await epub.generateInternalStream({ type: 'blob' }).accumulate(({ percent }) => {
const intPercent = Math.floor(percent);
if (intPercent > highestPercent) {
console.log(intPercent+'% complete');
highestPercent = intPercent
}
});
alert('Finished compressing textbook! Click OK to start download.');
window.__savedTextbookEpub = data;
url = URL.createObjectURL(data);
const tmpLink = document.createElement('a');
tmpLink.href = url;
const possibleTitle = opf.querySelector('metadata title');
const titleString = possibleTitle ? possibleTitle.innerHTML : 'textbook';
tmpLink.download = titleString + '.epub';
tmpLink.click();
URL.revokeObjectURL(url);
}).catch(err => alert('Textbook download failed because an error occurred: '+err));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment