LOADING
cntoh result: https://files.soonex.biz.id/67421446b7df.jpg
const { createCanvas, loadImage, CanvasRenderingContext2D } = require('canvas');
const axios = require('axios');
const fs = require('fs');
// atur aja config nya di sini
const config = {
title: 'Always Alone',
artist: '#Shn',
coverUrl: 'https://files.soonex.biz.id/cc0e64906c2c.png',
// pakai logo apple langsung
appleIconUrl: 'https://img.icons8.com/ios-glyphs/60/ffffff/mac-os.png',
currentTime: '2:40',
totalTime: '04:11',
progressPercent: 53,
color1: '#7b2cbf', // ungu
color2: '#ff2a5f', // merah ntah apa ini
color3: '#ff7e40', // oren
};
async function generateMusicCard() {
const { title, artist, coverUrl, appleIconUrl, currentTime, totalTime, progressPercent, color1, color2, color3 } = config;
const width = 1200;
const height = 700;
const canvas = createCanvas(width, height);
const ctx = canvas.getContext('2d');
// background gradient meSH
ctx.fillStyle = '#111';
ctx.fillRect(0, 0, width, height);
const grad1 = ctx.createRadialGradient(200, 350, 100, 300, 350, 900);
grad1.addColorStop(0, color1);
grad1.addColorStop(1, 'rgba(0,0,0,0)');
ctx.fillStyle = grad1;
ctx.fillRect(0, 0, width, height);
const grad2 = ctx.createRadialGradient(1000, 600, 50, 900, 500, 800);
grad2.addColorStop(0, color2);
grad2.addColorStop(1, 'rgba(0,0,0,0)');
ctx.fillStyle = grad2;
ctx.fillRect(0, 0, width, height);
const grad3 = ctx.createRadialGradient(1100, 100, 0, 900, 200, 700);
grad3.addColorStop(0, color3);
grad3.addColorStop(1, 'rgba(0,0,0,0)');
ctx.fillStyle = grad3;
ctx.fillRect(0, 0, width, height);
// bagian kartu kaca nya
const cardW = 1000;
const cardH = 460;
const cardX = (width - cardW) / 2;
const cardY = (height - cardH) / 2;
const cardRadius = 36;
ctx.shadowColor = 'rgba(0, 0, 0, 0.4)';
ctx.shadowBlur = 50;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 20;
ctx.beginPath();
ctx.roundRect(cardX, cardY, cardW, cardH, cardRadius);
ctx.fillStyle = 'rgba(28, 28, 30, 0.65)';
ctx.fill();
ctx.shadowColor = 'transparent';
ctx.shadowBlur = 0;
ctx.shadowOffsetY = 0;
ctx.lineWidth = 1.5;
ctx.strokeStyle = 'rgba(255, 255, 255, 0.15)';
ctx.stroke();
// buat get logo apple dan yang artis nya
let coverImg = null;
let appleImg = null;
try {
// download cover nya
const coverRes = await axios.get(coverUrl, {
responseType: 'arraybuffer',
headers: { 'User-Agent': 'Mozilla/5.0' }
});
coverImg = await loadImage(Buffer.from(coverRes.data));
// download logo apple
const appleRes = await axios.get(appleIconUrl, { responseType: 'arraybuffer' });
appleImg = await loadImage(Buffer.from(appleRes.data));
} catch (e) {
console.warn('⚠️ Ada gambar yang gagal didownload:', e.message);
}
// buat yang foto di kiri itu
const albumSize = 360;
const albumX = cardX + 50;
const albumY = cardY + 50;
const albumRadius = 24;
ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';
ctx.shadowBlur = 25;
ctx.shadowOffsetY = 12;
ctx.save();
ctx.beginPath();
ctx.roundRect(albumX, albumY, albumSize, albumSize, albumRadius);
ctx.fillStyle = '#333';
ctx.fill();
ctx.shadowColor = 'transparent';
ctx.shadowBlur = 0;
ctx.shadowOffsetY = 0;
ctx.clip();
if (coverImg) {
ctx.drawImage(coverImg, albumX, albumY, albumSize, albumSize);
} else {
ctx.fillStyle = '#444';
ctx.fillRect(albumX, albumY, albumSize, albumSize);
ctx.fillStyle = '#fff';
ctx.font = '50px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('🎵', albumX + albumSize/2, albumY + albumSize/2);
}
ctx.restore();
// buat keterangan artis, judul, dll
const detailX = albumX + albumSize + 60;
const detailW = cardW - (detailX - cardX) - 50;
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
// letak logo apel ny
const logoY = albumY + 5;
if (appleImg) {
// logo apel Ukuran 20x20
ctx.drawImage(appleImg, detailX, logoY - 2, 20, 20);
// tks msik d sblh ny
ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
ctx.font = '18px "Helvetica Neue", Helvetica, Arial, sans-serif';
ctx.fillText('Music', detailX + 26, logoY);
} else {
// fllbck jka gmbr errr
ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
ctx.font = '18px Arial';
ctx.fillText('Apple Music', detailX, logoY);
}
// tks jdl dn arts
const textY = logoY + 40;
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 42px "Helvetica Neue", Helvetica, Arial, sans-serif';
ctx.fillText(title.substring(0, 30), detailX, textY);
ctx.fillStyle = 'rgba(255, 255, 255, 0.6)';
ctx.font = '26px "Helvetica Neue", Helvetica, Arial, sans-serif';
ctx.fillText(artist.substring(0, 40), detailX, textY + 55);
// tmbl cntrl
const controlY = textY + 160;
const controlCenterX = detailX + (detailW / 2);
drawIcon(ctx, 'prev', controlCenterX - 85, controlY);
drawIcon(ctx, 'pause', controlCenterX, controlY);
drawIcon(ctx, 'next', controlCenterX + 85, controlY);
// prgrs bad
const barY = albumY + albumSize - 25;
const barH = 6;
ctx.beginPath();
ctx.roundRect(detailX, barY, detailW, barH, barH/2);
ctx.fillStyle = 'rgba(255, 255, 255, 0.2)';
ctx.fill();
const fillW = (progressPercent / 100) * detailW;
ctx.beginPath();
ctx.roundRect(detailX, barY, fillW, barH, barH/2);
ctx.fillStyle = '#ffffff';
ctx.fill();
ctx.beginPath();
ctx.arc(detailX + fillW, barY + barH/2, 8, 0, Math.PI * 2);
ctx.fillStyle = '#ffffff';
ctx.fill();
// wktu lgu
const timeY = barY + 18;
ctx.font = '14px "Helvetica Neue", Helvetica, Arial, sans-serif';
ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
ctx.textAlign = 'left';
ctx.fillText(currentTime, detailX, timeY);
ctx.textAlign = 'right';
ctx.fillText('-' + totalTime, detailX + detailW, timeY);
// smpn gmbr
const buffer = canvas.toBuffer('image/png');
fs.writeFileSync('apple_music.png', buffer);
console.log('✅ SELESAI! Gambar Apple & Cover beneran udah muncul sekarang! 🍏🎧');
}
// func pmbntu aj buat gmbr icon ny
function drawIcon(ctx, type, x, y) {
ctx.fillStyle = '#ffffff';
ctx.beginPath();
if (type === 'pause') {
const w = 8, h = 34, gap = 12;
ctx.roundRect(x - gap/2 - w, y - h/2, w, h, 3);
ctx.roundRect(x + gap/2, y - h/2, w, h, 3);
}
else if (type === 'next') {
const size = 22, gap = 18;
ctx.moveTo(x - gap, y - size/2); ctx.lineTo(x, y); ctx.lineTo(x - gap, y + size/2);
ctx.moveTo(x, y - size/2); ctx.lineTo(x + gap, y); ctx.lineTo(x, y + size/2);
ctx.roundRect(x + gap + 2, y - size/2, 4, size, 2);
}
else if (type === 'prev') {
const size = 22, gap = 18;
ctx.roundRect(x - gap - 6, y - size/2, 4, size, 2);
ctx.moveTo(x - gap, y); ctx.lineTo(x, y - size/2); ctx.lineTo(x, y + size/2);
ctx.moveTo(x, y); ctx.lineTo(x + gap, y - size/2); ctx.lineTo(x + gap, y + size/2);
}
ctx.fill();
ctx.closePath();
}
// polyfill roundrect
if (!CanvasRenderingContext2D.prototype.roundRect) {
CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, radii) {
const r = typeof radii === 'number' ? radii : (radii || 0);
this.moveTo(x + r, y);
this.arcTo(x + w, y, x + w, y + h, r);
this.arcTo(x + w, y + h, x, y + h, r);
this.arcTo(x, y + h, x, y, r);
this.arcTo(x, y, x + w, y, r);
this.closePath();
return this;
};
}
generateMusicCard().catch(err => console.error('❌ Error:', err));