728x90
1️⃣ 라이브러리 설치
pip install yt-dlp
2️⃣ 재생목록 전체 다운로드 (최고 화질)
from yt_dlp import YoutubeDL
playlist_url = "https://www.youtube.com/playlist?list=재생목록ID"
ydl_opts = {
"format": "bestvideo+bestaudio/best",
"merge_output_format": "mp4",
"outtmpl": "downloads/%(playlist_title)s/%(playlist_index)s - %(title)s.%(ext)s",
"ignoreerrors": True, # 오류 영상은 건너뜀
}
with YoutubeDL(ydl_opts) as ydl:
ydl.download([playlist_url])
print("재생목록 다운로드 완료!")
📁 결과 구조 예시:
downloads/
└─ 재생목록 제목/
├─ 01 - 영상제목.mp4
├─ 02 - 영상제목.mp4
└─ ...
3️⃣ 옵션별 커스터마이징 (자주 쓰는 것들)
🎧 오디오만 (mp3)
ydl_opts = {
"format": "bestaudio/best",
"postprocessors": [{
"key": "FFmpegExtractAudio",
"preferredcodec": "mp3",
"preferredquality": "192",
}],
"outtmpl": "downloads/%(playlist_title)s/%(playlist_index)s - %(title)s.%(ext)s",
}
📝 자막도 함께 다운로드
ydl_opts = {
"format": "best",
"writesubtitles": True,
"writeautomaticsub": True,
"subtitleslangs": ["ko", "en"],
}
🚀 다운로드 속도 & 안정성 옵션
ydl_opts = {
"format": "best",
"concurrent_fragment_downloads": 5,
"retries": 10,
}
✅ 다른 경로 지정 및 PlayList에서 성공/실패 카운트
from yt_dlp import YoutubeDL
from pathlib import Path
# ----------------------------
# 1️⃣ 다운로드 경로 설정
# ----------------------------
BASE_DIR = Path.home()
# 다운로드할 재생목록 URL
playlist_url = "https://www.youtube.com/playlist?list=재생목록ID"
# ----------------------------
# 2️⃣ yt-dlp 옵션
# ----------------------------
ydl_opts = {
"format": "bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4", # mp4만
"merge_output_format": "mp4", # 영상+오디오 병합
"outtmpl": str(BASE_DIR) + "/%(playlist_title)s/%(playlist_index)s - %(title)s.%(ext)s",
"ignoreerrors": True, # 오류 영상 건너뛰기
"quiet": False, # 다운로드 로그 표시
}
# ----------------------------
# 3️⃣ 다운로드 실행 및 요약
# ----------------------------
with YoutubeDL(ydl_opts) as ydl:
info_dict = ydl.extract_info(playlist_url, download=True)
# ----------------------------
# 4️⃣ 다운로드 성공/실패 분석
# ----------------------------
if "entries" in info_dict:
entries = info_dict["entries"]
# 성공한 영상
successful = [v for v in entries if v is not None]
# 실패한 영상
failed = [v for v in entries if v is None]
print("\n✅ 다운로드 완료 요약")
print(f"총 영상 수: {len(entries)}")
print(f"성공: {len(successful)}")
print(f"실패: {len(failed)}")
if failed:
print("\n⚠️ 다운로드 실패한 영상 인덱스:")
for idx, v in enumerate(failed, start=1):
print(f"{idx}. 실패")
else:
# 단일 영상일 경우
print("단일 영상 다운로드 완료!")
🔹 코드 특징
- mp4만 다운로드
- 영상(mp4) + 오디오(m4a) → 자동 병합 → 최종 mp4
- 자동 폴더 생성
- 재생목록 제목별로 폴더 생성
- 오류 영상 건너뛰기
- ignoreerrors=True → 중간에 다운로드 실패해도 진행
- 총 개수 + 성공/실패 요약
- 재생목록 전체 영상 수, 성공한 영상 수, 실패한 영상 수 출력
- 실패 영상 인덱스도 확인 가능
- 한글/특수문자 안전
- Path + 문자열 처리로 OS 공용 사용 가능
✅ GUI 버전 코드
Python + Tkinter 기반 GUI 버전
- 다운로드 폴더 선택
- 재생목록 URL 입력
- 시작 버튼 클릭 → 다운로드
- 진행 로그 및 다운로드 완료 요약 출력
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext
from yt_dlp import YoutubeDL
from pathlib import Path
import threading
# ----------------------------
# 1️⃣ 다운로드 함수
# ----------------------------
def download_playlist():
url = url_entry.get().strip()
folder = folder_entry.get().strip()
if not url:
messagebox.showwarning("경고", "재생목록 URL을 입력하세요!")
return
if not folder:
messagebox.showwarning("경고", "다운로드 폴더를 선택하세요!")
return
folder_path = Path(folder)
folder_path.mkdir(parents=True, exist_ok=True)
ydl_opts = {
"format": "bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4",
"merge_output_format": "mp4",
"outtmpl": str(folder_path) + "/%(playlist_title)s/%(playlist_index)s - %(title)s.%(ext)s",
"ignoreerrors": True,
"quiet": True,
"progress_hooks": [progress_hook],
}
def run_download():
try:
log_text.insert(tk.END, "다운로드 시작...\n")
with YoutubeDL(ydl_opts) as ydl:
info_dict = ydl.extract_info(url, download=True)
if "entries" in info_dict:
entries = info_dict["entries"]
successful = [v for v in entries if v is not None]
failed = [v for v in entries if v is None]
log_text.insert(tk.END, "\n✅ 다운로드 완료 요약\n")
log_text.insert(tk.END, f"총 영상 수: {len(entries)}\n")
log_text.insert(tk.END, f"성공: {len(successful)}\n")
log_text.insert(tk.END, f"실패: {len(failed)}\n")
if failed:
log_text.insert(tk.END, "\n⚠️ 다운로드 실패한 영상 인덱스:\n")
for idx, v in enumerate(failed, start=1):
log_text.insert(tk.END, f"{idx}. 실패\n")
else:
log_text.insert(tk.END, "단일 영상 다운로드 완료!\n")
log_text.insert(tk.END, "\n모든 작업 완료!\n")
except Exception as e:
log_text.insert(tk.END, f"\n❌ 오류 발생: {e}\n")
# 스레드로 실행 → GUI 멈춤 방지
threading.Thread(target=run_download).start()
# ----------------------------
# 2️⃣ 진행 로그 표시
# ----------------------------
def progress_hook(d):
if d['status'] == 'downloading':
log_text.insert(tk.END, f"다운로드 중: {d['_percent_str']} {d['_eta_str']}\n")
log_text.see(tk.END)
# ----------------------------
# 3️⃣ 폴더 선택
# ----------------------------
def browse_folder():
folder_selected = filedialog.askdirectory()
if folder_selected:
folder_entry.delete(0, tk.END)
folder_entry.insert(0, folder_selected)
# ----------------------------
# 4️⃣ GUI 레이아웃
# ----------------------------
root = tk.Tk()
root.title("YouTube 재생목록 MP4 다운로드")
tk.Label(root, text="재생목록 URL:").grid(row=0, column=0, sticky="e")
url_entry = tk.Entry(root, width=50)
url_entry.grid(row=0, column=1, padx=5, pady=5, columnspan=2)
tk.Label(root, text="다운로드 폴더:").grid(row=1, column=0, sticky="e")
folder_entry = tk.Entry(root, width=40)
folder_entry.grid(row=1, column=1, padx=5, pady=5)
tk.Button(root, text="폴더 선택", command=browse_folder).grid(row=1, column=2, padx=5, pady=5)
tk.Button(root, text="다운로드 시작", command=download_playlist, bg="green", fg="white").grid(row=2, column=0, columnspan=3, pady=10)
log_text = scrolledtext.ScrolledText(root, width=70, height=20)
log_text.grid(row=3, column=0, columnspan=3, padx=10, pady=10)
root.mainloop()
🔹 기능 설명
- 재생목록 URL 입력 → 텍스트 박스
- 다운로드 폴더 선택 → 폴더 선택 버튼
- 다운로드 시작 버튼 → 클릭 시 스레드로 실행
- 실시간 로그 → 다운로드 진행률, 완료/실패 영상 표시
- 완료 요약 → 총 영상 수, 성공/실패 개수
🔹 특징
- mp4만 다운로드 (영상+오디오 병합)
- 자동 폴더 생성
- 실패한 영상도 건너뛰고, 다운로드 완료 후 요약 표시
- GUI 멈춤 방지 위해 스레드 사용
728x90