This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

archiver-bot: never post broken messages, retry IPFS hash, fix lock contention

+35 -22
+35 -22
5pkgs/archiver-bot/archiver_bot/__init__.py
··· 115 115 if not video: 116 116 log.warning(f"No video file found for {path}") 117 117 return None 118 + import tempfile 119 + import shutil 120 + ipfs_path = tempfile.mkdtemp(prefix="ipfs-hash-") 118 121 try: 119 - ipfs_path = "/tmp/ipfs-hash-repo" 120 - if not os.path.exists(os.path.join(ipfs_path, "config")): 121 - init_proc = await asyncio.create_subprocess_exec( 122 - IPFS_BIN, "init", 123 - stdout=asyncio.subprocess.PIPE, 124 - stderr=asyncio.subprocess.PIPE, 125 - env={**os.environ, "IPFS_PATH": ipfs_path}, 126 - ) 127 - await init_proc.communicate() 128 122 env = {**os.environ, "IPFS_PATH": ipfs_path} 123 + init_proc = await asyncio.create_subprocess_exec( 124 + IPFS_BIN, "init", 125 + stdout=asyncio.subprocess.PIPE, 126 + stderr=asyncio.subprocess.PIPE, 127 + env=env, 128 + ) 129 + await init_proc.communicate() 129 130 proc = await asyncio.create_subprocess_exec( 130 131 IPFS_BIN, "add", "--only-hash", "--raw-leaves", "--quieter", video, 131 132 stdout=asyncio.subprocess.PIPE, ··· 133 134 env=env, 134 135 ) 135 136 try: 136 - stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=120) 137 + stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=300) 137 138 except asyncio.TimeoutError: 138 139 proc.kill() 139 140 await proc.communicate() ··· 147 148 log.warning(f"ipfs add --only-hash failed for {video}: {stderr.decode().strip()}") 148 149 except Exception as e: 149 150 log.warning(f"Failed to compute CID for {video}: {e}") 151 + finally: 152 + shutil.rmtree(ipfs_path, ignore_errors=True) 150 153 return None 151 154 152 155 ··· 228 231 lines_plain.append(f"🎬 Stream: {flix_link}") 229 232 lines_html.append(f'🎬 Stream: <a href="{flix_link}">{flix_link}</a>') 230 233 231 - # IPFS link (compute CID for the file) 232 - cid = await compute_cid(path) 234 + # IPFS link (compute CID for the file, retry up to 3 times) 235 + cid = None 236 + for attempt in range(3): 237 + cid = await compute_cid(path) 238 + if cid: 239 + break 240 + log.warning(f"IPFS hash attempt {attempt + 1}/3 failed for {path}") 241 + await asyncio.sleep(2) 242 + 233 243 if cid: 234 244 ipfs_link = f"{IPFS_GATEWAY_URL}/ipfs/{cid}" 235 245 lines_plain.append(f"🌐 IPFS: {ipfs_link}") ··· 237 247 lines_plain.append(f" ipfs get {cid}") 238 248 lines_html.append(f" <code>ipfs get {cid}</code>") 239 249 else: 250 + log.error(f"IPFS hash failed after 3 attempts for {path}, not posting message") 240 251 return None 241 252 242 253 return ("\n".join(lines_plain), "<br>".join(lines_html)) ··· 355 366 jf_id = await lookup_jellyfin_id(session, media_type, media_id) 356 367 links = await make_media_links(path, is_dir=is_dir, jellyfin_id=jf_id) 357 368 if not links: 358 - return (f"❌ {display} is available but IPFS hash failed.", f"❌ <b>{display}</b> is available but IPFS hash failed.") 369 + log.error(f"IPFS hash failed for {display}, not posting"); return None 359 370 links_plain, links_html = links 360 371 return (f"✅ {display} is already available!\n{links_plain}", f"✅ <b>{display}</b> is already available!<br>{links_html}") 361 372 return (f"✅ {display} is already available!", f"✅ <b>{display}</b> is already available!") ··· 376 387 # Still send the request to Jellyseerr so it's tracked there too 377 388 await jellyseerr_request(session, "/request", method="POST", json=payload) 378 389 if not links: 379 - return (f"❌ {display} is available but IPFS hash failed.", f"❌ <b>{display}</b> is available but IPFS hash failed.") 390 + log.error(f"IPFS hash failed for {display}, not posting"); return None 380 391 links_plain, links_html = links 381 392 return (f"✅ {display} is already available!\n{links_plain}", f"✅ <b>{display}</b> is already available!<br>{links_html}") 382 393 ··· 481 492 log.warning(f"Failed to fetch TMDB TV {tmdb_id}: {e}") 482 493 results.append(await request_media(http, media, room.room_id)) 483 494 484 - for plain, html in results: 485 - await send_notice(client, room.room_id, plain, html) 495 + for result in results: 496 + if result is not None: 497 + plain, html = result 498 + await send_notice(client, room.room_id, plain, html) 486 499 487 500 488 501 async def on_invite(client: AsyncClient, room: MatrixRoom, event: InviteMemberEvent): ··· 523 536 jf_id = await lookup_jellyfin_id(http, "movie", tmdb_id) 524 537 links = await make_media_links(file_path, jellyfin_id=jf_id) 525 538 if not links: 526 - links_plain = links_html = "❌ IPFS hash failed" 527 - else: 528 - links_plain, links_html = links 539 + log.error(f"IPFS hash failed for {display}, not notifying") 540 + return web.Response(text="ok") 541 + links_plain, links_html = links 529 542 plain = f"✅ {display} has been downloaded and is now available!\n{links_plain}" 530 543 html = f"✅ <b>{display}</b> has been downloaded and is now available!<br>{links_html}" 531 544 for room_id in rooms: ··· 571 584 jf_id = await lookup_jellyfin_id(http, "tv", tmdb_id) 572 585 links = await make_media_links(folder_path, is_dir=True, jellyfin_id=jf_id) 573 586 if not links: 574 - links_plain = links_html = "❌ IPFS hash failed" 575 - else: 576 - links_plain, links_html = links 587 + log.error(f"IPFS hash failed for {display}, not notifying") 588 + return web.Response(text="ok") 589 + links_plain, links_html = links 577 590 plain = f"✅ {display}{ep_info} has been downloaded and is now available!\n{links_plain}" 578 591 html = f"✅ <b>{display}</b>{ep_info} has been downloaded and is now available!<br>{links_html}" 579 592 for room_id in rooms: