From a3031e1593e22e7f8c91c0c8017701ecb2e30142 Mon Sep 17 00:00:00 2001 From: Guillem Hernandez Sola Date: Mon, 30 Mar 2026 17:06:21 +0200 Subject: [PATCH] Added new yml --- twitter2bsky_daemon.py | 77 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 11 deletions(-) diff --git a/twitter2bsky_daemon.py b/twitter2bsky_daemon.py index 91b8a63..e265d40 100644 --- a/twitter2bsky_daemon.py +++ b/twitter2bsky_daemon.py @@ -8,6 +8,7 @@ import os from dotenv import load_dotenv from atproto import Client, client_utils, models from playwright.sync_api import sync_playwright +from moviepy.editor import VideoFileClip # --- Logging Setup --- LOG_PATH = "twitter2bsky.log" @@ -199,7 +200,31 @@ def clean_url(url): return cleaned_url return None -# --- 4. Formatting & Bluesky Logic --- +# --- 4. Video Processing --- +def download_and_crop_video(video_url, output_path): + """Downloads the video and crops it to 59 seconds.""" + try: + # Download the video + response = httpx.get(video_url, timeout=10) + if response.status_code == 200: + with open(output_path, 'wb') as f: + f.write(response.content) + logging.info(f"✅ Video downloaded: {output_path}") + + # Crop the video to 59 seconds + video_clip = VideoFileClip(output_path) + cropped_clip = video_clip.subclip(0, min(59, video_clip.duration)) + cropped_clip.write_videofile(output_path, codec='libx264') + logging.info(f"✅ Video cropped to 59 seconds: {output_path}") + return output_path + else: + logging.error(f"❌ Failed to download video: {video_url}") + return None + except Exception as e: + logging.error(f"❌ Error processing video: {e}") + return None + +# --- 5. Formatting & Bluesky Logic --- def get_last_bsky(client, handle): timeline = client.get_author_feed(handle) for titem in timeline.feed: @@ -288,16 +313,17 @@ def make_rich(content): return text_builder -def get_blob_from_url(image_url, client): +def get_blob_from_url(media_url, client): + """Fetches and uploads the media (image or video) and returns the blob.""" try: - r = httpx.get(image_url, timeout=10) + r = httpx.get(media_url, timeout=10) if r.status_code == 200: return client.upload_blob(r.content).blob except Exception as e: - logging.warning(f"Could not fetch image {image_url}: {e}") + logging.warning(f"Could not fetch media {media_url}: {e}") return None -# --- 5. Main Sync Function --- +# --- 6. Main Sync Function --- def sync_feeds(args): logging.info("🔄 Starting sync cycle...") try: @@ -324,7 +350,8 @@ def sync_feeds(args): for tweet in reversed(tweets): tweet_time = arrow.get(tweet.created_on) - if tweet_time > last_bsky_time: # Only post new tweets + #if tweet_time > last_bsky_time: # Only post new tweets + if True: # For testing, post all tweets regardless of time logging.info(f"📝 Found new tweet from {tweet_time}. Posting to Bluesky...") raw_text = tweet.text.strip() @@ -365,10 +392,14 @@ def sync_feeds(args): # Inject our dynamic alt text here! images.append(models.AppBskyEmbedImages.Image(alt=dynamic_alt, image=blob)) elif media.type == "video": - # Handle video uploads if necessary (this part may vary based on your API capabilities) - blob = get_blob_from_url(media.media_url_https, bsky_client) - if blob: - images.append(models.AppBskyEmbedImages.Image(alt=dynamic_alt, image=blob)) + # Download and crop the video + video_path = "temp_video.mp4" + cropped_video_path = download_and_crop_video(media.media_url_https, video_path) + if cropped_video_path: + blob = get_blob_from_url(cropped_video_path, bsky_client) + if blob: + images.append(models.AppBskyEmbedImages.Image(alt=dynamic_alt, image=blob)) + os.remove(video_path) # Clean up the temporary video file # 🌐 Posting with Catalan language tag try: @@ -389,7 +420,31 @@ def sync_feeds(args): except Exception as e: logging.error(f"❌ Error during sync cycle: {e}") -# --- 6. Main Execution --- +# --- 7. Download and Crop Video Function --- +def download_and_crop_video(video_url, output_path): + """Downloads the video and crops it to 59 seconds.""" + try: + # Download the video + response = httpx.get(video_url, timeout=10) + if response.status_code == 200: + with open(output_path, 'wb') as f: + f.write(response.content) + logging.info(f"✅ Video downloaded: {output_path}") + + # Crop the video to 59 seconds + video_clip = VideoFileClip(output_path) + cropped_clip = video_clip.subclip(0, min(59, video_clip.duration)) + cropped_clip.write_videofile(output_path, codec='libx264') + logging.info(f"✅ Video cropped to 59 seconds: {output_path}") + return output_path + else: + logging.error(f"❌ Failed to download video: {video_url}") + return None + except Exception as e: + logging.error(f"❌ Error processing video: {e}") + return None + +# --- 8. Main Execution --- def main(): load_dotenv()