Skip to content

Instantly share code, notes, and snippets.

@101arrowz
Last active April 21, 2024 18:07
Show Gist options
  • Save 101arrowz/88156556326106a6ccd58ecb4526498c to your computer and use it in GitHub Desktop.
Save 101arrowz/88156556326106a6ccd58ecb4526498c 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','https://gist.githubusercontent.com/101arrowz/88156556326106a6ccd58ecb4526498c/raw/script.js');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

// Optimized code for the downloader.
!function(){var t=function(t){var e=Object.prototype,r=e.hasOwnProperty,n="function"==typeof Symbol?Symbol:{},o=n.iterator||"@@iterator",i=n.asyncIterator||"@@asyncIterator",a=n.toStringTag||"@@toStringTag";function c(t,e,r,n){var o=e&&e.prototype instanceof u?e:u,i=Object.create(o.prototype),a=new L(n||[]);return i._invoke=function(t,e,r){var n="suspendedStart";return function(o,i){if("executing"===n)throw new Error("Generator is already running");if("completed"===n){if("throw"===o)throw i;return k()}for(r.method=o,r.arg=i;;){var a=r.delegate;if(a){var c=w(a,r);if(c){if(c===l)continue;return c}}if("next"===r.method)r.sent=r._sent=r.arg;else if("throw"===r.method){if("suspendedStart"===n)throw n="completed",r.arg;r.dispatchException(r.arg)}else"return"===r.method&&r.abrupt("return",r.arg);n="executing";var u=s(t,e,r);if("normal"===u.type){if(n=r.done?"completed":"suspendedYield",u.arg===l)continue;return{value:u.arg,done:r.done}}"throw"===u.type&&(n="completed",r.method="throw",r.arg=u.arg)}}}(t,r,a),i}function s(t,e,r){try{return{type:"normal",arg:t.call(e,r)}}catch(t){return{type:"throw",arg:t}}}t.wrap=c;var l={};function u(){}function h(){}function d(){}var f={};f[o]=function(){return this};var p=Object.getPrototypeOf,v=p&&p(p(E([])));v&&v!==e&&r.call(v,o)&&(f=v);var y=d.prototype=u.prototype=Object.create(f);function m(t){["next","throw","return"].forEach((function(e){t[e]=function(t){return this._invoke(e,t)}}))}function g(t,e){var n;this._invoke=function(o,i){function a(){return new e((function(n,a){!function n(o,i,a,c){var l=s(t[o],t,i);if("throw"!==l.type){var u=l.arg,h=u.value;return h&&"object"==typeof h&&r.call(h,"__await")?e.resolve(h.__await).then((function(t){n("next",t,a,c)}),(function(t){n("throw",t,a,c)})):e.resolve(h).then((function(t){u.value=t,a(u)}),(function(t){return n("throw",t,a,c)}))}c(l.arg)}(o,i,n,a)}))}return n=n?n.then(a,a):a()}}function w(t,e){var r=t.iterator[e.method];if(void 0===r){if(e.delegate=null,"throw"===e.method){if(t.iterator.return&&(e.method="return",e.arg=void 0,w(t,e),"throw"===e.method))return l;e.method="throw",e.arg=new TypeError("The iterator does not provide a 'throw' method")}return l}var n=s(r,t.iterator,e.arg);if("throw"===n.type)return e.method="throw",e.arg=n.arg,e.delegate=null,l;var o=n.arg;return o?o.done?(e[t.resultName]=o.value,e.next=t.nextLoc,"return"!==e.method&&(e.method="next",e.arg=void 0),e.delegate=null,l):o:(e.method="throw",e.arg=new TypeError("iterator result is not an object"),e.delegate=null,l)}function x(t){var e={tryLoc:t[0]};1 in t&&(e.catchLoc=t[1]),2 in t&&(e.finallyLoc=t[2],e.afterLoc=t[3]),this.tryEntries.push(e)}function b(t){var e=t.completion||{};e.type="normal",delete e.arg,t.completion=e}function L(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(x,this),this.reset(!0)}function E(t){if(t){var e=t[o];if(e)return e.call(t);if("function"==typeof t.next)return t;if(!isNaN(t.length)){var n=-1,i=function e(){for(;++n<t.length;)if(r.call(t,n))return e.value=t[n],e.done=!1,e;return e.value=void 0,e.done=!0,e};return i.next=i}}return{next:k}}function k(){return{value:void 0,done:!0}}return h.prototype=y.constructor=d,d.constructor=h,d[a]=h.displayName="GeneratorFunction",t.isGeneratorFunction=function(t){var e="function"==typeof t&&t.constructor;return!!e&&(e===h||"GeneratorFunction"===(e.displayName||e.name))},t.mark=function(t){return Object.setPrototypeOf?Object.setPrototypeOf(t,d):(t.__proto__=d,a in t||(t[a]="GeneratorFunction")),t.prototype=Object.create(y),t},t.awrap=function(t){return{__await:t}},m(g.prototype),g.prototype[i]=function(){return this},t.AsyncIterator=g,t.async=function(e,r,n,o,i){void 0===i&&(i=Promise);var a=new g(c(e,r,n,o),i);return t.isGeneratorFunction(r)?a:a.next().then((function(t){return t.done?t.value:a.next()}))},m(y),y[a]="Generator",y[o]=function(){return this},y.toString=function(){return"[object Generator]"},t.keys=function(t){var e=[];for(var r in t)e.push(r);return e.reverse(),function r(){for(;e.length;){var n=e.pop();if(n in t)return r.value=n,r.done=!1,r}return r.done=!0,r}},t.values=E,L.prototype={constructor:L,reset:function(t){if(this.prev=0,this.next=0,this.sent=this._sent=void 0,this.done=!1,this.delegate=null,this.method="next",this.arg=void 0,this.tryEntries.forEach(b),!t)for(var e in this)"t"===e.charAt(0)&&r.call(this,e)&&!isNaN(+e.slice(1))&&(this[e]=void 0)},stop:function(){this.done=!0;var t=this.tryEntries[0].completion;if("throw"===t.type)throw t.arg;return this.rval},dispatchException:function(t){if(this.done)throw t;var e=this;function n(r,n){return a.type="throw",a.arg=t,e.next=r,n&&(e.method="next",e.arg=void 0),!!n}for(var o=this.tryEntries.length-1;o>=0;--o){var i=this.tryEntries[o],a=i.completion;if("root"===i.tryLoc)return n("end");if(i.tryLoc<=this.prev){var c=r.call(i,"catchLoc"),s=r.call(i,"finallyLoc");if(c&&s){if(this.prev<i.catchLoc)return n(i.catchLoc,!0);if(this.prev<i.finallyLoc)return n(i.finallyLoc)}else if(c){if(this.prev<i.catchLoc)return n(i.catchLoc,!0)}else{if(!s)throw new Error("try statement without catch or finally");if(this.prev<i.finallyLoc)return n(i.finallyLoc)}}}},abrupt:function(t,e){for(var n=this.tryEntries.length-1;n>=0;--n){var o=this.tryEntries[n];if(o.tryLoc<=this.prev&&r.call(o,"finallyLoc")&&this.prev<o.finallyLoc){var i=o;break}}i&&("break"===t||"continue"===t)&&i.tryLoc<=e&&e<=i.finallyLoc&&(i=null);var a=i?i.completion:{};return a.type=t,a.arg=e,i?(this.method="next",this.next=i.finallyLoc,l):this.complete(a)},complete:function(t,e){if("throw"===t.type)throw t.arg;return"break"===t.type||"continue"===t.type?this.next=t.arg:"return"===t.type?(this.rval=this.arg=t.arg,this.method="return",this.next="end"):"normal"===t.type&&e&&(this.next=e),l},finish:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.finallyLoc===t)return this.complete(r.completion,r.afterLoc),b(r),l}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.tryLoc===t){var n=r.completion;if("throw"===n.type){var o=n.arg;b(r)}return o}}throw new Error("illegal catch attempt")},delegateYield:function(t,e,r){return this.delegate={iterator:E(t),resultName:e,nextLoc:r},"next"===this.method&&(this.arg=void 0),l}},t}({});try{regeneratorRuntime=t}catch(e){Function("r","regeneratorRuntime = r")(t)}function e(t,e,r,n,o,i,a){try{var c=t[i](a),s=c.value}catch(t){return void r(t)}c.done?e(s):Promise.resolve(s).then(n,o)}self.fetch||(self.fetch=function(t,e){return e=e||{},new Promise((r,n)=>{const o=new XMLHttpRequest,i=[],a=[],c={},s=()=>({ok:2==(o.status/100|0),statusText:o.statusText,status:o.status,url:o.responseURL,text:()=>Promise.resolve(o.responseText),json:()=>Promise.resolve(JSON.parse(o.responseText)),blob:()=>Promise.resolve(new Blob([o.response])),clone:s,headers:{keys:()=>i,entries:()=>a,get:t=>c[t.toLowerCase()],has:t=>t.toLowerCase()in c}});o.open(e.method||"get",t,!0),o.onload=()=>{o.getAllResponseHeaders().replace(/^(.*?):[^\S\n]*([\s\S]*?)$/gm,(t,e,r)=>{i.push(e=e.toLowerCase()),a.push([e,r]),c[e]=c[e]?`${c[e]},${r}`:r}),r(s())},o.onerror=n,o.withCredentials="include"==e.credentials;for(const t in e.headers)o.setRequestHeader(t,e.headers[t]);o.send(e.body||null)})});var r=document.createElement("script");r.src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.5/jszip.min.js";var n=document.createElement("div");n.style.background="white",n.style.position="fixed",n.style.width="100vw",n.style.height="100vh",n.style.top=0,n.style.left=0,n.style.zIndex=1e6;var o=document.createTextNode("");n.appendChild(o),document.body.style.marginBottom=0,document.body.appendChild(n);var i=function(){var t;n.removeChild(n.lastChild);for(var e=arguments.length,r=new Array(e),o=0;o<e;o++)r[o]=arguments[o];(t=console).log.apply(t,r);var i=document.createElement("div");i.textContent=r.join(" "),n.appendChild(i)},a=function(){var t;n.removeChild(n.lastChild);for(var e=arguments.length,r=new Array(e),i=0;i<e;i++)r[i]=arguments[i];(t=console).error.apply(t,r);var a=document.createElement("div");a.style.color="red",a.textContent=r.join(" "),n.appendChild(a),n.appendChild(o)},c=function(){var t,r=(t=regeneratorRuntime.mark((function t(){var e,r,c,s,l,u,h,d,f,p,v,y,m,g,w,x,b,L;return regeneratorRuntime.wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return i("This is the McGraw-Hill Education Textbook Downloader. Once the download completes, refresh the page to go back to the original site. Starting file download..."),n.appendChild(o),t.next=4,fetch("https://player-api.mheducation.com/lti",{credentials:"include"});case 4:return t.next=6,t.sent.json();case 6:return e=t.sent.custom_epub_url,r=new JSZip,c=r.folder("META-INF"),t.t0=c,t.next=12,fetch(e+"META-INF/container.xml",{credentials:"include"});case 12:return t.next=14,t.sent.text();case 14:return t.t1=t.sent,t.t0.file.call(t.t0,"container.xml",t.t1),s=r.folder("OPS"),t.next=19,fetch(e+"OPS/content.opf",{credentials:"include"});case 19:return t.next=21,t.sent.text();case 21:l=t.sent,s.file("content.opf",l),u=(new DOMParser).parseFromString(l,"application/xml"),h=u.querySelector("manifest").children,d=h.length,f=0;case 27:if(!(f<d)){t.next=46;break}return p=h.item(f),v=p.getAttribute("href"),t.prev=30,t.next=33,fetch("".concat(e,"OPS/").concat(v),{credentials:"include"});case 33:return t.next=35,t.sent.arrayBuffer();case 35:y=t.sent,s.file(v,y),i("Finished downloading",v,"(".concat(f," of ").concat(d,")")),t.next=43;break;case 40:t.prev=40,t.t2=t.catch(30),a("Failed to download ".concat(v,": ").concat(t.t2));case 43:f++,t.next=27;break;case 46:return i("Finished downloading data! Starting compression..."),n.appendChild(o),window.__savedTextbook=r,m=0,t.next=52,r.generateInternalStream({type:"blob",compression:"STORE"}).accumulate((function(t){var e=t.percent,r=Math.floor(e);r>m&&(i(r+"% complete"),m=r)}));case 52:g=t.sent,i("Finished compressing textbook! Starting download..."),n.appendChild(o),window.__savedTextbookEpub=g,w=URL.createObjectURL(g),(x=document.createElement("a")).href=w,b=u.querySelector("metadata title"),L=b?b.innerHTML:"textbook",x.download=L+".epub",x.click(),URL.revokeObjectURL(w);case 64:case"end":return t.stop()}}),t,null,[[30,40]])})),function(){var r=this,n=arguments;return new Promise((function(o,i){var a=t.apply(r,n);function c(t){e(a,o,i,c,s,"next",t)}function s(t){e(a,o,i,c,s,"throw",t)}c(void 0)}))});return function(){return r.apply(this,arguments)}}();document.head.appendChild(r),r.onload=c}();
// 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.');
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');
metaInf.file('container.xml', await (await fetch(IMPORT_URL + 'META-INF/container.xml', { credentials: 'include' })).text());
const epubData = epub.folder('OPS');
const opfString = await (await fetch(IMPORT_URL + 'OPS/content.opf', { credentials: 'include' })).text();
epubData.file('content.opf', opfString);
const opf = new DOMParser().parseFromString(opfString, 'application/xml');
for (let item of opf.querySelector('manifest').children) {
const href = item.getAttribute('href');
try {
const data = await (await fetch(`${IMPORT_URL}OPS/${href}`, { credentials: 'include' })).arrayBuffer();
epubData.file(href, data);
console.log('Finished downloading', href);
} catch(e) {
throw `Failed to download ${href}: ${e}`;
}
}
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;
const 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));
@101arrowz
Copy link
Author

