Fies
This commit is contained in:
54
bsky_post.py
54
bsky_post.py
@@ -344,37 +344,42 @@ def upload_video_and_wait(
|
|||||||
client: Client,
|
client: Client,
|
||||||
video_data: bytes,
|
video_data: bytes,
|
||||||
alt_text: str = "",
|
alt_text: str = "",
|
||||||
service_url: str = "https://bsky.social",
|
service_url: str = "https://bsky.social", # kept for signature stability
|
||||||
) -> models.AppBskyEmbedVideo.Main | None:
|
) -> 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:
|
The video service is shared infrastructure across the atproto network — even
|
||||||
* Self-hosted/federated PDSes (e.g. eurosky.social) typically host the
|
federated PDSes (e.g. eurosky.social) use video.bsky.app for upload/processing.
|
||||||
video XRPC endpoints on the PDS itself, with `aud = did:web:<pds-host>`.
|
The resulting blob is stored back in the user's home PDS automatically.
|
||||||
* 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.
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
pds_base = normalize_pds_base(service_url)
|
VIDEO_SERVICE_HOST = "https://video.bsky.app"
|
||||||
service_did = pds_did_from_base(pds_base)
|
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 ---
|
# --- Token #1: bound to uploadVideo ---
|
||||||
logging.info("🎬 Requesting Service Auth for Video Upload...")
|
logging.info("🎬 Requesting Service Auth for Video Upload...")
|
||||||
upload_auth = client.com.atproto.server.get_service_auth({
|
upload_auth = client.com.atproto.server.get_service_auth({
|
||||||
'aud': service_did,
|
'aud': VIDEO_SERVICE_DID,
|
||||||
'lxm': 'app.bsky.video.uploadVideo',
|
'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 = {
|
upload_headers = {
|
||||||
"Authorization": f"Bearer {upload_auth.token}",
|
"Authorization": f"Bearer {upload_token}",
|
||||||
"Content-Type": "video/mp4",
|
"Content-Type": "video/mp4",
|
||||||
}
|
}
|
||||||
|
|
||||||
upload_url = f"{pds_base}/xrpc/app.bsky.video.uploadVideo"
|
|
||||||
logging.info(f"🎬 Uploading video to {upload_url} ...")
|
logging.info(f"🎬 Uploading video to {upload_url} ...")
|
||||||
upload_resp = requests.post(upload_url, headers=upload_headers, data=video_data)
|
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...")
|
logging.info(f"⏳ Video uploaded! Job ID: {job_id}. Waiting for processing...")
|
||||||
|
|
||||||
# --- Token #2: bound to getJobStatus ---
|
# --- getJobStatus is unauthenticated on video.bsky.app ---
|
||||||
status_auth = client.com.atproto.server.get_service_auth({
|
# (per the XRPC walkthrough, no auth header is required for status polling)
|
||||||
'aud': service_did,
|
status_url = f"{VIDEO_SERVICE_HOST}/xrpc/app.bsky.video.getJobStatus"
|
||||||
'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"
|
|
||||||
params = {"jobId": job_id}
|
params = {"jobId": job_id}
|
||||||
|
|
||||||
while True:
|
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:
|
if status_resp.status_code != 200:
|
||||||
logging.error(f"❌ Failed to get job status: {status_resp.status_code} - {status_resp.text}")
|
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)
|
time.sleep(10)
|
||||||
|
|
||||||
blob_dict = job_status.get("blob")
|
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(
|
return models.AppBskyEmbedVideo.Main(
|
||||||
video=blob_ref,
|
video=blob_ref,
|
||||||
@@ -432,7 +431,6 @@ def upload_video_and_wait(
|
|||||||
logging.error(f"❌ Failed to upload/process video: {repr(e)}")
|
logging.error(f"❌ Failed to upload/process video: {repr(e)}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Post
|
# Post
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ pipeline {
|
|||||||
python3 -m venv venv
|
python3 -m venv venv
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
pip install --upgrade pip --quiet
|
pip install --upgrade pip --quiet
|
||||||
pip install --quiet atproto
|
pip install --quiet atproto requests
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ pipeline {
|
|||||||
python3 -m venv venv
|
python3 -m venv venv
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
pip install --upgrade pip --quiet
|
pip install --upgrade pip --quiet
|
||||||
pip install --quiet atproto
|
pip install --quiet atproto requests
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ pipeline {
|
|||||||
python3 -m venv venv
|
python3 -m venv venv
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
pip install --upgrade pip --quiet
|
pip install --upgrade pip --quiet
|
||||||
pip install --quiet atproto
|
pip install --quiet atproto requests
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user