Add --disable-ipv6 option

+ Added option to disable ipv6 in aria2
+ Updated README to reflect argument changes
This commit is contained in:
Puyodead1 2021-11-19 16:17:01 -05:00
parent 97ca2cf401
commit 95b30841dc
2 changed files with 63 additions and 30 deletions

View File

@ -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
View File

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