@moehaid91 Sorry, you're using a different platform than what this was tested on so I can't really say why it isn't working.

Also, the web viewer is itself using an epub reader so in theory you can recreate the exact same appearance. In practice it doesn't line up perfectly but Calibre gives decent results.

@fir3-1ce
Copy link

Also, the web viewer is itself using an epub reader so in theory you can recreate the exact same appearance

I think there must be some other variables at play. Certain text in the epub I downloaded is meshed together and overlapping with one another. Also the page breaks happen at random whereas in the web viewer it's exactly every 2 pages (2 pages in the book = 1 page in the viewer). Maybe there are some missing UI elements that don't get transferred during the download process.

Either way these companies are greedy and do everything they can to keep young students paying upwards of $150 on top of their already expensive class. Even with the downloaded epub I still had to purchase the book to take tests

@x0thrmc-exe
Copy link

Certain text in the epub I downloaded is meshed together and overlapping with one another. Also the page breaks happen at random whereas in the web viewer it's exactly every 2 pages

I use Calibre for viewing to epub. Somewhere, there is a viewer that can duplicate the UX of the web viewer, but as of yet that has not been identified. It does not display the way a normal pdf or html document displays, as it is based on the absolute size of the document regardless of your screen size. Adjust the text size until there is no overlapping as you describe, there is usually a sweet spot value. Also adjust the window size until it looks right. If you are viewing on a really small laptop screen, it might have a really poor viewing experience because you can't zoom and resize normally. A high DPI monitor is much better.

