updates to README and some minor code changes

+ Use proper exit code of 1 on errors
+ Added error handling for subscription based course info extraction failure
+ Slightly more verbose login failure message
+ Updates to README to improve clarification among other things
This commit is contained in:
Puyodead1 2021-11-11 23:14:51 -05:00
parent a06ef516ad
commit 97ca2cf401
2 changed files with 63 additions and 37 deletions

View File

@ -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).<br>
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/<course name>/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 <Course URL> --concurrent-downloads 20`
- `python main.py -c <Course URL> -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!

50
main.py
View File

@ -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):