Python

yt-dlp 라이브러리 사용하여 youtube 동영상 다운 받는 코드 작성

스카이데이즈 2026. 1. 20. 14:38
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("단일 영상 다운로드 완료!")

🔹 코드 특징

  1. mp4만 다운로드
    • 영상(mp4) + 오디오(m4a) → 자동 병합 → 최종 mp4
  2. 자동 폴더 생성
    • 재생목록 제목별로 폴더 생성
  3. 오류 영상 건너뛰기
    • ignoreerrors=True → 중간에 다운로드 실패해도 진행
  4. 총 개수 + 성공/실패 요약
    • 재생목록 전체 영상 수, 성공한 영상 수, 실패한 영상 수 출력
    • 실패 영상 인덱스도 확인 가능
  5. 한글/특수문자 안전
    • 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()

🔹 기능 설명

  1. 재생목록 URL 입력 → 텍스트 박스
  2. 다운로드 폴더 선택 → 폴더 선택 버튼
  3. 다운로드 시작 버튼 → 클릭 시 스레드로 실행
  4. 실시간 로그 → 다운로드 진행률, 완료/실패 영상 표시
  5. 완료 요약 → 총 영상 수, 성공/실패 개수

🔹 특징

  • mp4만 다운로드 (영상+오디오 병합)
  • 자동 폴더 생성
  • 실패한 영상도 건너뛰고, 다운로드 완료 후 요약 표시
  • GUI 멈춤 방지 위해 스레드 사용
728x90