@x0thrmc-exe
Copy link

Has anyone found a viewer that works well for these epubs on Android/Apple tablets? Calibre works great on PC/Mac/Linux but there is no iOS /Android mobile version. The specific Epubs from McGrawhill won't open in the stock Apple e-reader apps either. Converting to PDF results in really weird rendering issues on the exported document.

@lannimade
Copy link

Hi! I am trying to download an eBook for my students to have access to at home that don't have wifi. I am getting this error. Any ideas?
image

@101arrowz
Copy link
Author

Could you send a screenshot of your full page? Without more context I can't say what's causing that error - it could even be that you're using a different URL for the viewer than I designed this script for.

@lannimade
Copy link

Oh, maybe that's it! Since I'm a teacher, and it's not high school/college, it might be different.

Screen Shot 2023-07-07 at 10 20 20 PM

@101arrowz
Copy link
Author

Yeah, that URL isn't the same that I used when developing the script, so there's probably some incompatibility there that's causing this issue. I unfortunately won't be able to fix it because I no longer have access to any MHE ebooks. Still, any students of yours who have purchased the ebook should be able to use it for themselves.

@Me-Mr
Copy link

Me-Mr commented Aug 6, 2023

Hi, thanks for your script! I just tried using it, and am getting this error. I'd appreciate any help. It says download completed, but never gives me a file.
Screenshot 2023-08-06 at 12 11 18 PM

