social media crossposting tool. 3rd time's the charm
mastodon
misskey
crossposting
bluesky
1import requests
2import subprocess
3import json
4from util import LOGGER
5
6def probe_bytes(bytes: bytes) -> dict:
7 cmd = [
8 'ffprobe',
9 '-v', 'error',
10 '-show_format',
11 '-show_streams',
12 '-print_format', 'json',
13 'pipe:0'
14 ]
15 proc = subprocess.run(cmd, input=bytes, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
16
17 if proc.returncode != 0:
18 raise RuntimeError(f"ffprobe failed: {proc.stderr.decode()}")
19
20 return json.loads(proc.stdout)
21
22def compress_image(image_bytes: bytes, quality: int = 90):
23 cmd = [
24 'ffmpeg',
25 '-f', 'image2pipe',
26 '-i', 'pipe:0',
27 '-c:v', 'webp',
28 '-q:v', str(quality),
29 '-f', 'image2pipe',
30 'pipe:1'
31 ]
32
33 proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
34 out_bytes, err = proc.communicate(input=image_bytes)
35
36 if proc.returncode != 0:
37 raise RuntimeError(f"ffmpeg compress failed: {err.decode()}")
38
39 return out_bytes
40
41def download_blob(url: str, max_bytes: int = 5_000_000) -> bytes | None:
42 response = requests.get(url, stream=True, timeout=20)
43 if response.status_code != 200:
44 LOGGER.info("Failed to download %s! %s", url, response)
45 return None
46
47 downloaded_bytes = b""
48 current_size = 0
49
50 for chunk in response.iter_content(chunk_size=8192):
51 if not chunk:
52 continue
53
54 current_size += len(chunk)
55 if current_size > max_bytes:
56 response.close()
57 return None
58
59 downloaded_bytes += chunk
60
61 return downloaded_bytes
62
63
64def get_media_meta(bytes: bytes):
65 probe = probe_bytes(bytes)
66 streams = [s for s in probe['streams'] if s['codec_type'] == 'video']
67 if not streams:
68 raise ValueError("No video stream found")
69
70 media = streams[0]
71 return {
72 'width': int(media['width']),
73 'height': int(media['height']),
74 'duration': float(media.get('duration', probe['format'].get('duration', -1)))
75 }