#!/usr/bin/env -S uv run --script # /// script # dependencies = [ # "youtube-transcript-api", # ] # /// import argparse import logging import sys from pathlib import Path from urllib.parse import urlparse, parse_qs from youtube_transcript_api import YouTubeTranscriptApi logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s: %(message)s") def extract_video_id(url: str) -> str: parsed = urlparse(url) if parsed.hostname in ("youtu.be", "www.youtu.be"): return parsed.path.lstrip("/") if parsed.hostname in ("youtube.com", "www.youtube.com", "m.youtube.com"): if parsed.path == "/watch": return parse_qs(parsed.query)["v"][0] elif parsed.path.startswith("/embed/"): return parsed.path.split("/")[2] elif parsed.path.startswith("/v/"): return parsed.path.split("/")[2] raise ValueError(f"Could not extract video ID from URL: {url}") def format_timestamp(seconds: float) -> str: hours = int(seconds // 3600) minutes = int((seconds % 3600) // 60) secs = int(seconds % 60) millis = int((seconds % 1) * 1000) return f"{hours:02d}:{minutes:02d}:{secs:02d},{millis:03d}" def transcript_to_srt(transcript: list[dict]) -> str: srt_lines = [] for i, entry in enumerate(transcript, start=1): start_time = format_timestamp(entry["start"]) end_time = format_timestamp(entry["start"] + entry["duration"]) text = entry["text"] srt_lines.append(f"{i}") srt_lines.append(f"{start_time} --> {end_time}") srt_lines.append(text) srt_lines.append("") return "\n".join(srt_lines) def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser( description="Fetch YouTube video transcripts", formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) parser.add_argument("url", help="YouTube video URL") parser.add_argument( "--save-srt", type=Path, metavar="FILE_PATH", help="Save transcript as SRT file to the specified path", ) return parser.parse_args() def main() -> None: args = parse_args() try: video_id = extract_video_id(args.url) logging.info(f"Fetching transcript for video ID: {video_id}") ytt_api = YouTubeTranscriptApi() transcript = ytt_api.fetch(video_id) if args.save_srt: srt_content = transcript_to_srt(transcript.to_raw_data()) args.save_srt.write_text(srt_content) logging.info(f"SRT file saved to: {args.save_srt}") else: text = "\n".join(snippet.text for snippet in transcript) print(text) except Exception as e: logging.error(f"Failed to fetch transcript: {e}") sys.exit(1) if __name__ == "__main__": main()