diff --git a/bsky_post.py b/bsky_post.py index 9d87461..cb1c40b 100644 --- a/bsky_post.py +++ b/bsky_post.py @@ -344,37 +344,42 @@ def upload_video_and_wait( client: Client, video_data: bytes, alt_text: str = "", - service_url: str = "https://bsky.social", + service_url: str = "https://bsky.social", # kept for signature stability ) -> models.AppBskyEmbedVideo.Main | None: """ - Upload a video to the user's PDS video service and wait for processing. + Upload a video to the Bluesky video service (video.bsky.app) and wait for processing. - Notes on portability: - * Self-hosted/federated PDSes (e.g. eurosky.social) typically host the - video XRPC endpoints on the PDS itself, with `aud = did:web:`. - * The official Bluesky network uses a separate host (video.bsky.app), - but this implementation targets the PDS-hosted variant which works - for both eurosky-style PDSes and any conforming atproto deployment. + The video service is shared infrastructure across the atproto network — even + federated PDSes (e.g. eurosky.social) use video.bsky.app for upload/processing. + The resulting blob is stored back in the user's home PDS automatically. """ try: - pds_base = normalize_pds_base(service_url) - service_did = pds_did_from_base(pds_base) + VIDEO_SERVICE_HOST = "https://video.bsky.app" + VIDEO_SERVICE_DID = "did:web:video.bsky.app" - logging.info(f"🎬 Using video service at {pds_base} (aud={service_did})") + logging.info(f"🎬 Using shared video service at {VIDEO_SERVICE_HOST}") # --- Token #1: bound to uploadVideo --- logging.info("🎬 Requesting Service Auth for Video Upload...") upload_auth = client.com.atproto.server.get_service_auth({ - 'aud': service_did, + 'aud': VIDEO_SERVICE_DID, 'lxm': 'app.bsky.video.uploadVideo', - 'exp': int(time.time()) + 60 * 30, # 30 min (allowed because lxm is set) + 'exp': int(time.time()) + 60 * 30, }) + upload_token = upload_auth.token + + # The video service needs the user's DID in the upload URL as a query param + user_did = client.me.did + upload_url = ( + f"{VIDEO_SERVICE_HOST}/xrpc/app.bsky.video.uploadVideo" + f"?did={user_did}&name={int(time.time())}.mp4" + ) + upload_headers = { - "Authorization": f"Bearer {upload_auth.token}", - "Content-Type": "video/mp4", + "Authorization": f"Bearer {upload_token}", + "Content-Type": "video/mp4", } - upload_url = f"{pds_base}/xrpc/app.bsky.video.uploadVideo" logging.info(f"🎬 Uploading video to {upload_url} ...") upload_resp = requests.post(upload_url, headers=upload_headers, data=video_data) @@ -389,19 +394,13 @@ def upload_video_and_wait( logging.info(f"⏳ Video uploaded! Job ID: {job_id}. Waiting for processing...") - # --- Token #2: bound to getJobStatus --- - status_auth = client.com.atproto.server.get_service_auth({ - 'aud': service_did, - 'lxm': 'app.bsky.video.getJobStatus', - 'exp': int(time.time()) + 60 * 30, - }) - status_headers = {"Authorization": f"Bearer {status_auth.token}"} - - status_url = f"{pds_base}/xrpc/app.bsky.video.getJobStatus" + # --- getJobStatus is unauthenticated on video.bsky.app --- + # (per the XRPC walkthrough, no auth header is required for status polling) + status_url = f"{VIDEO_SERVICE_HOST}/xrpc/app.bsky.video.getJobStatus" params = {"jobId": job_id} while True: - status_resp = requests.get(status_url, headers=status_headers, params=params) + status_resp = requests.get(status_url, params=params) if status_resp.status_code != 200: logging.error(f"❌ Failed to get job status: {status_resp.status_code} - {status_resp.text}") @@ -415,7 +414,7 @@ def upload_video_and_wait( time.sleep(10) blob_dict = job_status.get("blob") - blob_ref = models.BlobRef.from_dict(blob_dict) + blob_ref = models.BlobRef.from_dict(blob_dict) return models.AppBskyEmbedVideo.Main( video=blob_ref, @@ -432,7 +431,6 @@ def upload_video_and_wait( logging.error(f"❌ Failed to upload/process video: {repr(e)}") return None - # ============================================================ # Post # ============================================================ diff --git a/jenkins/dijous b/jenkins/dijous index 7467056..269d8dc 100644 --- a/jenkins/dijous +++ b/jenkins/dijous @@ -20,7 +20,7 @@ pipeline { python3 -m venv venv . venv/bin/activate pip install --upgrade pip --quiet - pip install --quiet atproto + pip install --quiet atproto requests """ } } diff --git a/jenkins/diumenge_nit b/jenkins/diumenge_nit index 8e14916..c3ad86c 100644 --- a/jenkins/diumenge_nit +++ b/jenkins/diumenge_nit @@ -20,7 +20,7 @@ pipeline { python3 -m venv venv . venv/bin/activate pip install --upgrade pip --quiet - pip install --quiet atproto + pip install --quiet atproto requests """ } } diff --git a/jenkins/divendres_nit b/jenkins/divendres_nit index 9b77ed8..47eaec0 100644 --- a/jenkins/divendres_nit +++ b/jenkins/divendres_nit @@ -20,7 +20,7 @@ pipeline { python3 -m venv venv . venv/bin/activate pip install --upgrade pip --quiet - pip install --quiet atproto + pip install --quiet atproto requests """ } }