← back to AgentAbrams

video/upload_youtube.py

90 lines

#!/usr/bin/env python3
"""
upload_youtube.py — Upload video to YouTube via OAuth refresh token.

Reads credentials from environment variables (safe for CI/CD).

Usage:
    python3 video/upload_youtube.py <video_file> <title> <description>

Environment variables:
    YOUTUBE_CLIENT_ID
    YOUTUBE_CLIENT_SECRET
    YOUTUBE_REFRESH_TOKEN
"""

import os
import sys

from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
from googleapiclient.http import MediaFileUpload

SCOPES = ["https://www.googleapis.com/auth/youtube.upload"]


def get_youtube_service():
    required = ["YOUTUBE_REFRESH_TOKEN", "YOUTUBE_CLIENT_ID", "YOUTUBE_CLIENT_SECRET"]
    missing = [k for k in required if not os.environ.get(k)]
    if missing:
        print(f"ERROR: missing environment variables: {', '.join(missing)}", file=sys.stderr)
        sys.exit(1)

    creds = Credentials(
        token=None,
        refresh_token=os.environ["YOUTUBE_REFRESH_TOKEN"],
        token_uri="https://oauth2.googleapis.com/token",
        client_id=os.environ["YOUTUBE_CLIENT_ID"],
        client_secret=os.environ["YOUTUBE_CLIENT_SECRET"],
        scopes=SCOPES,
    )
    return build("youtube", "v3", credentials=creds)


def upload_video(video_path, title, description, privacy="public"):
    youtube = get_youtube_service()

    body = {
        "snippet": {
            "title": title,
            "description": description,
            "tags": ["Claude Code", "AI agents", "AgentAbrams", "BuildInPublic"],
            "categoryId": "28",  # Science & Technology
        },
        "status": {
            "privacyStatus": privacy,
            "selfDeclaredMadeForKids": False,
        },
    }

    media = MediaFileUpload(video_path, resumable=True, chunksize=1024 * 1024)

    request = youtube.videos().insert(part="snippet,status", body=body, media_body=media)

    response = None
    while response is None:
        status, response = request.next_chunk()
        if status:
            pct = int(status.progress() * 100)
            print(f"Uploading... {pct}%")

    video_id = response["id"]
    print(f"Upload complete. Video ID: {video_id}")
    print(f"URL: https://www.youtube.com/watch?v={video_id}")
    return video_id


def main():
    if len(sys.argv) < 4:
        print("Usage: upload_youtube.py <video_file> <title> <description>", file=sys.stderr)
        sys.exit(2)
    if not os.path.isfile(sys.argv[1]):
        print(f"Video file not found: {sys.argv[1]}", file=sys.stderr)
        sys.exit(2)

    upload_video(sys.argv[1], sys.argv[2], sys.argv[3])


if __name__ == "__main__":
    main()