A simple tool which lets you scrape twitter accounts and crosspost them to bluesky accounts! Comes with a CLI and a webapp for managing profiles! Works with images/videos/link embeds/threads.

fix: restore video upload and improve fallback link

jack 1097567b 9d177ab2

+24 -3
+24 -3
src/index.ts
··· 439 439 .sort((a, b) => (b.bitrate || 0) - (a.bitrate || 0)); 440 440 441 441 if (mp4s.length > 0 && mp4s[0]) { 442 - // Reverting to links for video for now as native upload is failing 443 - text += `\n${media.media_url_https}`; 442 + const videoUrl = mp4s[0].url; 443 + try { 444 + const { buffer, mimeType } = await downloadMedia(videoUrl); 445 + if (buffer.length > 50 * 1024 * 1024) { // Reduced limit to be safer 446 + throw new Error('Video too large'); 447 + } 448 + const blob = await uploadToBluesky(agent, buffer, mimeType); 449 + videoBlob = blob; 450 + videoAspectRatio = aspectRatio; 451 + } catch (err) { 452 + console.warn(`Failed to upload video ${videoUrl}, linking to tweet instead:`, (err as Error).message); 453 + // Link to the actual tweet so it unfurls with the video correctly 454 + const tweetUrl = `https://twitter.com/${twitterUsername}/status/${tweetId}`; 455 + if (!text.includes(tweetUrl)) { 456 + text += `\n${tweetUrl}`; 457 + } 458 + } 444 459 } 445 460 } 446 461 } ··· 476 491 477 492 // Only attach media/quotes to the first chunk 478 493 if (i === 0) { 479 - if (images.length > 0) { 494 + if (videoBlob) { 495 + postRecord.embed = { 496 + $type: 'app.bsky.embed.video', 497 + video: videoBlob, 498 + aspectRatio: videoAspectRatio, 499 + }; 500 + } else if (images.length > 0) { 480 501 const imagesEmbed = { $type: 'app.bsky.embed.images', images }; 481 502 if (quoteEmbed) { 482 503 postRecord.embed = { $type: 'app.bsky.embed.recordWithMedia', media: imagesEmbed, record: quoteEmbed };