diff --git a/README.md b/README.md index 1c8043b..a72ecae 100644 --- a/README.md +++ b/README.md @@ -13,22 +13,21 @@ - **This tool will not work without decryption keys, and there currently no public way to get those keys. Do not bother installing unless you already have keys!** - This program is WIP, the code is provided as-is and I am not held resposible for any legal issues resulting from the use of this program. -# Support - -if you want help using the program, join [my discord server](https://discord.gg/5B3XVb4RRX) or use [github issues](https://github.com/Puyodead1/udemy-downloader/issues) - -# License - -All code is licensed under the MIT license - # Description Utility script to download Udemy courses, has support for DRM videos but requires the user to aquire the decryption key (for legal reasons).
-Windows is the primary development OS, but I've made an effort to support Linux also. +Windows is the primary development OS, but I've made an effort to support Linux also (Mac untested). # Requirements -1. You will need to download `ffmpeg`, `aria2c`, `mp4decrypt` (from Bento4 SDK) and `yt-dlp` (`pip install yt-dlp`). Ensure they are in the system path (typing their name in cmd should invoke them). +The following are a list of required third-party tools, you will need to ensure they are in your systems path and that typing their name in a terminal invokes them. + +_**Note**:_ _These are seperate requirements that are not installed with the pip command! You will need to download and install these manually!_ + +- [ffmpeg](https://www.ffmpeg.org/) - This tool is available in Linux package repositories +- [aria2/aria2c](https://github.com/aria2/aria2/) - This tool is available in Linux package repositories +- [mp4decrypt](https://www.bento4.com/) +- [yt-dlp](https://github.com/yt-dlp/yt-dlp/) - This tool is available in Linux package repositories, but can also be installed using pip if desired (`pip install yt-dlp`) # Usage @@ -40,27 +39,28 @@ You will need to get a few things before you can use this program: - Decryption Key - Udemy Course URL - Udemy Bearer Token (aka acccess token for udemy-dl users) +- Udemy cookies (only required for subscription plans - see [Udemy Subscription Plans](#udemy-subscription-plans)) -### Setting up +## Setting up - rename `.env.sample` to `.env` _(you only need to do this if you plan to use the .env file to store your bearer token)_ - rename `keyfile.example.json` to `keyfile.json` -### Aquire Bearer Token +## Aquire Bearer Token - Firefox: [Udemy-DL Guide](https://github.com/r0oth3x49/udemy-dl/issues/389#issuecomment-491903900) - Chrome: [Udemy-DL Guide](https://github.com/r0oth3x49/udemy-dl/issues/389#issuecomment-492569372) - If you want to use the .env file to store your Bearer Token, edit the .env and add your token. -### Key ID and Key +## Key ID and Key -It is up to you to aquire the key and key ID. Please **DO NOT** ask me for help acquiring these, decrypting DRM protected content can be considered piracy. +It is up to you to aquire the key and key ID. Please **DO NOT** ask me for help acquiring these, decrypting DRM protected content can be considered piracy. The tool required for this has already been discused in a GitHub issue. - Enter the key and key id in the `keyfile.json` - ![keyfile example](https://i.imgur.com/e5aU0ng.png) - ![example key and kid from console](https://i.imgur.com/awgndZA.png) -### Start Downloading +## Start Downloading You can now run the program, see the examples below. The course will download to `out_dir`. @@ -68,6 +68,8 @@ You can now run the program, see the examples below. The course will download to To download a course included in a subscription plan that you did not purchase individually, you will need to follow a few more steps to get setup. +_**NOTE**:_ _You do **NOT** need to follow this section if you don't have a **Udemy Pro** or **Udemy Personal** subscription plan! This section is not for individually purchased courses._ + ## Getting your cookies - Go to the page of the course you want to download @@ -82,6 +84,10 @@ To download a course included in a subscription plan that you did not purchase i - Paste the cookie into the file - save and close the file +You will also need to ensure the link is in the following format: `https://www.udemy.com/course//learn/`. + +Note the link is `/course` not `/program-taking`. It is also important that the link has `/learn`, otherwise you will see an error when trying to fetch the course information. + # Advanced Usage ``` @@ -142,6 +148,10 @@ optional arguments: - `python main.py -c --concurrent-downloads 20` - `python main.py -c -cd 20` +# Support + +if you want help using the program, join my [Discord](https://discord.gg/5B3XVb4RRX) server or use [GitHub Issues](https://github.com/Puyodead1/udemy-downloader/issues) + # Credits - https://github.com/Jayapraveen/Drm-Dash-stream-downloader - For the original code which this is based on @@ -149,3 +159,13 @@ optional arguments: - https://github.com/alastairmccormack/pymp4parse - For code related to mp4 box parsing (used by pywvpssh) - https://github.com/lbrayner/vtt-to-srt - For code related to converting subtitles from vtt to srt format - https://github.com/r0oth3x49/udemy-dl - For some of the informaton related to using the udemy api + +## License + +All code is licensed under the MIT license + +## and finally, donations! + +Woo, you made it this far! + +I spend a lot of time coding things, and almost all of them are for nothing in return. When theres a lot of use of a program I make, I try to keep it updated, fix bugs, and even implement new features! But after a while, I do run out of motivation to keep doing it. If you like my work, and can help me out even a little, it would really help me out. If you are interested, you can find all the available options [here](https://github.com/Puyodead1/#supporting-me). Even if you don't, thank you anyways! diff --git a/main.py b/main.py index c18c269..a9b1ab8 100644 --- a/main.py +++ b/main.py @@ -92,7 +92,7 @@ class Udemy: }) print("Login Success") else: - print("Login Failure!") + print("Login Failure! You are probably missing an access token!") sys.exit(1) def _extract_supplementary_assets(self, supp_assets): @@ -400,11 +400,11 @@ class Udemy: except conn_error as error: print(f"Udemy Says: Connection error, {error}") time.sleep(0.8) - sys.exit(0) + sys.exit(1) except (ValueError, Exception) as error: print(f"Udemy Says: {error} on {url}") time.sleep(0.8) - sys.exit(0) + sys.exit(1) else: results = webpage.get("results", []) return results @@ -418,7 +418,7 @@ class Udemy: except conn_error as error: print(f"Udemy Says: Connection error, {error}") time.sleep(0.8) - sys.exit(0) + sys.exit(1) else: return resp @@ -437,7 +437,7 @@ class Udemy: except conn_error as error: print(f"Udemy Says: Connection error, {error}") time.sleep(0.8) - sys.exit(0) + sys.exit(1) except (ValueError, Exception): resp = self._extract_large_course_content(url=url) return resp @@ -451,7 +451,7 @@ class Udemy: except conn_error as error: print(f"Udemy Says: Connection error, {error}") time.sleep(0.8) - sys.exit(0) + sys.exit(1) else: _next = data.get("next") while _next: @@ -461,7 +461,7 @@ class Udemy: except conn_error as error: print(f"Udemy Says: Connection error, {error}") time.sleep(0.8) - sys.exit(0) + sys.exit(1) else: _next = resp.get("next") results = resp.get("results") @@ -489,11 +489,11 @@ class Udemy: except conn_error as error: print(f"Udemy Says: Connection error, {error}") time.sleep(0.8) - sys.exit(0) + sys.exit(1) except (ValueError, Exception) as error: print(f"Udemy Says: {error}") time.sleep(0.8) - sys.exit(0) + sys.exit(1) else: results = webpage.get("results", []) return results @@ -506,11 +506,11 @@ class Udemy: except conn_error as error: print(f"Udemy Says: Connection error, {error}") time.sleep(0.8) - sys.exit(0) + sys.exit(1) except (ValueError, Exception) as error: print(f"Udemy Says: {error}") time.sleep(0.8) - sys.exit(0) + sys.exit(1) else: results = webpage.get("results", []) if results: @@ -529,11 +529,11 @@ class Udemy: except conn_error as error: print(f"Udemy Says: Connection error, {error}") time.sleep(0.8) - sys.exit(0) + sys.exit(1) except (ValueError, Exception) as error: print(f"Udemy Says: {error}") time.sleep(0.8) - sys.exit(0) + sys.exit(1) else: results = webpage.get("results", []) return results @@ -546,11 +546,11 @@ class Udemy: except conn_error as error: print(f"Udemy Says: Connection error, {error}") time.sleep(0.8) - sys.exit(0) + sys.exit(1) except (ValueError, Exception) as error: print(f"Udemy Says: {error}") time.sleep(0.8) - sys.exit(0) + sys.exit(1) else: results = webpage.get("results", []) return results @@ -563,11 +563,11 @@ class Udemy: except conn_error as error: print(f"Udemy Says: Connection error, {error}") time.sleep(0.8) - sys.exit(0) + sys.exit(1) except (ValueError, Exception) as error: print(f"Udemy Says: {error}") time.sleep(0.8) - sys.exit(0) + sys.exit(1) else: results = webpage.get("results", []) if results: @@ -586,11 +586,11 @@ class Udemy: except conn_error as error: print(f"Udemy Says: Connection error, {error}") time.sleep(0.8) - sys.exit(0) + sys.exit(1) except (ValueError, Exception) as error: print(f"Udemy Says: {error}") time.sleep(0.8) - sys.exit(0) + sys.exit(1) else: results = webpage.get("results", []) return results @@ -619,8 +619,14 @@ class Udemy: if not course: course_html = self.session._get(url).text soup = BeautifulSoup(course_html, "lxml") - data_args = soup.find( - "div", {"class": "ud-component--course-taking--app"}).attrs["data-module-args"] + data = soup.find( + "div", {"class": "ud-component--course-taking--app"}) + if not data: + print( + "Unable to extract arguments from course page! Make sure you have a cookies.txt file!") + self.session.terminate() + sys.exit(1) + data_args = data.attrs["data-module-args"] data_json = json.loads(data_args) course_id = data_json.get("courseId", None) portal_name = self.extract_portal_name(url) @@ -638,7 +644,7 @@ class Udemy: print("Trying to logout now...", ) self.session.terminate() print("Logged out successfully.", ) - sys.exit(0) + sys.exit(1) class Session(object):