(frames: Buffer[], fps = TG_STICKER_FPS)
| 946 | let imageBuffer = buffer; |
| 947 | const isVideoLike = kind === "animation" || kind === "video" || kind === "round" || looksLikeAnimatedEmoji(buffer); |
| 948 | if (isVideoLike) { |
| 949 | const converted = await convertAnimatedEmojiToPng(buffer); |
| 950 | if (converted) imageBuffer = converted; |
| 951 | } else if (kind === "sticker") { |
| 952 | imageBuffer = await (await getSharp())(buffer, { animated: false }).ensureAlpha().png({ force: true }).toBuffer(); |
| 953 | } |
| 954 | const { createCanvas, loadImage } = await getCanvas(); |
| 955 | const img = await loadImage(imageBuffer); |
| 956 | const canvas = createCanvas(img.width, img.height); |
| 957 | const ctx = canvas.getContext("2d"); |
| 958 | ctx.clearRect(0, 0, canvas.width, canvas.height); |
| 959 | ctx.drawImage(img, 0, 0); |
| 960 | return canvas; |
| 961 | } catch (err: any) { |
| 962 | console.warn("quote media canvas failed", kind, err?.message || err); |
| 963 | return undefined; |
| 964 | } |
| 965 | } |
| 966 | |
| 967 | async function prepareQuoteMedia(msg: Api.Message, args: QuoteArgs): Promise<{ |
| 968 | mediaBuffer?: Buffer; |
| 969 | mediaCanvas?: any; |
| 970 | mediaType?: string; |
| 971 | mediaMaxSize?: number; |
| 972 | mediaCrop?: boolean; |
| 973 | mediaDuration?: number; |
| 974 | voice?: { waveform: number[]; duration?: number }; |
| 975 | /** quote-api attachments.js: file_name / file_size */ |
| 976 | document?: { file_name: string; file_size?: number }; |
| 977 | /** quote-api attachments.js: title / performer / duration / thumb */ |
| 978 | audio?: { title?: string; performer?: string; duration?: number; thumb?: any }; |
| 979 | }> { |
| 980 | const kind = getMediaKind(msg); |
| 981 | const waveform = kind === "voice" ? voiceWaveform(msg) : undefined; |
| 982 | const voiceAttr = audioAttribute(msg); |
| 983 | const duration = Number(voiceAttr?.duration ?? voiceAttr?.voiceDuration ?? 0) || undefined; |
| 984 | const videoAttr = getDocumentAttributes(msg).find((a: any) => |
| 985 | (a.className || a.constructor?.name || "").includes("Video") |
| 986 | ); |
| 987 | const mediaDuration = |
| 988 | kind === "video" || kind === "animation" || kind === "round" |
| 989 | ? Number(videoAttr?.duration ?? 0) || undefined |
| 990 | : kind === "voice" || kind === "audio" |
| 991 | ? duration |
| 992 | : undefined; |
| 993 | |
| 994 | // Glass: voice/document/audio → in-bubble rows; photo/sticker/video/gif → mediaCanvas + badges |
no test coverage detected