74 lines
3.7 KiB
Python
Executable file
74 lines
3.7 KiB
Python
Executable file
import argparse
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
|
|
# define command-line arguments
|
|
parser = argparse.ArgumentParser(description='Download YouTube videos and playlists.')
|
|
parser.add_argument('input', help='File path or YouTube link. If a file path is provided, it should contain YouTube links line by line.')
|
|
parser.add_argument('--format', choices=['video', 'audio'], default='video', help='Specify the download format (audio or video). Default is video.')
|
|
parser.add_argument('--max-downloads', type=int, default=sys.maxsize, help='Maximum number of downloads. Default is unlimited.')
|
|
parser.add_argument('--path', help='Output path for downloaded files. Default is "./out".')
|
|
|
|
# parse the command-line arguments
|
|
args = parser.parse_args()
|
|
|
|
# use the provided output path or default to "./out"
|
|
output_path = args.path or './out'
|
|
|
|
# extract URLs from the input file or use the provided URL
|
|
urls = [line.strip() for line in open(args.input)] if os.path.isfile(args.input) else [args.input.strip()]
|
|
for u in urls:
|
|
# extract the base video URL without any additional parameters
|
|
base_url = u.split('&')[0]
|
|
print(f"\033[36mSYSTEM:\033[0m [{__file__}] URL: {base_url}")
|
|
|
|
# check if url is a video url or a playlist url
|
|
is_playlist = 'list=' in base_url
|
|
|
|
# download the best quality video file
|
|
video_format = ['bestvideo+bestaudio/best']
|
|
|
|
# download the best quality audio file and convert to flac
|
|
audio_format = ['bestaudio/best', '-x', '--audio-format', 'flac']
|
|
|
|
format_option = video_format if args.format == 'video' else audio_format
|
|
|
|
# extract channel id or playlist id from url, and use the id as folder name
|
|
channel_id = f'{base_url.split("list=")[1] if is_playlist else subprocess.check_output(["yt-dlp", "--print", "channel_url", "--playlist-items", "1", base_url], stderr=subprocess.DEVNULL, text=True).strip().split("/")[-1]}'
|
|
print(f"\033[36mSYSTEM:\033[0m [{__file__}] ID: {channel_id}")
|
|
|
|
try:
|
|
# create subdirectory using id as name (return to the initial directory after completing the download)
|
|
path = os.path.join(output_path, channel_id)
|
|
os.makedirs(path, exist_ok=True)
|
|
os.chdir(path)
|
|
|
|
except Exception as e:
|
|
# skip loop if id is invalid
|
|
print(f"\033[31mERROR:\033[0m [{__file__}] '{path}' is an invalid ID.")
|
|
continue
|
|
|
|
# construct filename with limited 'uploader' and 'title' to 60 bytes each to prevent exceeding maximum filename length (255)
|
|
filename = f'%(upload_date)s.%(uploader).60B.%(title).60B.(%(resolution)s).%(format_id)s.[%(id)s].%(ext)s'
|
|
|
|
options = [
|
|
'--write-info-json', '--write-description', # export metadata and description
|
|
'--write-thumbnail', '--embed-thumbnail', # download thumbnail image file
|
|
'--embed-metadata', # embed metadata to the downloaded file
|
|
'--sub-langs', 'all', '--write-subs', '--embed-subs', # download captions (except auto-generated) and live chat
|
|
'--embed-chapters', # include segment information
|
|
'--download-archive', 'archive.txt', # record id of downloaded video to prevent redownloading
|
|
'--geo-bypass', # bypass geographic restriction due to copyright
|
|
'--yes-playlist' if is_playlist else '--no-playlist',
|
|
'--max-downloads', str(args.max_downloads), # limit max download count
|
|
# retrieve up to 100 comments, with a maximum of 10 replies total, sorted by the top comments
|
|
'--get-comments', '--extractor-args', 'youtube:comment_sort=top;max_comments=100,all,10'
|
|
]
|
|
|
|
# execute the download command
|
|
subprocess.run(['yt-dlp', '-f'] + format_option + ['-o', filename, base_url] + options)
|
|
|
|
# return to the initial directory once the download is complete
|
|
os.chdir('../..')
|
|
|