Add direct YouTube upload to publish pipeline, publish ep14
Bypass flaky YouTube RSS ingestion by converting MP3+cover to MP4 and uploading via YouTube Data API. Videos are auto-added to the podcast playlist. Includes yt_auth.py for token management. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
49
yt_auth.py
Normal file
49
yt_auth.py
Normal file
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Re-authenticate YouTube API with full scopes (upload + playlist management).
|
||||
|
||||
Run this directly in your terminal when the YouTube token expires or needs new scopes:
|
||||
python yt_auth.py
|
||||
|
||||
It will open a browser for Google OAuth consent, then save the token.
|
||||
"""
|
||||
from pathlib import Path
|
||||
from google.oauth2.credentials import Credentials
|
||||
from google_auth_oauthlib.flow import InstalledAppFlow
|
||||
from google.auth.transport.requests import Request
|
||||
from googleapiclient.discovery import build as yt_build
|
||||
|
||||
CLIENT_SECRETS = Path(__file__).parent / "youtube_client_secrets.json"
|
||||
TOKEN_FILE = Path(__file__).parent / "youtube_token.json"
|
||||
SCOPES = [
|
||||
"https://www.googleapis.com/auth/youtube.upload",
|
||||
"https://www.googleapis.com/auth/youtube",
|
||||
]
|
||||
|
||||
def main():
|
||||
if not CLIENT_SECRETS.exists():
|
||||
print("Error: youtube_client_secrets.json not found")
|
||||
print("Download OAuth2 Desktop App credentials from Google Cloud Console")
|
||||
return
|
||||
|
||||
print("Opening browser for Google OAuth...")
|
||||
flow = InstalledAppFlow.from_client_secrets_file(str(CLIENT_SECRETS), SCOPES)
|
||||
creds = flow.run_local_server(port=8090, open_browser=True)
|
||||
|
||||
with open(TOKEN_FILE, "w") as f:
|
||||
f.write(creds.to_json())
|
||||
print(f"Token saved to {TOKEN_FILE}")
|
||||
|
||||
# Verify it works
|
||||
youtube = yt_build("youtube", "v3", credentials=creds)
|
||||
ch = youtube.channels().list(part="snippet", mine=True).execute()
|
||||
name = ch["items"][0]["snippet"]["title"] if ch.get("items") else "unknown"
|
||||
print(f"Authenticated as: {name}")
|
||||
|
||||
# Test playlist access
|
||||
pl = youtube.playlists().list(part="snippet", mine=True, maxResults=50).execute()
|
||||
print(f"Playlists: {len(pl.get('items', []))}")
|
||||
for p in pl.get("items", []):
|
||||
print(f" - {p['snippet']['title']} ({p['id']})")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user