From 8cd938016633b13e1eb67cae44347e3293290237 Mon Sep 17 00:00:00 2001 From: yhqe Date: Mon, 13 Jul 2026 03:18:07 -0500 Subject: [PATCH] added logo, for snippet command which has some other changes as well (gif support, error handling, bugfixes, etc) --- assets/logo.png | Bin 0 -> 39175 bytes src/commands/music/snippet.js | 221 ++++++++++++++++++++++++++-------- 2 files changed, 173 insertions(+), 48 deletions(-) create mode 100644 assets/logo.png diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..4f118e5affb9683eb6e6e30e0914c54ae2df7873 GIT binary patch literal 39175 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 setTimeout(resolve, ms)); +} + async function downloadAttachment(attachment, filePath) { - const response = await fetch(attachment.url); + const attempts = 3; - if (!response.ok || !response.body) { - throw new Error(`failed to download ${attachment.name}`); + for (let attempt = 1; attempt <= attempts; attempt += 1) { + try { + const response = await fetch(attachment.url); + + if (!response.ok || !response.body) { + throw new Error(`failed to download ${attachment.name}: HTTP ${response.status}`); + } + + await pipeline(Readable.fromWeb(response.body), fs.createWriteStream(filePath)); + return; + } catch (err) { + await fsp.rm(filePath, { force: true }).catch(() => null); + + if (attempt < attempts && isRetryableFetchError(err)) { + logger.warn( + `[snippet] attachment download failed, retrying (${attempt}/${attempts}) for ${attachment.name || 'unknown'}: ${err.message || err}`, + ); + await sleep(250 * attempt); + continue; + } + + throw new Error(`failed to download ${attachment.name}: ${err.message || err}`); + } } - - await pipeline(Readable.fromWeb(response.body), fs.createWriteStream(filePath)); } async function getDuration(filePath) { @@ -119,11 +179,32 @@ function buildEmbed(client, message, description, fields = []) { return embed; } +function getErrorMessage(err) { + if (err instanceof Error) { + return err.stack || err.message; + } + + if (typeof err === 'string') { + return err; + } + + return 'unknown error'; +} + +const logoPath = path.resolve(__dirname, '../../../assets/logo.png'); + +function buildVideoFilter() { + return [ + '[0:v]scale=trunc(iw/2)*2:trunc(ih/2)*2[base]', + '[2:v]format=rgba,colorchannelmixer=aa=0.5,scale=trunc(iw*0.12/2)*2:-1,rotate=(PI/2)*t:ow=rotw(iw):oh=roth(ih):c=none[logo]', + '[base][logo]overlay=x=W-w-16:y=H-h-16:shortest=1[v]', + ].join(';'); +} + module.exports = { name: 'snippet', - description: 'mix a random mp3 segment with a random video segment and export a clipped video', - aliases: ['mix', 'clip'], - usage: 'snippet [mp3 attachment] [video attachment]', + description: 'create a snippet with an image/video and an mp3', + usage: 'snippet [mp3 attachment] [image or video attachment]', async execute(client, message, args = []) { const requestedLength = Number.parseInt(args[0], 10); @@ -134,22 +215,27 @@ module.exports = { embeds: [buildEmbed( client, message, - '<:no:1481811955592466492> provide a clip length in seconds, then attach one mp3 and one video file.', + '<:no:1481811955592466492> provide a clip length in seconds, then attach one mp3 and either one image or one video file.', )], }); } const attachments = [...message.attachments.values()]; const mp3Attachment = attachments.find(isMp3Attachment); + const imageAttachment = attachments.find(isImageAttachment); + const gifAttachment = attachments.find(isGifAttachment); const videoAttachment = attachments.find(isVideoAttachment); - if (!mp3Attachment || !videoAttachment) { + const visualAttachment = imageAttachment || gifAttachment || videoAttachment; + const hasMultipleVisualTypes = [imageAttachment, gifAttachment, videoAttachment].filter(Boolean).length > 1; + + if (!mp3Attachment || !visualAttachment || hasMultipleVisualTypes) { return message.reply({ content: null, embeds: [buildEmbed( client, message, - '<:no:1481811955592466492> attach exactly one mp3 file and one video file to use this command.', + '<:no:1481811955592466492> attach one mp3 file and either one image, one gif, or one video file, but not more than one visual file.', )], }); } @@ -159,64 +245,97 @@ module.exports = { embeds: [buildEmbed( client, message, - ' building your clip...', + ' building your snippet...', )], }); const tempDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'twitter-snippet-')); - const mp3Path = path.join(tempDir, mp3Attachment.name || 'input.mp3'); - const videoPath = path.join(tempDir, videoAttachment.name || 'input.mp4'); + const audioPath = path.join(tempDir, mp3Attachment.name || 'input.mp3'); + const visualPath = path.join(tempDir, visualAttachment.name || (imageAttachment ? 'input.png' : gifAttachment ? 'input.gif' : 'input.mp4')); const outputPath = path.join(tempDir, 'snippet.mp4'); try { await Promise.all([ - downloadAttachment(mp3Attachment, mp3Path), - downloadAttachment(videoAttachment, videoPath), + downloadAttachment(mp3Attachment, audioPath), + downloadAttachment(visualAttachment, visualPath), ]); - const mp3Duration = await getDuration(mp3Path); - const videoDuration = await getDuration(videoPath); + const audioDuration = await getDuration(audioPath); + const isStillImageSource = Boolean(imageAttachment && imageAttachment === visualAttachment); + const isGifSource = Boolean(gifAttachment && gifAttachment === visualAttachment); + const isLoopedVisualSource = isStillImageSource || isGifSource; + const visualDuration = isLoopedVisualSource ? Infinity : await getDuration(visualPath); - if (requestedLength > mp3Duration || requestedLength > videoDuration) { + if (requestedLength > audioDuration || requestedLength > visualDuration) { throw new Error('requested length is longer than one of the attached files'); } const clipLength = requestedLength; - const videoStart = videoDuration === clipLength - ? 0 - : Math.random() * (videoDuration - clipLength); - const audioStart = mp3Duration === clipLength - ? 0 - : Math.random() * (mp3Duration - clipLength); + if (isLoopedVisualSource) { + const audioStart = audioDuration === clipLength + ? 0 + : Math.random() * (audioDuration - clipLength); - await runProcess('ffmpeg', [ - '-y', - '-ss', videoStart.toFixed(3), - '-i', videoPath, - '-ss', audioStart.toFixed(3), - '-i', mp3Path, - '-t', clipLength.toFixed(3), - '-map', '0:v:0', - '-map', '1:a:0', - '-c:v', 'libx264', - '-preset', 'veryfast', - '-crf', '20', - '-c:a', 'aac', - '-b:a', '192k', - '-pix_fmt', 'yuv420p', - '-vf', 'scale=trunc(iw/2)*2:trunc(ih/2)*2', - '-movflags', '+faststart', - outputPath, - ]); + await runProcess('ffmpeg', [ + '-y', + ...(isStillImageSource ? ['-loop', '1'] : ['-stream_loop', '-1']), + '-i', visualPath, + '-ss', audioStart.toFixed(3), + '-i', audioPath, + '-loop', '1', + '-i', logoPath, + '-t', clipLength.toFixed(3), + '-filter_complex', buildVideoFilter(), + '-map', '[v]', + '-map', '1:a:0', + '-c:v', 'libx264', + '-preset', 'veryfast', + '-crf', '20', + '-c:a', 'aac', + '-b:a', '192k', + '-pix_fmt', 'yuv420p', + '-movflags', '+faststart', + outputPath, + ]); + } else { + const videoStart = visualDuration === clipLength + ? 0 + : Math.random() * (visualDuration - clipLength); + const audioStart = audioDuration === clipLength + ? 0 + : Math.random() * (audioDuration - clipLength); + + await runProcess('ffmpeg', [ + '-y', + '-ss', videoStart.toFixed(3), + '-i', visualPath, + '-ss', audioStart.toFixed(3), + '-i', audioPath, + '-loop', '1', + '-i', logoPath, + '-t', clipLength.toFixed(3), + '-filter_complex', buildVideoFilter(), + '-map', '[v]', + '-map', '1:a:0', + '-c:v', 'libx264', + '-preset', 'veryfast', + '-crf', '20', + '-c:a', 'aac', + '-b:a', '192k', + '-pix_fmt', 'yuv420p', + '-movflags', '+faststart', + outputPath, + ]); + } const finalEmbed = buildEmbed( client, message, '<:check:1481811973447356496> clip finished successfully.', [ - { name: 'mp3 source', value: `**${mp3Attachment.name || 'unknown'}**`, inline: true }, - { name: 'video source', value: `**${videoAttachment.name || 'unknown'}**`, inline: true }, + { name: 'audio source', value: `**${mp3Attachment.name || 'unknown'}**`, inline: true }, + { name: 'visual source', value: `**${visualAttachment.name || 'unknown'}**`, inline: true }, { name: 'clip length', value: `**${clipLength.toFixed(2)}s**`, inline: true }, ], ); @@ -227,6 +346,10 @@ module.exports = { files: [{ attachment: outputPath, name: 'snippet.mp4' }], }); } catch (err) { + const errorMessage = getErrorMessage(err); + + logger.error('[snippet] failed to build clip', err instanceof Error ? err : new Error(String(err))); + await sent.edit({ content: null, embeds: [buildEmbed( @@ -234,10 +357,12 @@ module.exports = { message, '<:no:1481811955592466492> failed to build the clip.', [ - { name: 'reason', value: `**${err.message || 'unknown error'}**` }, + { name: 'reason', value: `**${errorMessage.slice(0, 1000)}**` }, ], )], - }).catch(() => null); + }).catch((editErr) => { + logger.error('[snippet] failed to edit error reply', editErr instanceof Error ? editErr : new Error(String(editErr))); + }); } finally { await fsp.rm(tempDir, { recursive: true, force: true }).catch(() => null); }