UNPKG

panaiscard

Version:

A powerful Node.js package to generate stunning music cards for Discord bots

569 lines (561 loc) 24.1 kB
// node_modules/tsup/assets/esm_shims.js import { fileURLToPath } from "url"; import path from "path"; var getFilename = () => fileURLToPath(import.meta.url); var getDirname = () => path.dirname(getFilename()); var __dirname = /* @__PURE__ */ getDirname(); // src/themes/classic.ts import { createCanvas, loadImage } from "@napi-rs/canvas"; import { cropImage } from "cropify"; // src/functions/generateSvg.ts var generateSvg = (svgContent) => { return `data:image/svg+xml;base64,${Buffer.from(svgContent).toString("base64")}`; }; // src/functions/registerFont.ts import { GlobalFonts } from "@napi-rs/canvas"; import fs from "node:fs"; import path2 from "node:path"; function registerFont(fontPath, fontName) { const rootFontsPath = path2.join(__dirname, "../fonts", fontPath); if (fs.existsSync(rootFontsPath)) { GlobalFonts.registerFromPath(rootFontsPath, fontName); } else { const srcFontsPath = path2.join(__dirname, "../fonts", fontPath); if (fs.existsSync(srcFontsPath)) { GlobalFonts.registerFromPath(srcFontsPath, fontName); } else { throw new Error(`Font file not found at ${rootFontsPath} or ${srcFontsPath}`); } } } // src/themes/classic.ts registerFont("PlusJakartaSans-Bold.ttf", "bold"); registerFont("PlusJakartaSans-ExtraBold.ttf", "extrabold"); registerFont("PlusJakartaSans-ExtraLight.ttf", "extralight"); registerFont("PlusJakartaSans-Light.ttf", "light"); registerFont("PlusJakartaSans-Medium.ttf", "medium"); registerFont("PlusJakartaSans-Regular.ttf", "regular"); registerFont("PlusJakartaSans-SemiBold.ttf", "semibold"); var Classic = async (option) => { if (!option.progress) option.progress = 3; if (!option.title) option.title = "Panaiscard"; if (!option.author) option.author = "By LucasB25"; if (!option.startTime) option.startTime = "0:00"; if (!option.endTime) option.endTime = "0:00"; if (!option.progressBarColor) option.progressBarColor = "#5F2D00"; if (!option.progressColor) option.progressColor = "#FF7A00"; if (!option.backgroundColor) option.backgroundColor = "#070707"; if (!option.titleColor) option.titleColor = "#FF7A00"; if (!option.authorColor) option.authorColor = "#FFFFFF"; if (!option.timeColor) option.timeColor = "#FFFFFF"; if (!option.imageDarkness) option.imageDarkness = 10; const noImageSvg = generateSvg(`<svg width="837" height="837" viewBox="0 0 837 837" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect width="837" height="837" fill="${option.progressColor}"/> <path d="M419.324 635.912C406.035 635.912 394.658 631.18 385.195 621.717C375.732 612.254 371 600.878 371 587.589C371 574.3 375.732 562.923 385.195 553.46C394.658 543.997 406.035 539.265 419.324 539.265C432.613 539.265 443.989 543.997 453.452 553.46C462.915 562.923 467.647 574.3 467.647 587.589C467.647 600.878 462.915 612.254 453.452 621.717C443.989 631.18 432.613 635.912 419.324 635.912ZM371 490.941V201H467.647V490.941H371Z" fill="${option.backgroundColor}"/> </svg>`); if (!option.thumbnailImage) { option.thumbnailImage = noImageSvg; } let thumbnail; try { thumbnail = await loadImage( await cropImage({ imagePath: option.thumbnailImage, borderRadius: 50, width: 837, height: 837, cropCenter: true }) ); } catch { thumbnail = await loadImage( await cropImage({ imagePath: noImageSvg, borderRadius: 50, width: 837, height: 837, cropCenter: true }) ); } if (option.progress > 100) { option.progress = 100; } if (option.imageDarkness < 0) { option.imageDarkness = 0; } else if (option.imageDarkness > 100) { option.imageDarkness = 100; } if (option.title.length > 18) { option.title = `${option.title.slice(0, 18)}...`; } if (option.author.length > 18) { option.author = `${option.author.slice(0, 18)}...`; } try { const canvas = createCanvas(2458, 837); const ctx = canvas.getContext("2d"); if (option.backgroundImage) { try { const darknessSvg = generateSvg(`<svg width="1568" height="837" viewBox="0 0 1568 837" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect width="1568" height="512" rx="50" fill="#070707" fill-opacity="${option.imageDarkness / 100}"/> <rect y="565" width="1568" height="272" rx="50" fill="#070707" fill-opacity="${option.imageDarkness / 100}"/> </svg>`); const image = await cropImage({ imagePath: option.backgroundImage, width: 1568, height: 837, cropCenter: true }); await cropImage({ // @ts-ignore imagePath: image, x: 0, y: -170, width: 1568, height: 512, borderRadius: 50 }).then(async (x) => { ctx.drawImage(await loadImage(x), 0, 0); }); await cropImage({ // @ts-ignore imagePath: image, x: 0, y: -845, width: 1568, height: 272, borderRadius: 50 }).then(async (x) => { ctx.drawImage(await loadImage(x), 0, 565); }); ctx.drawImage(await loadImage(darknessSvg), 0, 0); } catch (err) { const backgroundSvg = generateSvg(`<svg width="2458" height="837" viewBox="0 0 2458 837" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect width="1568" height="512" rx="50" fill="${option.backgroundColor}"/> <rect y="565" width="1568" height="272" rx="50" fill="${option.backgroundColor}"/> </svg>`); const background = await loadImage(backgroundSvg); ctx.drawImage(background, 0, 0); } } else { const backgroundSvg = generateSvg(`<svg width="2458" height="837" viewBox="0 0 2458 837" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect width="1568" height="512" rx="50" fill="${option.backgroundColor}"/> <rect y="565" width="1568" height="272" rx="50" fill="${option.backgroundColor}"/> </svg>`); const background = await loadImage(backgroundSvg); ctx.drawImage(background, 0, 0); } ctx.drawImage(thumbnail, 1621, 0); const completed = 1342 * option.progress / 100; const progressBarSvg = generateSvg(`<svg width="1342" height="76" viewBox="0 0 1342 76" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect y="13" width="1342" height="47" rx="25" fill="${option.progressBarColor}"/> <rect y="13" width="${completed}" height="47" rx="25" fill="${option.progressColor}"/> <rect x="${completed - 40}" y="3" width="69.4422" height="69.4422" rx="34.7211" fill="${option.progressColor}" stroke="${option.backgroundColor}" stroke-width="6"/> </svg>`); const progressBar = await loadImage(progressBarSvg); ctx.drawImage(progressBar, 113, 635); ctx.fillStyle = `${option.titleColor}`; ctx.font = "124px extrabold"; ctx.shadowColor = "rgba(0, 0, 0, 0.7)"; ctx.shadowBlur = 10; ctx.fillText(option.title, 113, 230); ctx.fillStyle = `${option.authorColor}`; ctx.font = "87px regular"; ctx.shadowColor = "rgba(0, 0, 0, 0.7)"; ctx.shadowBlur = 10; ctx.fillText(option.author, 113, 370); ctx.fillStyle = `${option.timeColor}`; ctx.font = "50px semibold"; ctx.fillText(option.startTime, 113, 768); ctx.fillStyle = `${option.timeColor}`; ctx.font = "50px semibold"; ctx.fillText(option.endTime, 1332, 768); return canvas.toBuffer("image/png"); } catch (e) { throw new Error(e.message); } }; // src/themes/classicpro.ts import { createCanvas as createCanvas2, loadImage as loadImage2 } from "@napi-rs/canvas"; import { cropImage as cropImage2 } from "cropify"; registerFont("PlusJakartaSans-Bold.ttf", "bold"); registerFont("PlusJakartaSans-ExtraBold.ttf", "extrabold"); registerFont("PlusJakartaSans-ExtraLight.ttf", "extralight"); registerFont("PlusJakartaSans-Light.ttf", "light"); registerFont("PlusJakartaSans-Medium.ttf", "medium"); registerFont("PlusJakartaSans-Regular.ttf", "regular"); registerFont("PlusJakartaSans-SemiBold.ttf", "semibold"); var ClassicPro = async (option) => { if (!option.progress) option.progress = 3.6; if (!option.title) option.title = "Panaiscard"; if (!option.author) option.author = "By LucasB25"; if (!option.startTime) option.startTime = "0:00"; if (!option.endTime) option.endTime = "0:00"; if (!option.progressBarColor) option.progressBarColor = "#5F2D00"; if (!option.progressColor) option.progressColor = "#FF7A00"; if (!option.backgroundColor) option.backgroundColor = "#070707"; if (!option.titleColor) option.titleColor = "#FF7A00"; if (!option.authorColor) option.authorColor = "#FFFFFF"; if (!option.timeColor) option.timeColor = "#FFFFFF"; if (!option.imageDarkness) option.imageDarkness = 10; const noImageSvg = generateSvg(`<svg width="837" height="837" viewBox="0 0 837 837" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect width="837" height="837" fill="${option.progressColor}"/> <path d="M419.324 635.912C406.035 635.912 394.658 631.18 385.195 621.717C375.732 612.254 371 600.878 371 587.589C371 574.3 375.732 562.923 385.195 553.46C394.658 543.997 406.035 539.265 419.324 539.265C432.613 539.265 443.989 543.997 453.452 553.46C462.915 562.923 467.647 574.3 467.647 587.589C467.647 600.878 462.915 612.254 453.452 621.717C443.989 631.18 432.613 635.912 419.324 635.912ZM371 490.941V201H467.647V490.941H371Z" fill="${option.backgroundColor}"/> </svg>`); if (!option.thumbnailImage) { option.thumbnailImage = noImageSvg; } let thumbnail; try { thumbnail = await loadImage2( await cropImage2({ imagePath: option.thumbnailImage, borderRadius: 50, width: 331, height: 331, cropCenter: true }) ); } catch { thumbnail = await loadImage2( await cropImage2({ imagePath: noImageSvg, borderRadius: 50, width: 331, height: 331, cropCenter: true }) ); } if (option.progress > 100) { option.progress = 100; } if (option.title.length > 12) { option.title = `${option.title.slice(0, 12)}...`; } if (option.author.length > 12) { option.author = `${option.author.slice(0, 12)}...`; } try { const canvas = createCanvas2(1252, 708); const ctx = canvas.getContext("2d"); if (option.backgroundImage) { try { const darknessSvg = generateSvg(`<svg width="1252" height="708" viewBox="0 0 1252 708" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect width="1252" height="708" rx="50" fill="url(#grad1)" fill-opacity="${option.imageDarkness / 100}"/> <defs> <linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%"> <stop offset="0%" style="stop-color:${option.backgroundColor};stop-opacity:1" /> <stop offset="100%" style="stop-color:#000000;stop-opacity:0.6" /> </linearGradient> </defs> </svg>`); const image = await cropImage2({ imagePath: option.backgroundImage, width: 1252, height: 708, borderRadius: 50, cropCenter: true }); ctx.drawImage(await loadImage2(image), 0, 0); ctx.drawImage(await loadImage2(darknessSvg), 0, 0); } catch (error) { const backgroundSvg = generateSvg(`<svg width="1252" height="708" viewBox="0 0 1252 708" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect width="1252" height="708" rx="50" fill="${option.backgroundColor}"/> </svg>`); const background = await loadImage2(backgroundSvg); ctx.drawImage(background, 0, 0); } } else { const backgroundSvg = generateSvg(`<svg width="1252" height="708" viewBox="0 0 1252 708" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect width="1252" height="708" rx="50" fill="${option.backgroundColor}"/> </svg>`); const background = await loadImage2(backgroundSvg); ctx.drawImage(background, 0, 0); } ctx.drawImage(thumbnail, 87, 91); const completed = 1083 * option.progress / 100; const progressBarSvg = generateSvg(`<svg width="1083" height="77" viewBox="0 0 1083 77" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect y="19" width="1083" height="40" rx="20" fill="${option.progressBarColor}"/> <rect y="19" width="${completed}" height="40" rx="20" fill="${option.progressColor}"/> <rect x="${completed - 40}" y="5" width="65" height="65" rx="35.5" fill="${option.progressColor}" stroke="${option.backgroundColor}" stroke-width="5"/> </svg>`); const progressBar = await loadImage2(progressBarSvg); ctx.drawImage(progressBar, 87, 490); ctx.fillStyle = `${option.titleColor}`; ctx.font = "90px extrabold"; ctx.shadowColor = "rgba(0, 0, 0, 0.7)"; ctx.shadowBlur = 10; ctx.fillText(option.title, 486, 240); ctx.fillStyle = `${option.authorColor}`; ctx.font = "60px regular"; ctx.shadowColor = "rgba(0, 0, 0, 0.7)"; ctx.shadowBlur = 10; ctx.fillText(option.author, 486, 330); ctx.fillStyle = `${option.timeColor}`; ctx.font = "40px semibold"; ctx.fillText(option.startTime, 85, 630); ctx.fillStyle = `${option.timeColor}`; ctx.font = "40px semibold"; ctx.fillText(option.endTime, 1070, 630); return canvas.toBuffer("image/png"); } catch (e) { throw new Error(e.message); } }; // src/themes/dynamic.ts import { createCanvas as createCanvas3, loadImage as loadImage3 } from "@napi-rs/canvas"; import { cropImage as cropImage3 } from "cropify"; registerFont("PlusJakartaSans-Bold.ttf", "bold"); registerFont("PlusJakartaSans-ExtraBold.ttf", "extrabold"); registerFont("PlusJakartaSans-ExtraLight.ttf", "extralight"); registerFont("PlusJakartaSans-Light.ttf", "light"); registerFont("PlusJakartaSans-Medium.ttf", "medium"); registerFont("PlusJakartaSans-Regular.ttf", "regular"); registerFont("PlusJakartaSans-SemiBold.ttf", "semibold"); var Dynamic = async (option) => { if (!option.progress) option.progress = 0.618; if (!option.title) option.title = "Panaiscard"; if (!option.author) option.author = "By LucasB25"; if (!option.progressBarColor) option.progressBarColor = "#5F2D00"; if (!option.progressColor) option.progressColor = "#FF7A00"; if (!option.backgroundColor) option.backgroundColor = "#070707"; if (!option.titleColor) option.titleColor = "#FF7A00"; if (!option.authorColor) option.authorColor = "#FFFFFF"; if (!option.imageDarkness) option.imageDarkness = 10; const noImageSvg = generateSvg(`<svg width="837" height="837" viewBox="0 0 837 837" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect width="837" height="837" fill="${option.progressColor}"/> <path d="M419.324 635.912C406.035 635.912 394.658 631.18 385.195 621.717C375.732 612.254 371 600.878 371 587.589C371 574.3 375.732 562.923 385.195 553.46C394.658 543.997 406.035 539.265 419.324 539.265C432.613 539.265 443.989 543.997 453.452 553.46C462.915 562.923 467.647 574.3 467.647 587.589C467.647 600.878 462.915 612.254 453.452 621.717C443.989 631.18 432.613 635.912 419.324 635.912ZM371 490.941V201H467.647V490.941H371Z" fill="${option.backgroundColor}"/> </svg>`); if (!option.thumbnailImage) { option.thumbnailImage = noImageSvg; } let thumbnail; try { thumbnail = await loadImage3( await cropImage3({ imagePath: option.thumbnailImage, borderRadius: 210, width: 400, height: 400, cropCenter: true }) ); } catch { thumbnail = await loadImage3( await cropImage3({ imagePath: noImageSvg, borderRadius: 210, width: 400, height: 400, cropCenter: true }) ); } if (option.progress >= 100) { option.progress = 99.999; } if (option.title.length > 20) { option.title = `${option.title.slice(0, 20)}...`; } if (option.author.length > 20) { option.author = `${option.author.slice(0, 20)}...`; } try { const canvas = createCanvas3(2367, 520); const ctx = canvas.getContext("2d"); if (option.backgroundImage) { try { const darknessSvg = generateSvg(`<svg width="2367" height="520" viewBox="0 0 2367 520" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M0 0H2367V520H0V0Z" fill="#070707" fill-opacity="${option.imageDarkness / 100}"/> </svg>`); const image = await cropImage3({ imagePath: option.backgroundImage, width: 2367, height: 520, borderRadius: 270, cropCenter: true }); const darkImage = await cropImage3({ imagePath: darknessSvg, width: 2367, height: 520, borderRadius: 270, cropCenter: true }); ctx.drawImage(await loadImage3(image), 0, 0); ctx.drawImage(await loadImage3(darkImage), 0, 0); } catch (error) { const backgroundSvg = generateSvg(`<svg width="2367" height="520" viewBox="0 0 2367 520" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M0 260C0 116.406 116.406 0 260 0H2107C2250.59 0 2367 116.406 2367 260V260C2367 403.594 2250.59 520 2107 520H260C116.406 520 0 403.594 0 260V260Z" fill="${option.backgroundColor}"/> </svg>`); const background = await loadImage3(backgroundSvg); ctx.drawImage(background, 0, 0); } } else { const backgroundSvg = generateSvg(`<svg width="2367" height="520" viewBox="0 0 2367 520" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M0 260C0 116.406 116.406 0 260 0H2107C2250.59 0 2367 116.406 2367 260V260C2367 403.594 2250.59 520 2107 520H260C116.406 520 0 403.594 0 260V260Z" fill="${option.backgroundColor}"/> </svg>`); const background = await loadImage3(backgroundSvg); ctx.drawImage(background, 0, 0); } ctx.drawImage(thumbnail, 69, 61); ctx.beginPath(); ctx.arc(2100, 260, 155, 0, Math.PI * 2, true); ctx.closePath(); ctx.lineWidth = 35; ctx.strokeStyle = `${option.progressBarColor}`; ctx.stroke(); const angle = option.progress / 100 * Math.PI * 2; ctx.beginPath(); ctx.arc(2100, 260, 155, -Math.PI / 2, -Math.PI / 2 + angle, false); ctx.lineWidth = 35; ctx.strokeStyle = option.progressColor; ctx.stroke(); ctx.fillStyle = `${option.titleColor}`; ctx.font = "100px extrabold"; ctx.fillText(option.title, 550, 240); ctx.fillStyle = `${option.authorColor}`; ctx.font = "70px semibold"; ctx.fillText(option.author, 550, 350); return canvas.toBuffer("image/png"); } catch (e) { throw new Error(e.message); } }; // src/themes/card.ts import { createCanvas as createCanvas4, loadImage as loadImage4 } from "@napi-rs/canvas"; import { cropImage as cropImage4 } from "cropify"; registerFont("PlusJakartaSans-Bold.ttf", "bold"); registerFont("PlusJakartaSans-ExtraBold.ttf", "extrabold"); registerFont("PlusJakartaSans-ExtraLight.ttf", "extralight"); registerFont("PlusJakartaSans-Light.ttf", "light"); registerFont("PlusJakartaSans-Medium.ttf", "medium"); registerFont("PlusJakartaSans-Regular.ttf", "regular"); registerFont("PlusJakartaSans-SemiBold.ttf", "semibold"); var Card = async (options) => { if (!options.title) options.title = "Panaiscard"; if (!options.titleColor) options.titleColor = "#d0d5d6"; if (!options.author) options.author = "By LucasB25"; if (!options.authorColor) options.authorColor = "#FFFFFF"; if (!options.trackIndex) options.trackIndex = 1; if (!options.trackIndexTextColor) options.trackIndexTextColor = "#000000"; if (!options.trackIndexBackgroundColor) options.trackIndexBackgroundColor = "#d0d5d6"; if (!options.trackIndexBackgroundRadii && !Array.isArray(options.trackIndexBackgroundRadii)) options.trackIndexBackgroundRadii = 10; if (!options.backgroundColor) options.backgroundColor = "#070707"; if (!options.imageDarkness) options.imageDarkness = 10; const noImageSvg = generateSvg(`<svg width="613" height="837" viewBox="0 0 613 837" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect width="613" height="837" rx="50" fill="${options.backgroundColor}" /> </svg>`); if (!options.thumbnailImage) { options.thumbnailImage = noImageSvg; } let thumbnail; try { thumbnail = await loadImage4( await cropImage4({ //@ts-ignore imagePath: options.thumbnailImage, borderRadius: 20, width: 150, height: 150, cropCenter: true }) ); } catch { thumbnail = await loadImage4( await cropImage4({ imagePath: noImageSvg, borderRadius: 20, width: 150, height: 150, cropCenter: true }) ); } if (options.imageDarkness < 0) { options.imageDarkness = 0; } else if (options.imageDarkness > 100) { options.imageDarkness = 100; } if (options.title.length > 18) { options.title = `${options.title.slice(0, 18)}...`; } if (options.author.length > 19) { options.author = `${options.author.slice(0, 19)}...`; } try { const canvas = createCanvas4(690, 194); const ctx = canvas.getContext("2d"); if (options.backgroundImage) { try { const darknessSvg = generateSvg(`<svg width="690" height="194" viewBox="0 0 690 194" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect width="690" height="194" rx="30" fill="#070707" fill-opacity="${options.imageDarkness / 100}"/> </svg>`); const image = await cropImage4({ // @ts-ignore imagePath: options.backgroundImage, width: 690, height: 194, borderRadius: 35, cropCenter: true }); ctx.drawImage(await loadImage4(image), 0, 0); ctx.drawImage(await loadImage4(darknessSvg), 0, 0); } catch { const backgroundSvg = generateSvg(`<svg width="690" height="194" viewBox="0 0 690 194" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect width="690" height="194" rx="35" fill="${options.backgroundColor}"/> </svg>`); const backgroundColor = await loadImage4(backgroundSvg); ctx.drawImage(backgroundColor, 0, 0); } } else { const backgroundSvg = generateSvg(`<svg width="690" height="194" viewBox="0 0 690 194" fill="none" xmlns="http://www.w3.org/2000/svg"> <rect width="690" height="194" rx="35" fill="${options.backgroundColor}"/> </svg>`); const backgroundColor = await loadImage4(backgroundSvg); ctx.drawImage(backgroundColor, 0, 0); } ctx.shadowColor = "rgba(0, 0, 0, 0.4)"; ctx.shadowBlur = 10; ctx.lineWidth = 4; ctx.strokeStyle = "#ffffff"; ctx.beginPath(); ctx.arc(97, 97, 77, 0, Math.PI * 2); ctx.stroke(); ctx.closePath(); ctx.shadowColor = "transparent"; ctx.drawImage(thumbnail, 22, 22); ctx.font = "33px extrabold"; ctx.fillStyle = options.titleColor; ctx.shadowColor = "rgba(0, 0, 0, 0.3)"; ctx.shadowBlur = 5; ctx.fillText(options.title, 200, canvas.height / 2 - 10); ctx.font = "23px medium"; ctx.fillStyle = options.authorColor; ctx.shadowColor = "rgba(0, 0, 0, 0.3)"; ctx.shadowBlur = 5; ctx.fillText(options.author, 200, canvas.height / 2 + 35); ctx.fillStyle = options.trackIndexBackgroundColor; ctx.beginPath(); ctx.roundRect(canvas.width - 65, canvas.height - 63, 50, 50, options.trackIndexBackgroundRadii); ctx.fill(); ctx.closePath(); ctx.fillStyle = options.trackIndexTextColor; ctx.font = "30px bold"; ctx.shadowColor = "rgba(0, 0, 0, 0.3)"; ctx.shadowBlur = 5; ctx.fillText(options.trackIndex.toString(), canvas.width - 47, canvas.height - 26); return canvas.toBuffer("image/png"); } catch (error) { throw new Error(error.message); } }; export { Card, Classic, ClassicPro, Dynamic };