From 7deeec0a41a13877bf920a58df08ddd73b548f44 Mon Sep 17 00:00:00 2001 From: Guillem Hernandez Sola Date: Fri, 8 May 2026 09:53:59 +0200 Subject: [PATCH] Video posting --- bsky_post.py | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/bsky_post.py b/bsky_post.py index e6560e5..ceaf0f0 100644 --- a/bsky_post.py +++ b/bsky_post.py @@ -322,31 +322,41 @@ def upload_image( def upload_video_and_wait( client: Client, video_data: bytes, - alt_text: str = "", - password: str = "" + 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 (eurosky.social) + # The audience (aud) MUST be the DID of the Bluesky Video Service + VIDEO_SERVICE_DID = "did:web:video.bsky.app" + auth_response = client.com.atproto.server.get_service_auth({ + 'aud': VIDEO_SERVICE_DID + }) + service_auth_token = auth_response.token + logging.info("🎬 Uploading video to Bluesky Video Service...") - # Create a temporary client pointing to the official Bluesky service - # just for the video upload - video_client = Client(base_url="https://bsky.social") + # 2. Create a temporary client pointing to the dedicated video service + video_client = Client(base_url="https://video.bsky.app") - # We must manually set the session so it knows who we are - if client.me and password: - # Login using the explicitly passed password - video_client.login(client.me.handle, password) - else: - logging.error("❌ Main client is not logged in or password missing.") - return None + # 3. Inject the Service Auth token into the headers + # We don't call .login() on this client; we just set the authorization header manually. + video_client._session = models.ComAtprotoServerCreateSession.Response( + access_jwt=service_auth_token, + refresh_jwt="", + handle=client.me.handle, + did=client.me.did, + active=True + ) - # 1. Upload the video to the official service + # 4. Upload the video to the official service response = video_client.app.bsky.video.upload_video(video_data) job_id = response.job_id logging.info(f"⏳ Video uploaded! Job ID: {job_id}. Waiting for processing...") - # 2. Poll the job status using the video client + # 5. Poll the job status using the video client while True: status_resp = video_client.app.bsky.video.get_job_status({'job_id': job_id}) state = status_resp.job_status.state @@ -392,7 +402,9 @@ def post_to_bsky( video_data = f.read() # Pass the password to our custom polling function - video_embed = upload_video_and_wait(client, video_data, alt_text, password) + # Use our custom polling function (no password needed) + video_embed = upload_video_and_wait(client, video_data, alt_text) + if not video_embed: logging.error("❌ Aborting post: video upload/processing failed.")