Files
video-pipeline/modules/yt_poster.py
Gramps e4d4a6c15f 🧠 Integrate notes.txt for dynamic thumbnail prompt generation
- Added generate_thumbnail_prompt(notes) to thumbnail_utils.py
- yt_poster.py now reads notes.txt (if present) from the clip directory
- Uses content to generate brand-aligned OpenAI prompt for future AI thumbnail generation
- Fallback: generic description if notes.txt is missing
- Prompt is printed for now; image generation hook is pending
2025-07-25 19:32:11 -07:00

153 lines
5.2 KiB
Python

"""
yt_poster.py
Handles video uploads to YouTube using the YouTube Data API.
This module includes logic for setting titles, descriptions, tags, and
privacy status. It integrates with description generation tools and supports
automatic metadata based on the video type (e.g., montage).
Requires authentication via OAuth 2.0 and expects a valid token.pickle file.
Author: Llama Chile Shop
"""
import os
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from googleapiclient.http import MediaFileUpload
from modules.title_utils import generate_montage_title
from modules.description_utils import generate_montage_description
from modules.thumbnail_utils import generate_thumbnail, generate_thumbnail_prompt
from modules.config import DEBUG
from dotenv import load_dotenv
from datetime import datetime
from pathlib import Path
load_dotenv()
def upload_video(file_path: Path, is_vertical: bool, stream_date: str, description: str = None, private: bool = DEBUG) -> str:
"""
Uploads a video to YouTube, assigns to playlist, sets recording date, and optionally uploads a thumbnail.
Args:
file_path (Path): Full path to the rendered video file.
is_vertical (bool): True if video is vertical.
stream_date (str): Stream session date in YYYY.MM.DD[.N] format.
description (str): Optional description. Generated if None.
private (bool): If True, uploads as private (used for debug mode).
Returns:
str: YouTube video URL.
"""
try:
from authorize_youtube import get_authenticated_service
youtube = get_authenticated_service()
title = generate_montage_title(stream_date)
if not description:
description = generate_montage_description()
tags = ["Fortnite", "Zero Build", "Solo", "Gramps", "CoolHandGramps"]
privacy_status = "private" if private else "public"
# Upload video
body = {
"snippet": {
"title": title,
"description": description,
"tags": tags,
"categoryId": "20", # Gaming
},
"status": {
"privacyStatus": privacy_status,
"selfDeclaredMadeForKids": False,
}
}
print(f"📤 Uploading to YouTube: {file_path.name}")
media = MediaFileUpload(str(file_path), chunksize=-1, resumable=True)
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:
print(f"🟡 Uploading: {int(status.progress() * 100)}%")
video_id = response["id"]
video_url = f"https://youtu.be/{video_id}"
print(f"✅ Upload complete: {video_url}")
# ✅ Generate thumbnail if widescreen
if not is_vertical:
# Load notes.txt if present
notes_file = file_path.with_name("notes.txt")
if notes_file.exists():
with open(notes_file, "r", encoding="utf-8") as f:
notes_text = f.read().strip()
else:
notes_text = "A funny Fortnite moment with Gramps."
# Build prompt (future use for AI image generation)
thumbnail_prompt = generate_thumbnail_prompt(notes_text)
print(f"🧠 Thumbnail prompt: {thumbnail_prompt}")
# Generate local thumbnail via ffmpeg
thumbnail_path = generate_thumbnail(file_path, output_path=f"{file_path.stem}_thumb.jpg")
if thumbnail_path:
youtube.thumbnails().set(
videoId=video_id,
media_body=str(thumbnail_path)
).execute()
print("✅ Custom thumbnail generated and set.")
# ✅ Add to playlist
playlist_id = os.getenv("YT_PLAYLIST_ID_SHORTS" if is_vertical else "YT_PLAYLIST_ID_CLIPS")
if playlist_id:
youtube.playlistItems().insert(
part="snippet",
body={
"snippet": {
"playlistId": playlist_id,
"resourceId": {
"kind": "youtube#video",
"videoId": video_id
}
}
}
).execute()
print(f"✅ Added to playlist: {playlist_id}")
# ✅ Set recording date
parts = stream_date.split(".")
date_obj = datetime(int(parts[0]), int(parts[1]), int(parts[2]))
recording_date = date_obj.strftime("%Y-%m-%dT00:00:00Z")
youtube.videos().update(
part="recordingDetails",
body={
"id": video_id,
"recordingDetails": {
"recordingDate": recording_date
}
}
).execute()
print(f"✅ Recording date set: {recording_date}")
return video_url
except HttpError as e:
print(f"❌ YouTube API error: {e}")
return ""
except Exception as e:
print(f"❌ Unexpected error during upload: {e}")
return ""