@101arrowz
Copy link
Author

Looks like you don't have enough memory - I might try to fix this to use way less memory at some point, but for now just try running on a more powerful computer.

@Me-Mr
Copy link

Me-Mr commented Aug 7, 2023

Hi, I'm running it on apple silicon (16gb) , could that be why?

thanks for your hrlp

@ATC1st
Copy link

ATC1st commented Aug 20, 2023

Is there supposed to be an indication that it is downloading somewhere. I just see "Starting file download" but don't see it downloading anywhere.

@samlk11
Copy link

samlk11 commented Aug 26, 2023

Hi, thanks for your script! I just tried using it, and am getting this error. I'd appreciate any help. It says download completed, but never gives me a file. Screenshot 2023-08-06 at 12 11 18 PM

I am having a very similar issue, also on a Mac that uses Apple Silicon. I have 32gb of RAM, though. Hope this helps!

Screenshot 2023-08-25 at 9 24 25 PM

@chloecogswell
Copy link

this worked on microsoft edge! thanks a bunch

@Thundersauce101
Copy link

Thanks for the script, very helpful. Did you ever have any success downloading and renaming the videos to their associated titles in the Media Bank?

Did some digging and here's an example for one video:
Title: COV LO 01-A2 Part 1 Financial Statement Analysis
Download URL: https://connectprod-vfs.cdn-ec.viddler.com/connectprod_2atf5g8o22rmoh1h4ju6_800.mp4?fd9f2a1c14aadf1069f046ce60f41e2b4ff6e79f2d14a10dd7664f544f1fec9f07464a5ac1226061f3888a736ccb0f5d56a756cb345a8a6ec5e98ea0900f61f569e00d023b61f78fb1c6d5e56ae86224d5030f&ec_rate=800&ec_prebuf=60
File name when downloaded: connectprod_636617.mp4

