mirror of
https://cdm-project.com/Download-Tools/udemy-downloader.git
synced 2025-05-04 02:04:25 +02:00
Add --disable-ipv6 option
+ Added option to disable ipv6 in aria2 + Updated README to reflect argument changes
This commit is contained in:
parent
97ca2cf401
commit
95b30841dc
31
README.md
31
README.md
@ -91,30 +91,33 @@ Note the link is `/course` not `/program-taking`. It is also important that the
|
|||||||
# Advanced Usage
|
# Advanced Usage
|
||||||
|
|
||||||
```
|
```
|
||||||
usage: main.py [-h] -c COURSE_URL [-b BEARER_TOKEN] [-q QUALITY] [-l LANG] [-cd CONCURRENT_DOWNLOADS] [--skip-lectures] [--download-assets]
|
usage: main.py [-h] -c COURSE_URL [-b BEARER_TOKEN] [-q QUALITY] [-l LANG] [-cd CONCURRENT_DOWNLOADS] [--disable-ipv6]
|
||||||
[--download-captions] [--keep-vtt] [--skip-hls] [--info]
|
[--skip-lectures] [--download-assets] [--download-captions] [--keep-vtt] [--skip-hls] [--info] [-v]
|
||||||
|
|
||||||
Udemy Downloader
|
Udemy Downloader
|
||||||
|
|
||||||
optional arguments:
|
options:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
-c COURSE_URL, --course-url COURSE_URL
|
-c COURSE_URL, --course-url COURSE_URL
|
||||||
The URL of the course to download
|
The URL of the course to download
|
||||||
-b BEARER_TOKEN, --bearer BEARER_TOKEN
|
-b BEARER_TOKEN, --bearer BEARER_TOKEN
|
||||||
The Bearer token to use
|
The Bearer token to use
|
||||||
-q QUALITY, --quality QUALITY
|
-q QUALITY, --quality QUALITY
|
||||||
Download specific video quality. If the requested quality isn't available, the closest quality will be used. If not
|
Download specific video quality. If the requested quality isn't available, the closest quality
|
||||||
specified, the best quality will be downloaded for each lecture
|
will be used. If not specified, the best quality will be downloaded for each lecture
|
||||||
-l LANG, --lang LANG The language to download for captions, specify 'all' to download all captions (Default is 'en')
|
-l LANG, --lang LANG The language to download for captions, specify 'all' to download all captions (Default is
|
||||||
|
'en')
|
||||||
-cd CONCURRENT_DOWNLOADS, --concurrent-downloads CONCURRENT_DOWNLOADS
|
-cd CONCURRENT_DOWNLOADS, --concurrent-downloads CONCURRENT_DOWNLOADS
|
||||||
The number of maximum concurrent downloads for segments (HLS and DASH, must be a number 1-50)
|
The number of maximum concurrent downloads for segments (HLS and DASH, must be a number 1-30)
|
||||||
|
--disable-ipv6 If specified, ipv6 will be disabled in aria2
|
||||||
--skip-lectures If specified, lectures won't be downloaded
|
--skip-lectures If specified, lectures won't be downloaded
|
||||||
--download-assets If specified, lecture assets will be downloaded
|
--download-assets If specified, lecture assets will be downloaded
|
||||||
--download-captions If specified, captions will be downloaded
|
--download-captions If specified, captions will be downloaded
|
||||||
--keep-vtt If specified, .vtt files won't be removed
|
--keep-vtt If specified, .vtt files won't be removed
|
||||||
--skip-hls If specified, hls streams will be skipped (faster fetching) (hls streams usually contain 1080p quality for non-drm
|
--skip-hls If specified, hls streams will be skipped (faster fetching) (hls streams usually contain 1080p
|
||||||
lectures)
|
quality for non-drm lectures)
|
||||||
--info If specified, only course information will be printed, nothing will be downloaded
|
--info If specified, only course information will be printed, nothing will be downloaded
|
||||||
|
-v, --version show program's version number and exit
|
||||||
```
|
```
|
||||||
|
|
||||||
- Passing a Bearer Token and Course ID as an argument
|
- Passing a Bearer Token and Course ID as an argument
|
||||||
@ -148,6 +151,16 @@ optional arguments:
|
|||||||
- `python main.py -c <Course URL> --concurrent-downloads 20`
|
- `python main.py -c <Course URL> --concurrent-downloads 20`
|
||||||
- `python main.py -c <Course URL> -cd 20`
|
- `python main.py -c <Course URL> -cd 20`
|
||||||
|
|
||||||
|
If you encounter errors while downloading such as
|
||||||
|
|
||||||
|
`errorCode=1 Network problem has occurred. cause:Unknown socket error 10051 (0x2743)`
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
`errorCode=1 Network problem has occurred. cause:A socket operation was attempted to an unreachable network.`
|
||||||
|
|
||||||
|
Then try disabling ipv6 in aria2 using the `--disable-ipv6` option
|
||||||
|
|
||||||
# Support
|
# 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)
|
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)
|
||||||
|
62
main.py
62
main.py
@ -9,8 +9,8 @@ import time
|
|||||||
import cloudscraper
|
import cloudscraper
|
||||||
import m3u8
|
import m3u8
|
||||||
import requests
|
import requests
|
||||||
from pathlib import Path
|
|
||||||
import yt_dlp
|
import yt_dlp
|
||||||
|
from pathlib import Path
|
||||||
from html.parser import HTMLParser as compat_HTMLParser
|
from html.parser import HTMLParser as compat_HTMLParser
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from requests.exceptions import ConnectionError as conn_error
|
from requests.exceptions import ConnectionError as conn_error
|
||||||
@ -893,7 +893,7 @@ def decrypt(kid, in_filepath, out_filepath):
|
|||||||
|
|
||||||
|
|
||||||
def handle_segments(url, format_id, video_title,
|
def handle_segments(url, format_id, video_title,
|
||||||
output_path, lecture_file_name, concurrent_connections, chapter_dir):
|
output_path, lecture_file_name, concurrent_connections, chapter_dir, disable_ipv6):
|
||||||
os.chdir(os.path.join(chapter_dir))
|
os.chdir(os.path.join(chapter_dir))
|
||||||
file_name = lecture_file_name.replace("%", "").replace(".mp4", "")
|
file_name = lecture_file_name.replace("%", "").replace(".mp4", "")
|
||||||
video_filepath_enc = file_name + ".encrypted.mp4"
|
video_filepath_enc = file_name + ".encrypted.mp4"
|
||||||
@ -901,12 +901,16 @@ def handle_segments(url, format_id, video_title,
|
|||||||
video_filepath_dec = file_name + ".decrypted.mp4"
|
video_filepath_dec = file_name + ".decrypted.mp4"
|
||||||
audio_filepath_dec = file_name + ".decrypted.m4a"
|
audio_filepath_dec = file_name + ".decrypted.m4a"
|
||||||
print("> Downloading Lecture Tracks...")
|
print("> Downloading Lecture Tracks...")
|
||||||
ret_code = subprocess.Popen([
|
args = [
|
||||||
"yt-dlp", "--force-generic-extractor", "--allow-unplayable-formats",
|
"yt-dlp", "--force-generic-extractor", "--allow-unplayable-formats",
|
||||||
"--concurrent-fragments", f"{concurrent_connections}", "--downloader",
|
"--concurrent-fragments", f"{concurrent_connections}", "--downloader",
|
||||||
"aria2c", "--fixup", "never", "-k", "-o", f"{file_name}.encrypted.%(ext)s",
|
"aria2c", "--fixup", "never", "-k", "-o", f"{file_name}.encrypted.%(ext)s",
|
||||||
"-f", format_id, f"{url}"
|
"-f", format_id, f"{url}"
|
||||||
]).wait()
|
]
|
||||||
|
if disable_ipv6:
|
||||||
|
args.append("--downloader-args")
|
||||||
|
args.append("aria2c:\"--disable-ipv6\"")
|
||||||
|
ret_code = subprocess.Popen(args).wait()
|
||||||
print("> Lecture Tracks Downloaded")
|
print("> Lecture Tracks Downloaded")
|
||||||
|
|
||||||
print("Return code: " + str(ret_code))
|
print("Return code: " + str(ret_code))
|
||||||
@ -1007,21 +1011,24 @@ def download(url, path, filename):
|
|||||||
return file_size
|
return file_size
|
||||||
|
|
||||||
|
|
||||||
def download_aria(url, file_dir, filename):
|
def download_aria(url, file_dir, filename, disable_ipv6):
|
||||||
"""
|
"""
|
||||||
@author Puyodead1
|
@author Puyodead1
|
||||||
"""
|
"""
|
||||||
print(" > Downloading File...")
|
print(" > Downloading File...")
|
||||||
ret_code = subprocess.Popen([
|
args = [
|
||||||
"aria2c", url, "-o", filename, "-d", file_dir, "-j16", "-s20", "-x16",
|
"aria2c", url, "-o", filename, "-d", file_dir, "-j16", "-s20", "-x16",
|
||||||
"-c", "--auto-file-renaming=false", "--summary-interval=0"
|
"-c", "--auto-file-renaming=false", "--summary-interval=0"
|
||||||
]).wait()
|
]
|
||||||
|
if disable_ipv6:
|
||||||
|
args.append("--disable-ipv6")
|
||||||
|
ret_code = subprocess.Popen(args).wait()
|
||||||
print(" > File Downloaded")
|
print(" > File Downloaded")
|
||||||
|
|
||||||
print("Return code: " + str(ret_code))
|
print("Return code: " + str(ret_code))
|
||||||
|
|
||||||
|
|
||||||
def process_caption(caption, lecture_title, lecture_dir, keep_vtt, tries=0):
|
def process_caption(caption, lecture_title, lecture_dir, keep_vtt, tries=0, disable_ipv6=False):
|
||||||
filename = f"%s_%s.%s" % (sanitize(lecture_title), caption.get("language"),
|
filename = f"%s_%s.%s" % (sanitize(lecture_title), caption.get("language"),
|
||||||
caption.get("extension"))
|
caption.get("extension"))
|
||||||
filename_no_ext = f"%s_%s" % (sanitize(lecture_title),
|
filename_no_ext = f"%s_%s" % (sanitize(lecture_title),
|
||||||
@ -1033,7 +1040,7 @@ def process_caption(caption, lecture_title, lecture_dir, keep_vtt, tries=0):
|
|||||||
else:
|
else:
|
||||||
print(f" > Downloading caption: '%s'" % filename)
|
print(f" > Downloading caption: '%s'" % filename)
|
||||||
try:
|
try:
|
||||||
download_aria(caption.get("download_url"), lecture_dir, filename)
|
download_aria(caption.get("download_url"), lecture_dir, filename, disable_ipv6)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if tries >= 3:
|
if tries >= 3:
|
||||||
print(
|
print(
|
||||||
@ -1045,7 +1052,7 @@ def process_caption(caption, lecture_title, lecture_dir, keep_vtt, tries=0):
|
|||||||
f" > Error downloading caption: {e}. Will retry {3-tries} more times."
|
f" > Error downloading caption: {e}. Will retry {3-tries} more times."
|
||||||
)
|
)
|
||||||
process_caption(caption, lecture_title, lecture_dir, keep_vtt,
|
process_caption(caption, lecture_title, lecture_dir, keep_vtt,
|
||||||
tries + 1)
|
tries + 1, disable_ipv6)
|
||||||
if caption.get("extension") == "vtt":
|
if caption.get("extension") == "vtt":
|
||||||
try:
|
try:
|
||||||
print(" > Converting caption to SRT format...")
|
print(" > Converting caption to SRT format...")
|
||||||
@ -1058,7 +1065,7 @@ def process_caption(caption, lecture_title, lecture_dir, keep_vtt, tries=0):
|
|||||||
|
|
||||||
|
|
||||||
def process_lecture(lecture, lecture_path, lecture_file_name, quality, access_token,
|
def process_lecture(lecture, lecture_path, lecture_file_name, quality, access_token,
|
||||||
concurrent_connections, chapter_dir):
|
concurrent_connections, chapter_dir, disable_ipv6):
|
||||||
lecture_title = lecture.get("lecture_title")
|
lecture_title = lecture.get("lecture_title")
|
||||||
is_encrypted = lecture.get("is_encrypted")
|
is_encrypted = lecture.get("is_encrypted")
|
||||||
lecture_sources = lecture.get("video_sources")
|
lecture_sources = lecture.get("video_sources")
|
||||||
@ -1075,7 +1082,7 @@ def process_lecture(lecture, lecture_path, lecture_file_name, quality, access_to
|
|||||||
handle_segments(source.get("download_url"),
|
handle_segments(source.get("download_url"),
|
||||||
source.get(
|
source.get(
|
||||||
"format_id"), lecture_title, lecture_path, lecture_file_name,
|
"format_id"), lecture_title, lecture_path, lecture_file_name,
|
||||||
concurrent_connections, chapter_dir)
|
concurrent_connections, chapter_dir, disable_ipv6)
|
||||||
else:
|
else:
|
||||||
print(f" > Lecture '%s' is missing media links" %
|
print(f" > Lecture '%s' is missing media links" %
|
||||||
lecture_title)
|
lecture_title)
|
||||||
@ -1103,17 +1110,21 @@ def process_lecture(lecture, lecture_path, lecture_file_name, quality, access_to
|
|||||||
if source_type == "hls":
|
if source_type == "hls":
|
||||||
temp_filepath = lecture_path.replace(
|
temp_filepath = lecture_path.replace(
|
||||||
".mp4", ".%(ext)s")
|
".mp4", ".%(ext)s")
|
||||||
ret_code = subprocess.Popen([
|
args = [
|
||||||
"yt-dlp", "--force-generic-extractor",
|
"yt-dlp", "--force-generic-extractor",
|
||||||
"--concurrent-fragments",
|
"--concurrent-fragments",
|
||||||
f"{concurrent_connections}", "--downloader",
|
f"{concurrent_connections}", "--downloader",
|
||||||
"aria2c", "-o", f"{temp_filepath}", f"{url}"
|
"aria2c", "-o", f"{temp_filepath}", f"{url}"
|
||||||
]).wait()
|
]
|
||||||
|
if disable_ipv6:
|
||||||
|
args.append("--downloader-args")
|
||||||
|
args.append( "aria2c:\"--disable-ipv6\"")
|
||||||
|
ret_code = subprocess.Popen(args).wait()
|
||||||
if ret_code == 0:
|
if ret_code == 0:
|
||||||
# os.rename(temp_filepath, lecture_path)
|
# os.rename(temp_filepath, lecture_path)
|
||||||
print(" > HLS Download success")
|
print(" > HLS Download success")
|
||||||
else:
|
else:
|
||||||
download_aria(url, chapter_dir, lecture_title + ".mp4")
|
download_aria(url, chapter_dir, lecture_title + ".mp4", disable_ipv6)
|
||||||
except EnvironmentError as e:
|
except EnvironmentError as e:
|
||||||
print("> Error downloading lecture")
|
print("> Error downloading lecture")
|
||||||
raise e
|
raise e
|
||||||
@ -1126,7 +1137,7 @@ def process_lecture(lecture, lecture_path, lecture_file_name, quality, access_to
|
|||||||
|
|
||||||
|
|
||||||
def parse_new(_udemy, quality, skip_lectures, dl_assets, dl_captions,
|
def parse_new(_udemy, quality, skip_lectures, dl_assets, dl_captions,
|
||||||
caption_locale, keep_vtt, access_token, concurrent_connections):
|
caption_locale, keep_vtt, access_token, concurrent_connections, disable_ipv6):
|
||||||
total_chapters = _udemy.get("total_chapters")
|
total_chapters = _udemy.get("total_chapters")
|
||||||
total_lectures = _udemy.get("total_lectures")
|
total_lectures = _udemy.get("total_lectures")
|
||||||
print(f"Chapter(s) ({total_chapters})")
|
print(f"Chapter(s) ({total_chapters})")
|
||||||
@ -1187,7 +1198,7 @@ def parse_new(_udemy, quality, skip_lectures, dl_assets, dl_captions,
|
|||||||
else:
|
else:
|
||||||
process_lecture(lecture, lecture_path, lecture_file_name,
|
process_lecture(lecture, lecture_path, lecture_file_name,
|
||||||
quality, access_token,
|
quality, access_token,
|
||||||
concurrent_connections, chapter_dir)
|
concurrent_connections, chapter_dir, disable_ipv6)
|
||||||
|
|
||||||
if dl_assets:
|
if dl_assets:
|
||||||
assets = lecture.get("assets")
|
assets = lecture.get("assets")
|
||||||
@ -1223,7 +1234,7 @@ def parse_new(_udemy, quality, skip_lectures, dl_assets, dl_captions,
|
|||||||
elif asset_type == "audio" or asset_type == "e-book" or asset_type == "file" or asset_type == "presentation":
|
elif asset_type == "audio" or asset_type == "e-book" or asset_type == "file" or asset_type == "presentation":
|
||||||
try:
|
try:
|
||||||
download_aria(download_url, chapter_dir,
|
download_aria(download_url, chapter_dir,
|
||||||
f"{asset_id}-{filename}")
|
f"{asset_id}-{filename}", disable_ipv6)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("> Error downloading asset: ", e)
|
print("> Error downloading asset: ", e)
|
||||||
continue
|
continue
|
||||||
@ -1257,7 +1268,7 @@ def parse_new(_udemy, quality, skip_lectures, dl_assets, dl_captions,
|
|||||||
lang = subtitle.get("language")
|
lang = subtitle.get("language")
|
||||||
if lang == caption_locale or caption_locale == "all":
|
if lang == caption_locale or caption_locale == "all":
|
||||||
process_caption(subtitle, lecture_title, chapter_dir,
|
process_caption(subtitle, lecture_title, chapter_dir,
|
||||||
keep_vtt)
|
keep_vtt, disable_ipv6)
|
||||||
|
|
||||||
|
|
||||||
def _print_course_info(course_data):
|
def _print_course_info(course_data):
|
||||||
@ -1365,6 +1376,12 @@ if __name__ == "__main__":
|
|||||||
type=int,
|
type=int,
|
||||||
help="The number of maximum concurrent downloads for segments (HLS and DASH, must be a number 1-30)",
|
help="The number of maximum concurrent downloads for segments (HLS and DASH, must be a number 1-30)",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--disable-ipv6",
|
||||||
|
dest="disable_ipv6",
|
||||||
|
action="store_true",
|
||||||
|
help="If specified, ipv6 will be disabled in aria2",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--skip-lectures",
|
"--skip-lectures",
|
||||||
dest="skip_lectures",
|
dest="skip_lectures",
|
||||||
@ -1428,6 +1445,7 @@ if __name__ == "__main__":
|
|||||||
keep_vtt = False
|
keep_vtt = False
|
||||||
skip_hls = False
|
skip_hls = False
|
||||||
concurrent_downloads = 10
|
concurrent_downloads = 10
|
||||||
|
disable_ipv6 = False
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
if args.download_assets:
|
if args.download_assets:
|
||||||
@ -1453,6 +1471,8 @@ if __name__ == "__main__":
|
|||||||
elif concurrent_downloads > 30:
|
elif concurrent_downloads > 30:
|
||||||
# if the user gave a number thats greater than 30, set cc to the max of 30
|
# if the user gave a number thats greater than 30, set cc to the max of 30
|
||||||
concurrent_downloads = 30
|
concurrent_downloads = 30
|
||||||
|
if args.disable_ipv6:
|
||||||
|
disable_ipv6 = args.disable_ipv6
|
||||||
|
|
||||||
aria_ret_val = check_for_aria()
|
aria_ret_val = check_for_aria()
|
||||||
if not aria_ret_val:
|
if not aria_ret_val:
|
||||||
@ -1530,7 +1550,7 @@ if __name__ == "__main__":
|
|||||||
else:
|
else:
|
||||||
parse_new(_udemy, quality, skip_lectures, dl_assets, dl_captions,
|
parse_new(_udemy, quality, skip_lectures, dl_assets, dl_captions,
|
||||||
caption_locale, keep_vtt, access_token,
|
caption_locale, keep_vtt, access_token,
|
||||||
concurrent_downloads)
|
concurrent_downloads, disable_ipv6)
|
||||||
else:
|
else:
|
||||||
_udemy = {}
|
_udemy = {}
|
||||||
_udemy["access_token"] = access_token
|
_udemy["access_token"] = access_token
|
||||||
@ -1765,4 +1785,4 @@ if __name__ == "__main__":
|
|||||||
else:
|
else:
|
||||||
parse_new(_udemy, quality, skip_lectures, dl_assets, dl_captions,
|
parse_new(_udemy, quality, skip_lectures, dl_assets, dl_captions,
|
||||||
caption_locale, keep_vtt, access_token,
|
caption_locale, keep_vtt, access_token,
|
||||||
concurrent_downloads)
|
concurrent_downloads, disable_ipv6)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user