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>
50 lines
1.8 KiB
Python
50 lines
1.8 KiB
Python
#!/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()
|