image

At 574 videos for just one of my classes, I don't want to spend time downloading and watching them. Not the greatest user experience; you also can't view a list of all the videos on one page. Any help would be appreciated. Being able to make a playlist to review would be great.

@x0thrmc-exe
Copy link

Thanks for the script, very helpful. Did you ever have any success downloading and renaming the videos to their associated titles in the Media Bank?

I never found a way to mass download any of the material from the media bank. I used extensions like CocoCut to download the videos but need to do it one by one.

@x0thrmc-exe
Copy link

Has anyone found a viewer that works well for these epubs on Android/Apple tablets? Calibre works great on PC/Mac/Linux but there is no iOS /Android mobile version. The specific Epubs from McGrawhill won't open in the stock Apple e-reader apps either. Converting to PDF results in really weird rendering issues on the exported document.

Just to follow up on my own inquiry: I downloaded nearly every popular ebook reader in the Google Play store. Most, like the big ones like Kindle Reader did not even open the book. The one I am using daily now is Lithium, which opens and renders the books correctly, but you cannot zoom.

A second option is the eBook reader included with the legacy versions of MIXplorer. This was does let you zoom, but it does not show the table of contents, so it was less convenient for me. Current versions don't work so you need to find an archived APK.

I probably tried the top 10 Android eBook readers and all the others were worthless.

