mirror of
https://github.com/darkzoul5/YoutubePlaylistSync.git
synced 2026-07-03 12:34:00 +03:00
Enhance download functionality by adding video download mode and max video quality options
This commit is contained in:
@@ -132,6 +132,7 @@ jobs:
|
|||||||
autoreconf -i
|
autoreconf -i
|
||||||
./configure ARIA2_STATIC=yes --with-openssl
|
./configure ARIA2_STATIC=yes --with-openssl
|
||||||
make -j$(nproc)
|
make -j$(nproc)
|
||||||
|
cd $CI_PROJECT_DIR
|
||||||
mkdir -p dist/linux/bin
|
mkdir -p dist/linux/bin
|
||||||
cp src/aria2c dist/linux/bin/aria2c
|
cp src/aria2c dist/linux/bin/aria2c
|
||||||
|
|
||||||
|
|||||||
+102
-12
@@ -44,6 +44,8 @@ class ConfigLoader:
|
|||||||
"yt_dlp_path": "./bin/yt-dlp.exe" if platform.system() == "Windows" else "yt-dlp",
|
"yt_dlp_path": "./bin/yt-dlp.exe" if platform.system() == "Windows" else "yt-dlp",
|
||||||
"ffmpeg_path": "./bin/ffmpeg.exe" if platform.system() == "Windows" else "ffmpeg",
|
"ffmpeg_path": "./bin/ffmpeg.exe" if platform.system() == "Windows" else "ffmpeg",
|
||||||
"aria2c_path": "./bin/aria2c.exe" if platform.system() == "Windows" else "aria2c",
|
"aria2c_path": "./bin/aria2c.exe" if platform.system() == "Windows" else "aria2c",
|
||||||
|
"download_mode": "audio", # options: audio, video, both
|
||||||
|
"max_video_quality": "1080p", # options: 720p, 1080p, 1440p, 2160p, best
|
||||||
"max_parallel_downloads": 10,
|
"max_parallel_downloads": 10,
|
||||||
"aria2c_connections": 8
|
"aria2c_connections": 8
|
||||||
}
|
}
|
||||||
@@ -102,6 +104,14 @@ class ConfigLoader:
|
|||||||
def aria2c_path(self):
|
def aria2c_path(self):
|
||||||
return self.data["aria2c_path"]
|
return self.data["aria2c_path"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def download_mode(self):
|
||||||
|
return self.data.get("download_mode", "audio")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def max_video_quality(self):
|
||||||
|
return self.data.get("max_video_quality", "1080p")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def max_parallel_downloads(self):
|
def max_parallel_downloads(self):
|
||||||
return self.data.get("max_parallel_downloads", 10)
|
return self.data.get("max_parallel_downloads", 10)
|
||||||
@@ -140,6 +150,8 @@ class PlaylistDownloader:
|
|||||||
self.yt_dlp = config.yt_dlp_path
|
self.yt_dlp = config.yt_dlp_path
|
||||||
self.ffmpeg = config.ffmpeg_path
|
self.ffmpeg = config.ffmpeg_path
|
||||||
self.aria2c = config.aria2c_path
|
self.aria2c = config.aria2c_path
|
||||||
|
self.download_mode = config.download_mode
|
||||||
|
self.max_video_quality = config.max_video_quality
|
||||||
self.max_parallel = config.max_parallel_downloads
|
self.max_parallel = config.max_parallel_downloads
|
||||||
self.aria2c_connections = config.aria2c_connections
|
self.aria2c_connections = config.aria2c_connections
|
||||||
|
|
||||||
@@ -185,11 +197,24 @@ class PlaylistDownloader:
|
|||||||
def download_video(self, video, track_index):
|
def download_video(self, video, track_index):
|
||||||
title = video.get("title", "[Unknown]")
|
title = video.get("title", "[Unknown]")
|
||||||
safe_title = self.sanitize_title(title, video["id"])
|
safe_title = self.sanitize_title(title, video["id"])
|
||||||
file_output = self.get_file_path(track_index, safe_title)
|
|
||||||
video_url = f"https://www.youtube.com/watch?v={video['id']}"
|
video_url = f"https://www.youtube.com/watch?v={video['id']}"
|
||||||
|
|
||||||
try:
|
# --- video quality mapping helper ---
|
||||||
subprocess.run([
|
def build_video_format(max_quality):
|
||||||
|
mapping = {
|
||||||
|
"720p": "bestvideo[height<=720]+bestaudio/best[height<=720]",
|
||||||
|
"1080p": "bestvideo[height<=1080]+bestaudio/best[height<=1080]",
|
||||||
|
"1440p": "bestvideo[height<=1440]+bestaudio/best[height<=1440]",
|
||||||
|
"2160p": "bestvideo[height<=2160]+bestaudio/best[height<=2160]",
|
||||||
|
"best": "bestvideo+bestaudio/best"
|
||||||
|
}
|
||||||
|
return mapping.get(max_quality.lower(), mapping["1080p"])
|
||||||
|
|
||||||
|
# --- decide command based on download mode ---
|
||||||
|
cmds = []
|
||||||
|
if self.download_mode == "audio":
|
||||||
|
output_path = self.get_file_path(track_index, safe_title)
|
||||||
|
args = [
|
||||||
str(self.yt_dlp),
|
str(self.yt_dlp),
|
||||||
"-f", "bestaudio",
|
"-f", "bestaudio",
|
||||||
"--extract-audio",
|
"--extract-audio",
|
||||||
@@ -197,19 +222,84 @@ class PlaylistDownloader:
|
|||||||
"--audio-quality", "0",
|
"--audio-quality", "0",
|
||||||
"--ffmpeg-location", str(self.ffmpeg),
|
"--ffmpeg-location", str(self.ffmpeg),
|
||||||
"--download-archive", str(self.archive),
|
"--download-archive", str(self.archive),
|
||||||
"-o", str(file_output),
|
"-o", str(output_path),
|
||||||
"--external-downloader", str(self.aria2c),
|
"--external-downloader", str(self.aria2c),
|
||||||
"--external-downloader-args", f"aria2c:-x {self.aria2c_connections} -s {self.aria2c_connections}",
|
"--external-downloader-args",
|
||||||
|
f"aria2c:-x {self.aria2c_connections} -s {self.aria2c_connections}",
|
||||||
video_url
|
video_url
|
||||||
], check=True)
|
]
|
||||||
print(f"{OK} Downloaded: {track_index:03d} - {title}")
|
cmds.append((args, f"{track_index:03d} - {title} (audio)"))
|
||||||
return True
|
|
||||||
except subprocess.CalledProcessError as e:
|
elif self.download_mode == "video":
|
||||||
#print(f"{FAIL} Error downloading {title}: {e}") #print full error
|
fmt = build_video_format(self.max_video_quality)
|
||||||
err_msg = e.stderr.strip().splitlines()[-1] if e.stderr else "Video Unlisted or Unavailable"
|
output_path = self.save_path / f"{track_index:03d} - {safe_title}.mp4"
|
||||||
print(f"{FAIL} Download failed: {title} — {err_msg}")
|
args = [
|
||||||
|
str(self.yt_dlp),
|
||||||
|
"-f", fmt,
|
||||||
|
"--merge-output-format", "mp4",
|
||||||
|
"--ffmpeg-location", str(self.ffmpeg),
|
||||||
|
"--download-archive", str(self.archive),
|
||||||
|
"-o", str(output_path),
|
||||||
|
"--external-downloader", str(self.aria2c),
|
||||||
|
"--external-downloader-args",
|
||||||
|
f"aria2c:-x {self.aria2c_connections} -s {self.aria2c_connections}",
|
||||||
|
video_url
|
||||||
|
]
|
||||||
|
cmds.append((args, f"{track_index:03d} - {title} (video)"))
|
||||||
|
|
||||||
|
elif self.download_mode == "both":
|
||||||
|
# audio
|
||||||
|
audio_output = self.get_file_path(track_index, safe_title)
|
||||||
|
audio_args = [
|
||||||
|
str(self.yt_dlp),
|
||||||
|
"-f", "bestaudio",
|
||||||
|
"--extract-audio",
|
||||||
|
"--audio-format", "mp3",
|
||||||
|
"--audio-quality", "0",
|
||||||
|
"--ffmpeg-location", str(self.ffmpeg),
|
||||||
|
"--download-archive", str(self.archive),
|
||||||
|
"-o", str(audio_output),
|
||||||
|
"--external-downloader", str(self.aria2c),
|
||||||
|
"--external-downloader-args",
|
||||||
|
f"aria2c:-x {self.aria2c_connections} -s {self.aria2c_connections}",
|
||||||
|
video_url
|
||||||
|
]
|
||||||
|
cmds.append((audio_args, f"{track_index:03d} - {title} (audio)"))
|
||||||
|
|
||||||
|
# video
|
||||||
|
fmt = build_video_format(self.max_video_quality)
|
||||||
|
video_output = self.save_path / f"{track_index:03d} - {safe_title}.mp4"
|
||||||
|
video_args = [
|
||||||
|
str(self.yt_dlp),
|
||||||
|
"-f", fmt,
|
||||||
|
"--merge-output-format", "mp4",
|
||||||
|
"--ffmpeg-location", str(self.ffmpeg),
|
||||||
|
"--download-archive", str(self.archive),
|
||||||
|
"-o", str(video_output),
|
||||||
|
"--external-downloader", str(self.aria2c),
|
||||||
|
"--external-downloader-args",
|
||||||
|
f"aria2c:-x {self.aria2c_connections} -s {self.aria2c_connections}",
|
||||||
|
video_url
|
||||||
|
]
|
||||||
|
cmds.append((video_args, f"{track_index:03d} - {title} (video)"))
|
||||||
|
|
||||||
|
else:
|
||||||
|
print(f"{FAIL} Invalid download_mode '{self.download_mode}', skipping")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# --- execute one or both downloads ---
|
||||||
|
success = True
|
||||||
|
for args, label in cmds:
|
||||||
|
try:
|
||||||
|
subprocess.run(args, check=True)
|
||||||
|
print(f"{OK} Downloaded: {label}")
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
err_msg = e.stderr.strip().splitlines()[-1] if e.stderr else "Unknown error"
|
||||||
|
print(f"{FAIL} Download failed: {label} — {err_msg}")
|
||||||
|
success = False
|
||||||
|
|
||||||
|
return success
|
||||||
|
|
||||||
def renumber_all_tracks(self, playlist_entries):
|
def renumber_all_tracks(self, playlist_entries):
|
||||||
print(f"\n{STEP} Renumbering files according to playlist order")
|
print(f"\n{STEP} Renumbering files according to playlist order")
|
||||||
temp_suffix = ".renametemp"
|
temp_suffix = ".renametemp"
|
||||||
|
|||||||
Reference in New Issue
Block a user