import os import requests from google.auth.transport.requests import Request from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import InstalledAppFlow # type: ignore PARTNER_API_ROOT = "https://www.googleapis.com/youtube/partner/v1" YT_API_ROOT = "https://www.googleapis.com/youtube/v3" class Client: auth_headers: dict[str, str] def __init__( self, token_file: str = "token.json", app_secrets_file: str = "./client_secrets.json", scopes: list[str] = ["https://www.googleapis.com/auth/youtubepartner"], ) -> None: try: os.environ["OAUTHLIB_RELAX_TOKEN_SCOPE"] = "1" creds = None if os.path.exists(token_file): creds = Credentials.from_authorized_user_file(token_file, scopes) if not creds or not creds.valid: if creds and creds.expired and creds.refresh_token: creds.refresh(Request()) else: flow = InstalledAppFlow.from_client_secrets_file( app_secrets_file, scopes ) creds = flow.run_local_server(port=8080) with open(token_file, "w") as token: token.write(creds.to_json()) self.auth_headers = { "Authorization": f"Bearer {creds.token}", "Accept": "application/json", "Content-Type": "application/json", } except Exception as e: if str(e).startswith("Authorized user info was not in the expected format"): print(f""" Error: {e} 1. Remove app at https://myaccount.google.com/connections?filters=3,4&hl=en 2. Delete {token_file} 3. Retry """) exit(1) raise e def insert_reference(self, claim_id: str, behalf_of: str) -> dict: url = f"{PARTNER_API_ROOT}/references" params = {"claimId": claim_id, "onBehalfOfContentOwner": behalf_of} body = { "contentType": "audiovisual", } response = requests.post( url, headers=self.auth_headers, params=params, json=body ) response.raise_for_status() return response.json() def get_reference(self, reference_id: str, behalf_of: str) -> dict: url = f"{PARTNER_API_ROOT}/references/{reference_id}" params = {"onBehalfOfContentOwner": behalf_of} response = requests.get(url, headers=self.auth_headers, params=params) response.raise_for_status() return response.json() def get_references(self, asset_id: str, behalf_of: str) -> dict: url = f"{PARTNER_API_ROOT}/references" params = {"assetId": asset_id, "onBehalfOfContentOwner": behalf_of} response = requests.get(url, headers=self.auth_headers, params=params) response.raise_for_status() return response.json() def get_claim(self, claim_id: str, behalf_of: str) -> dict: url = f"{PARTNER_API_ROOT}/claims/{claim_id}" params = {"onBehalfOfContentOwner": behalf_of} response = requests.get(url, headers=self.auth_headers, params=params) response.raise_for_status() return response.json() def get_owner(self, reference_owner_id: str, behalf_of: str) -> dict: url = f"{PARTNER_API_ROOT}/contentOwners/{reference_owner_id}" params = {"onBehalfOfContentOwner": behalf_of} response = requests.get(url, headers=self.auth_headers, params=params) response.raise_for_status() return response.json() def get_asset(self, asset_id: str, behalf_of: str) -> dict: url = f"{PARTNER_API_ROOT}/assets/{asset_id}" params = {"onBehalfOfContentOwner": behalf_of} response = requests.get(url, headers=self.auth_headers, params=params) response.raise_for_status() return response.json() def get_videos(self, video_ids: list[str]) -> dict: url = f"{YT_API_ROOT}/videos" params = { "id": ",".join(video_ids), "part": "contentDetails,status,snippet", } response = requests.get(url, headers=self.auth_headers, params=params) response.raise_for_status() return response.json()