@aakashthakkar
Copy link

aakashthakkar commented Sep 8, 2023

Screenshot 2023-09-07 at 7 26 08 PM

Tried in edge too. Did not work for me. Nothing happens in the network tab too. :(

@Slidice3627
Copy link

I'm not a pro, but I figured out that we need to override the current address bar from the textbook page with "javascript:......", it worked!! Just wanna share this with people who are not familiar with how Java works :)

@LuckyF33
Copy link

Worked perfectly, I actually ran it on safari. Was my first time running a javascript on a webpage actually, I've only recently been getting into code and AI related stuff so it felt very nice having it work on my first try :3 Thanks for providing this, it's gonna help me a ton at school

@ize69
Copy link

ize69 commented Oct 3, 2023

Run the optimized version to have better luck

@101arrowz
Copy link
Author

The instructions should do exactly the same thing as script.js - actually, the only thing the instructions do is run it - but yes, you can also paste script.js into the developer console to run the program.

@chunbiggus
Copy link

Capture

Hey, I keep getting this issue repeatedly when attempting to download my textbook. I bought lifetime access to it through McGraw-Hill. I've tried running the script on multiple browsers and it either gives this error or another one stating that it can't create a "blob". I have 32GB of RAM and all of my storage drives have TB worth of space. Any help is appreciated cuz I'm desperate to get this textbook. Thanks in advance

@101arrowz
Copy link
Author

If your book is over 2GB in total size the script may fail; I may not be able to fix that anymore as I no longer have access to any McGraw-Hill ebooks, but I'll check if it's possible to do anyway.

@nutterbutter232
Copy link

Web capture_10-12-2023_23635_prod reader-ui prod mheducation com

Worked on Edge, thank you!

@katrina28mariz
Copy link

Thank you so much for providing this! I was able to successfully download a textbook from mcgraw hill. But I was wondering does anyone know if there's something similar to the macmillan textbook. i have access to both achieve.macmillan and macmillan.vitalsource. Please let me know if you need more information to help me with this. Thank you in advance :)

@anarigit
Copy link

Worked like a charm, thank you!

@Kish242
Copy link

Kish242 commented Jan 13, 2024

OMG it worked! Awesome! Is there one for Wileyplus?

@shees1993
Copy link

Wow! Thank God! Worked the first time itself and like a charm! Unlike so many useless Python scripts that are out there for almost about everything! Thank you so, so, so much! :)

@W1zarDddD
Copy link

W1zarDddD commented Mar 22, 2024

Frankly, I don't find McGraw Hill Education's e-textbooks particularly interesting. They may contain useful information, but they are usually presented in a rather boring format. Although it’s hard for me to judge, I like regular books more and get so carried away when I read that I use assignment writing services for university, I found https://ca.edubirdie.com/assignment-writing-services for this. In general, I prefer to learn through more interactive and engaging methods, such as educational videos. So for me, the electronic option is not a priority.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment