From e6377757d305e0b6b533859c8bd4f20e7c5f85a5 Mon Sep 17 00:00:00 2001 From: Guillem Hernandez Sola Date: Fri, 8 May 2026 10:55:36 +0200 Subject: [PATCH] ff --- bsky_post.py | 70 ++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/bsky_post.py b/bsky_post.py index d8e7f34..7b7ef8a 100644 --- a/bsky_post.py +++ b/bsky_post.py @@ -322,68 +322,68 @@ def upload_image( return None def upload_video_and_wait( - client: Client, - video_data: bytes, + client: Client, + video_data: bytes, alt_text: str = "" ) -> models.AppBskyEmbedVideo.Main | None: try: - logging.info("🎬 Requesting Service Auth for Video Upload...") - - # 1. Get a Service Auth token from your home PDS VIDEO_SERVICE_DID = "did:web:video.bsky.app" - auth_response = client.com.atproto.server.get_service_auth({ + + # --- Token #1: bound to uploadVideo --- + logging.info("🎬 Requesting Service Auth for Video Upload...") + upload_auth = client.com.atproto.server.get_service_auth({ 'aud': VIDEO_SERVICE_DID, - 'exp': int(time.time()) + 60 * 30, # Token valid for 30 mins + 'lxm': 'app.bsky.video.uploadVideo', + 'exp': int(time.time()) + 60 * 30, }) - service_auth_token = auth_response.token + upload_headers = { + "Authorization": f"Bearer {upload_auth.token}", + "Content-Type": "video/mp4", + } logging.info("🎬 Uploading video to Bluesky Video Service...") - - # 2. Upload the video using standard HTTP requests upload_url = "https://video.bsky.app/xrpc/app.bsky.video.uploadVideo" - headers = { - "Authorization": f"Bearer {service_auth_token}", - "Content-Type": "video/mp4" # Assuming mp4, adjust if needed - } - - # The endpoint expects the raw bytes in the body - upload_resp = requests.post(upload_url, headers=headers, data=video_data) - + upload_resp = requests.post(upload_url, headers=upload_headers, data=video_data) + if upload_resp.status_code != 200: logging.error(f"❌ Video upload failed: {upload_resp.status_code} - {upload_resp.text}") return None - + job_id = upload_resp.json().get("jobId") if not job_id: logging.error("❌ No Job ID returned from video service.") return None - + logging.info(f"⏳ Video uploaded! Job ID: {job_id}. Waiting for processing...") - - # 3. Poll the job status + + # --- Token #2: bound to getJobStatus --- + status_auth = client.com.atproto.server.get_service_auth({ + 'aud': VIDEO_SERVICE_DID, + 'lxm': 'app.bsky.video.getJobStatus', + 'exp': int(time.time()) + 60 * 30, + }) + status_headers = {"Authorization": f"Bearer {status_auth.token}"} + status_url = "https://video.bsky.app/xrpc/app.bsky.video.getJobStatus" params = {"jobId": job_id} - + while True: - status_resp = requests.get(status_url, headers=headers, params=params) - + status_resp = requests.get(status_url, headers=status_headers, params=params) + if status_resp.status_code != 200: logging.error(f"❌ Failed to get job status: {status_resp.status_code} - {status_resp.text}") return None - + job_status = status_resp.json().get("jobStatus", {}) state = job_status.get("state") - + if state == 'JOB_STATE_COMPLETED': logging.info("✅ Processing complete! Waiting 10s for CDN propagation...") - time.sleep(10) - - # Construct the blob object exactly as the SDK expects it + time.sleep(10) + blob_dict = job_status.get("blob") - - # Convert the raw dictionary into the SDK's BlobRef model blob_ref = models.BlobRef.from_dict(blob_dict) - + return models.AppBskyEmbedVideo.Main( video=blob_ref, alt=alt_text @@ -391,9 +391,9 @@ def upload_video_and_wait( elif state == 'JOB_STATE_FAILED': logging.error("❌ Video processing failed on Bluesky's servers.") return None - + logging.info(" ...still processing...") - time.sleep(3) + time.sleep(3) except Exception as e: logging.error(f"❌ Failed to upload/process video: {repr(e)}")