fuelcard 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -8
- package/src/index.js +20 -11
- package/src/themes/Aqua.js +5 -9
- package/src/themes/Flame.js +12 -48
- package/src/themes/Fuego.js +6 -14
- package/src/themes/Fuelex.js +11 -44
- package/src/themes/Oscuro.js +5 -10
- package/src/themes/Rosa.js +5 -9
- package/test.js +7 -31
package/package.json
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fuelcard",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "The
|
|
5
|
-
"type": "module",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "The Most Advanced Discord Music Card Generator - By Ramkrishna & ZayDocs",
|
|
6
5
|
"main": "src/index.js",
|
|
7
6
|
"scripts": {
|
|
8
7
|
"test": "node test.js"
|
|
@@ -12,12 +11,11 @@
|
|
|
12
11
|
"music",
|
|
13
12
|
"card",
|
|
14
13
|
"musicard",
|
|
15
|
-
"lavalink",
|
|
16
|
-
"riffy",
|
|
17
14
|
"distube",
|
|
15
|
+
"riffy",
|
|
16
|
+
"lavalink",
|
|
18
17
|
"canvas",
|
|
19
|
-
"image"
|
|
20
|
-
"generator"
|
|
18
|
+
"image"
|
|
21
19
|
],
|
|
22
20
|
"author": "Ramkrishna & ZayDocs",
|
|
23
21
|
"license": "UNLICENSED",
|
|
@@ -28,4 +26,4 @@
|
|
|
28
26
|
"@napi-rs/canvas": "^0.1.83",
|
|
29
27
|
"cropify": "^2.0.1"
|
|
30
28
|
}
|
|
31
|
-
}
|
|
29
|
+
}
|
package/src/index.js
CHANGED
|
@@ -10,16 +10,25 @@
|
|
|
10
10
|
* @private
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
const { Fuego } = require('./themes/Fuego.js');
|
|
14
|
+
const { Aqua } = require('./themes/Aqua.js');
|
|
15
|
+
const { Rosa } = require('./themes/Rosa.js');
|
|
16
|
+
const { Oscuro } = require('./themes/Oscuro.js');
|
|
17
|
+
const { Flame } = require('./themes/Flame.js');
|
|
18
|
+
const { Fuelex } = require('./themes/Fuelex.js');
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
module.exports = {
|
|
21
|
+
// Main themes (like reference cards with mascot)
|
|
22
|
+
Fuego, // Orange/Gold
|
|
23
|
+
Aqua, // Blue/Cyan
|
|
24
|
+
Rosa, // Pink/Magenta
|
|
25
|
+
Oscuro, // Dark with accent
|
|
22
26
|
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
// Premium themes
|
|
28
|
+
Flame, // Fire gradient
|
|
29
|
+
Fuelex, // Wide with mascot
|
|
30
|
+
|
|
31
|
+
// Version info
|
|
32
|
+
version: '1.0.1',
|
|
33
|
+
authors: ['Ramkrishna', 'ZayDocs']
|
|
34
|
+
};
|
package/src/themes/Aqua.js
CHANGED
|
@@ -5,20 +5,16 @@
|
|
|
5
5
|
* ╚═══════════════════════════════════════════════════════════════╝
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import { fileURLToPath } from 'url';
|
|
12
|
-
|
|
13
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
-
const __dirname = path.dirname(__filename);
|
|
8
|
+
const { createCanvas, loadImage } = require('@napi-rs/canvas');
|
|
9
|
+
const { cropImage } = require('cropify');
|
|
10
|
+
const path = require('path');
|
|
15
11
|
|
|
16
12
|
const MASCOT_PATH = path.join(__dirname, '../../assets/mascot.png');
|
|
17
13
|
|
|
18
14
|
/**
|
|
19
15
|
* Generate an Aqua-styled music card (blue/cyan like reference)
|
|
20
16
|
*/
|
|
21
|
-
|
|
17
|
+
const Aqua = async ({
|
|
22
18
|
thumbnail,
|
|
23
19
|
trackName = 'Unknown Track',
|
|
24
20
|
artistName = 'Unknown Artist',
|
|
@@ -153,4 +149,4 @@ export const Aqua = async ({
|
|
|
153
149
|
return finalImage;
|
|
154
150
|
};
|
|
155
151
|
|
|
156
|
-
|
|
152
|
+
module.exports = { Aqua };
|
package/src/themes/Flame.js
CHANGED
|
@@ -5,35 +5,16 @@
|
|
|
5
5
|
* ╚═══════════════════════════════════════════════════════════════╝
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import { fileURLToPath } from 'url';
|
|
8
|
+
const { createCanvas, loadImage } = require('@napi-rs/canvas');
|
|
9
|
+
const { cropImage } = require('cropify');
|
|
10
|
+
const path = require('path');
|
|
12
11
|
|
|
13
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
-
const __dirname = path.dirname(__filename);
|
|
15
|
-
|
|
16
|
-
// Path to mascot image
|
|
17
12
|
const MASCOT_PATH = path.join(__dirname, '../../assets/mascot.png');
|
|
18
13
|
|
|
19
|
-
/**
|
|
20
|
-
* @typedef {Object} FlameOptions
|
|
21
|
-
* @property {string|Buffer} thumbnail - Album art URL or buffer
|
|
22
|
-
* @property {string} [trackName='Unknown Track'] - Song title
|
|
23
|
-
* @property {string} [artistName='Unknown Artist'] - Artist name
|
|
24
|
-
* @property {string} [requester] - Who requested the track
|
|
25
|
-
* @property {number} [progress=0] - Progress percentage (0-100)
|
|
26
|
-
* @property {string} [startTime='0:00'] - Current timestamp
|
|
27
|
-
* @property {string} [endTime='0:00'] - Total duration
|
|
28
|
-
* @property {boolean} [showMascot=true] - Show the Fuelcard mascot
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
14
|
/**
|
|
32
15
|
* Generate a flame-styled music card with optional mascot
|
|
33
|
-
* @param {FlameOptions} options
|
|
34
|
-
* @returns {Promise<Buffer>}
|
|
35
16
|
*/
|
|
36
|
-
|
|
17
|
+
const Flame = async ({
|
|
37
18
|
thumbnail,
|
|
38
19
|
trackName = 'Unknown Track',
|
|
39
20
|
artistName = 'Unknown Artist',
|
|
@@ -46,12 +27,10 @@ export const Flame = async ({
|
|
|
46
27
|
const width = 900;
|
|
47
28
|
const height = 280;
|
|
48
29
|
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
const secondaryColor = '#ffd700'; // Gold
|
|
30
|
+
const primaryColor = '#ff6b00';
|
|
31
|
+
const secondaryColor = '#ffd700';
|
|
52
32
|
const bgColor = '#0a0a0a';
|
|
53
33
|
|
|
54
|
-
// Truncate text
|
|
55
34
|
if (trackName.length > 32) trackName = trackName.substring(0, 29) + '...';
|
|
56
35
|
if (artistName.length > 35) artistName = artistName.substring(0, 32) + '...';
|
|
57
36
|
if (requester && requester.length > 20) requester = requester.substring(0, 17) + '...';
|
|
@@ -59,11 +38,10 @@ export const Flame = async ({
|
|
|
59
38
|
const safeProgress = Math.min(Math.max(progress, 0), 100);
|
|
60
39
|
const progressWidth = (safeProgress / 100) * 450;
|
|
61
40
|
|
|
62
|
-
// Create canvas
|
|
63
41
|
const canvas = createCanvas(width, height);
|
|
64
42
|
const ctx = canvas.getContext('2d');
|
|
65
43
|
|
|
66
|
-
// Dark background
|
|
44
|
+
// Dark background
|
|
67
45
|
const gradient = ctx.createLinearGradient(0, 0, width, height);
|
|
68
46
|
gradient.addColorStop(0, bgColor);
|
|
69
47
|
gradient.addColorStop(0.5, '#111111');
|
|
@@ -86,7 +64,7 @@ export const Flame = async ({
|
|
|
86
64
|
ctx.roundRect(2, 2, width - 4, height - 4, 19);
|
|
87
65
|
ctx.stroke();
|
|
88
66
|
|
|
89
|
-
//
|
|
67
|
+
// Thumbnail
|
|
90
68
|
try {
|
|
91
69
|
const thumbBuffer = await cropImage({
|
|
92
70
|
imagePath: thumbnail,
|
|
@@ -96,7 +74,6 @@ export const Flame = async ({
|
|
|
96
74
|
});
|
|
97
75
|
const thumbImage = await loadImage(thumbBuffer);
|
|
98
76
|
|
|
99
|
-
// Flame glow effect
|
|
100
77
|
ctx.shadowColor = primaryColor;
|
|
101
78
|
ctx.shadowBlur = 20;
|
|
102
79
|
ctx.fillStyle = primaryColor;
|
|
@@ -105,17 +82,14 @@ export const Flame = async ({
|
|
|
105
82
|
ctx.fill();
|
|
106
83
|
ctx.shadowBlur = 0;
|
|
107
84
|
|
|
108
|
-
// Draw thumbnail
|
|
109
85
|
ctx.drawImage(thumbImage, 30, 40, 200, 200);
|
|
110
86
|
|
|
111
|
-
// Gold border
|
|
112
87
|
ctx.strokeStyle = secondaryColor;
|
|
113
88
|
ctx.lineWidth = 3;
|
|
114
89
|
ctx.beginPath();
|
|
115
90
|
ctx.roundRect(30, 40, 200, 200, 20);
|
|
116
91
|
ctx.stroke();
|
|
117
92
|
} catch (e) {
|
|
118
|
-
// Placeholder
|
|
119
93
|
ctx.fillStyle = '#1a1a1a';
|
|
120
94
|
ctx.beginPath();
|
|
121
95
|
ctx.roundRect(30, 40, 200, 200, 20);
|
|
@@ -128,22 +102,18 @@ export const Flame = async ({
|
|
|
128
102
|
ctx.fillText('🔥', 130, 140);
|
|
129
103
|
}
|
|
130
104
|
|
|
131
|
-
//
|
|
105
|
+
// Mascot
|
|
132
106
|
if (showMascot) {
|
|
133
107
|
try {
|
|
134
108
|
const mascotImage = await loadImage(MASCOT_PATH);
|
|
135
|
-
// Draw mascot in bottom right corner
|
|
136
109
|
ctx.drawImage(mascotImage, width - 140, height - 140, 130, 130);
|
|
137
|
-
} catch (e) {
|
|
138
|
-
// Mascot not found, continue without it
|
|
139
|
-
}
|
|
110
|
+
} catch (e) { }
|
|
140
111
|
}
|
|
141
112
|
|
|
142
|
-
// Text
|
|
113
|
+
// Text
|
|
143
114
|
ctx.textAlign = 'left';
|
|
144
115
|
ctx.textBaseline = 'top';
|
|
145
116
|
|
|
146
|
-
// Track name with flame glow
|
|
147
117
|
ctx.shadowColor = primaryColor;
|
|
148
118
|
ctx.shadowBlur = 10;
|
|
149
119
|
ctx.fillStyle = '#ffffff';
|
|
@@ -151,12 +121,10 @@ export const Flame = async ({
|
|
|
151
121
|
ctx.fillText(trackName, 280, 45);
|
|
152
122
|
ctx.shadowBlur = 0;
|
|
153
123
|
|
|
154
|
-
// Artist name
|
|
155
124
|
ctx.fillStyle = '#cccccc';
|
|
156
125
|
ctx.font = '20px Arial';
|
|
157
126
|
ctx.fillText(artistName, 280, 85);
|
|
158
127
|
|
|
159
|
-
// Requester with gold accent
|
|
160
128
|
if (requester) {
|
|
161
129
|
ctx.fillStyle = secondaryColor;
|
|
162
130
|
ctx.font = '16px Arial';
|
|
@@ -169,13 +137,11 @@ export const Flame = async ({
|
|
|
169
137
|
const barWidth = showMascot ? 450 : 520;
|
|
170
138
|
const actualProgressWidth = (safeProgress / 100) * barWidth;
|
|
171
139
|
|
|
172
|
-
// Background
|
|
173
140
|
ctx.fillStyle = 'rgba(255,255,255,0.12)';
|
|
174
141
|
ctx.beginPath();
|
|
175
142
|
ctx.roundRect(progressX, progressY, barWidth, 14, 7);
|
|
176
143
|
ctx.fill();
|
|
177
144
|
|
|
178
|
-
// Flame gradient fill
|
|
179
145
|
if (safeProgress > 0) {
|
|
180
146
|
const fillGradient = ctx.createLinearGradient(progressX, 0, progressX + actualProgressWidth, 0);
|
|
181
147
|
fillGradient.addColorStop(0, primaryColor);
|
|
@@ -186,7 +152,6 @@ export const Flame = async ({
|
|
|
186
152
|
ctx.roundRect(progressX, progressY, actualProgressWidth, 14, 7);
|
|
187
153
|
ctx.fill();
|
|
188
154
|
|
|
189
|
-
// Glowing knob
|
|
190
155
|
ctx.shadowColor = secondaryColor;
|
|
191
156
|
ctx.shadowBlur = 10;
|
|
192
157
|
ctx.fillStyle = '#ffffff';
|
|
@@ -210,7 +175,6 @@ export const Flame = async ({
|
|
|
210
175
|
ctx.textAlign = 'right';
|
|
211
176
|
ctx.fillText('Powered by Fuelcard', width - 20, height - 15);
|
|
212
177
|
|
|
213
|
-
// Crop final image
|
|
214
178
|
const finalImage = await cropImage({
|
|
215
179
|
imagePath: canvas.toBuffer('image/png'),
|
|
216
180
|
width: width,
|
|
@@ -221,4 +185,4 @@ export const Flame = async ({
|
|
|
221
185
|
return finalImage;
|
|
222
186
|
};
|
|
223
187
|
|
|
224
|
-
|
|
188
|
+
module.exports = { Flame };
|
package/src/themes/Fuego.js
CHANGED
|
@@ -5,20 +5,16 @@
|
|
|
5
5
|
* ╚═══════════════════════════════════════════════════════════════╝
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import { fileURLToPath } from 'url';
|
|
12
|
-
|
|
13
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
-
const __dirname = path.dirname(__filename);
|
|
8
|
+
const { createCanvas, loadImage } = require('@napi-rs/canvas');
|
|
9
|
+
const { cropImage } = require('cropify');
|
|
10
|
+
const path = require('path');
|
|
15
11
|
|
|
16
12
|
const MASCOT_PATH = path.join(__dirname, '../../assets/mascot.png');
|
|
17
13
|
|
|
18
14
|
/**
|
|
19
15
|
* Generate a Fuego-styled music card (orange/gold like reference)
|
|
20
16
|
*/
|
|
21
|
-
|
|
17
|
+
const Fuego = async ({
|
|
22
18
|
thumbnail,
|
|
23
19
|
trackName = 'Unknown Track',
|
|
24
20
|
artistName = 'Unknown Artist',
|
|
@@ -29,7 +25,6 @@ export const Fuego = async ({
|
|
|
29
25
|
const width = 600;
|
|
30
26
|
const height = 170;
|
|
31
27
|
|
|
32
|
-
// Truncate text
|
|
33
28
|
if (trackName.length > 22) trackName = trackName.substring(0, 19) + '...';
|
|
34
29
|
if (artistName.length > 28) artistName = artistName.substring(0, 25) + '...';
|
|
35
30
|
|
|
@@ -68,7 +63,6 @@ export const Fuego = async ({
|
|
|
68
63
|
});
|
|
69
64
|
const thumbImage = await loadImage(thumbBuffer);
|
|
70
65
|
|
|
71
|
-
// Shadow behind thumbnail
|
|
72
66
|
ctx.shadowColor = 'rgba(0,0,0,0.3)';
|
|
73
67
|
ctx.shadowBlur = 10;
|
|
74
68
|
ctx.shadowOffsetX = 3;
|
|
@@ -95,12 +89,10 @@ export const Fuego = async ({
|
|
|
95
89
|
ctx.textAlign = 'left';
|
|
96
90
|
ctx.textBaseline = 'top';
|
|
97
91
|
|
|
98
|
-
// Track name (white, bold)
|
|
99
92
|
ctx.fillStyle = '#ffffff';
|
|
100
93
|
ctx.font = 'bold 22px Arial';
|
|
101
94
|
ctx.fillText(trackName, textX, 25);
|
|
102
95
|
|
|
103
|
-
// Artist name (darker)
|
|
104
96
|
ctx.fillStyle = 'rgba(0,0,0,0.6)';
|
|
105
97
|
ctx.font = '16px Arial';
|
|
106
98
|
ctx.fillText(artistName, textX, 55);
|
|
@@ -131,7 +123,7 @@ export const Fuego = async ({
|
|
|
131
123
|
ctx.textAlign = 'right';
|
|
132
124
|
ctx.fillText(endTime, progressX + progressWidth, progressY + 14);
|
|
133
125
|
|
|
134
|
-
// Draw mascot
|
|
126
|
+
// Draw mascot
|
|
135
127
|
try {
|
|
136
128
|
const mascotImage = await loadImage(MASCOT_PATH);
|
|
137
129
|
const mascotHeight = 190;
|
|
@@ -152,4 +144,4 @@ export const Fuego = async ({
|
|
|
152
144
|
return finalImage;
|
|
153
145
|
};
|
|
154
146
|
|
|
155
|
-
|
|
147
|
+
module.exports = { Fuego };
|
package/src/themes/Fuelex.js
CHANGED
|
@@ -5,35 +5,16 @@
|
|
|
5
5
|
* ╚═══════════════════════════════════════════════════════════════╝
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import { fileURLToPath } from 'url';
|
|
8
|
+
const { createCanvas, loadImage } = require('@napi-rs/canvas');
|
|
9
|
+
const { cropImage } = require('cropify');
|
|
10
|
+
const path = require('path');
|
|
12
11
|
|
|
13
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
-
const __dirname = path.dirname(__filename);
|
|
15
|
-
|
|
16
|
-
// Path to mascot image
|
|
17
12
|
const MASCOT_PATH = path.join(__dirname, '../../assets/mascot.png');
|
|
18
13
|
|
|
19
|
-
/**
|
|
20
|
-
* @typedef {Object} FuelexOptions
|
|
21
|
-
* @property {string|Buffer} thumbnail - Album art URL or buffer
|
|
22
|
-
* @property {string} [trackName='Unknown Track'] - Song title
|
|
23
|
-
* @property {string} [artistName='Unknown Artist'] - Artist name
|
|
24
|
-
* @property {number} [progress=0] - Progress percentage (0-100)
|
|
25
|
-
* @property {string} [startTime='0:00'] - Current timestamp
|
|
26
|
-
* @property {string} [endTime='0:00'] - Total duration
|
|
27
|
-
* @property {string} [backgroundColor='#1a1a2e'] - Card background color
|
|
28
|
-
* @property {string} [accentColor='#ff6b00'] - Accent color
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
14
|
/**
|
|
32
15
|
* Generate a Fuelex-styled music card with the mascot character
|
|
33
|
-
* @param {FuelexOptions} options
|
|
34
|
-
* @returns {Promise<Buffer>}
|
|
35
16
|
*/
|
|
36
|
-
|
|
17
|
+
const Fuelex = async ({
|
|
37
18
|
thumbnail,
|
|
38
19
|
trackName = 'Unknown Track',
|
|
39
20
|
artistName = 'Unknown Artist',
|
|
@@ -43,21 +24,18 @@ export const Fuelex = async ({
|
|
|
43
24
|
backgroundColor = '#1a1a2e',
|
|
44
25
|
accentColor = '#ff6b00'
|
|
45
26
|
}) => {
|
|
46
|
-
// Card dimensions (similar to reference)
|
|
47
27
|
const width = 900;
|
|
48
28
|
const height = 200;
|
|
49
29
|
|
|
50
|
-
// Truncate text
|
|
51
30
|
if (trackName.length > 28) trackName = trackName.substring(0, 25) + '...';
|
|
52
31
|
if (artistName.length > 35) artistName = artistName.substring(0, 32) + '...';
|
|
53
32
|
|
|
54
33
|
const safeProgress = Math.min(Math.max(progress, 0), 100);
|
|
55
34
|
|
|
56
|
-
// Create canvas
|
|
57
35
|
const canvas = createCanvas(width, height);
|
|
58
36
|
const ctx = canvas.getContext('2d');
|
|
59
37
|
|
|
60
|
-
// Background gradient
|
|
38
|
+
// Background gradient
|
|
61
39
|
const bgGradient = ctx.createLinearGradient(0, 0, width, 0);
|
|
62
40
|
bgGradient.addColorStop(0, backgroundColor);
|
|
63
41
|
bgGradient.addColorStop(0.7, '#0d0d1a');
|
|
@@ -80,7 +58,7 @@ export const Fuelex = async ({
|
|
|
80
58
|
ctx.roundRect(2, 2, width - 4, height - 4, 14);
|
|
81
59
|
ctx.stroke();
|
|
82
60
|
|
|
83
|
-
//
|
|
61
|
+
// Thumbnail
|
|
84
62
|
const thumbSize = 140;
|
|
85
63
|
const thumbX = 25;
|
|
86
64
|
const thumbY = (height - thumbSize) / 2;
|
|
@@ -94,20 +72,17 @@ export const Fuelex = async ({
|
|
|
94
72
|
});
|
|
95
73
|
const thumbImage = await loadImage(thumbBuffer);
|
|
96
74
|
|
|
97
|
-
// Glow effect behind thumbnail
|
|
98
75
|
ctx.shadowColor = accentColor;
|
|
99
76
|
ctx.shadowBlur = 15;
|
|
100
77
|
ctx.drawImage(thumbImage, thumbX, thumbY, thumbSize, thumbSize);
|
|
101
78
|
ctx.shadowBlur = 0;
|
|
102
79
|
|
|
103
|
-
// Border on thumbnail
|
|
104
80
|
ctx.strokeStyle = accentColor;
|
|
105
81
|
ctx.lineWidth = 2;
|
|
106
82
|
ctx.beginPath();
|
|
107
83
|
ctx.roundRect(thumbX, thumbY, thumbSize, thumbSize, 15);
|
|
108
84
|
ctx.stroke();
|
|
109
85
|
} catch (e) {
|
|
110
|
-
// Placeholder
|
|
111
86
|
ctx.fillStyle = '#2a2a4e';
|
|
112
87
|
ctx.beginPath();
|
|
113
88
|
ctx.roundRect(thumbX, thumbY, thumbSize, thumbSize, 15);
|
|
@@ -120,7 +95,7 @@ export const Fuelex = async ({
|
|
|
120
95
|
ctx.fillText('♪', thumbX + thumbSize / 2, thumbY + thumbSize / 2);
|
|
121
96
|
}
|
|
122
97
|
|
|
123
|
-
//
|
|
98
|
+
// Mascot (large)
|
|
124
99
|
try {
|
|
125
100
|
const mascotImage = await loadImage(MASCOT_PATH);
|
|
126
101
|
const mascotWidth = 220;
|
|
@@ -129,18 +104,15 @@ export const Fuelex = async ({
|
|
|
129
104
|
const mascotY = height - mascotHeight + 20;
|
|
130
105
|
|
|
131
106
|
ctx.drawImage(mascotImage, mascotX, mascotY, mascotWidth, mascotHeight);
|
|
132
|
-
} catch (e) {
|
|
133
|
-
// Mascot not available
|
|
134
|
-
}
|
|
107
|
+
} catch (e) { }
|
|
135
108
|
|
|
136
|
-
// Text
|
|
109
|
+
// Text
|
|
137
110
|
const textX = thumbX + thumbSize + 25;
|
|
138
111
|
const textMaxWidth = 380;
|
|
139
112
|
|
|
140
113
|
ctx.textAlign = 'left';
|
|
141
114
|
ctx.textBaseline = 'top';
|
|
142
115
|
|
|
143
|
-
// Track name with glow
|
|
144
116
|
ctx.shadowColor = accentColor;
|
|
145
117
|
ctx.shadowBlur = 8;
|
|
146
118
|
ctx.fillStyle = '#ffffff';
|
|
@@ -148,7 +120,6 @@ export const Fuelex = async ({
|
|
|
148
120
|
ctx.fillText(trackName, textX, 30, textMaxWidth);
|
|
149
121
|
ctx.shadowBlur = 0;
|
|
150
122
|
|
|
151
|
-
// Artist name
|
|
152
123
|
ctx.fillStyle = '#cccccc';
|
|
153
124
|
ctx.font = '18px Arial';
|
|
154
125
|
ctx.fillText(artistName, textX, 68, textMaxWidth);
|
|
@@ -160,13 +131,11 @@ export const Fuelex = async ({
|
|
|
160
131
|
const progressHeight = 10;
|
|
161
132
|
const actualProgressWidth = (safeProgress / 100) * progressWidth;
|
|
162
133
|
|
|
163
|
-
// Background bar
|
|
164
134
|
ctx.fillStyle = 'rgba(255,255,255,0.15)';
|
|
165
135
|
ctx.beginPath();
|
|
166
136
|
ctx.roundRect(progressX, progressY, progressWidth, progressHeight, 5);
|
|
167
137
|
ctx.fill();
|
|
168
138
|
|
|
169
|
-
// Progress fill with gradient
|
|
170
139
|
if (safeProgress > 0) {
|
|
171
140
|
const progressGradient = ctx.createLinearGradient(progressX, 0, progressX + actualProgressWidth, 0);
|
|
172
141
|
progressGradient.addColorStop(0, accentColor);
|
|
@@ -177,7 +146,6 @@ export const Fuelex = async ({
|
|
|
177
146
|
ctx.roundRect(progressX, progressY, actualProgressWidth, progressHeight, 5);
|
|
178
147
|
ctx.fill();
|
|
179
148
|
|
|
180
|
-
// Knob
|
|
181
149
|
ctx.fillStyle = '#ffffff';
|
|
182
150
|
ctx.beginPath();
|
|
183
151
|
ctx.arc(progressX + actualProgressWidth, progressY + progressHeight / 2, 7, 0, Math.PI * 2);
|
|
@@ -192,13 +160,12 @@ export const Fuelex = async ({
|
|
|
192
160
|
ctx.textAlign = 'right';
|
|
193
161
|
ctx.fillText(endTime, progressX + progressWidth, progressY + 18);
|
|
194
162
|
|
|
195
|
-
//
|
|
163
|
+
// Branding
|
|
196
164
|
ctx.fillStyle = '#555555';
|
|
197
165
|
ctx.font = 'bold 11px Arial';
|
|
198
166
|
ctx.textAlign = 'left';
|
|
199
167
|
ctx.fillText('FUELCARD', textX, height - 22);
|
|
200
168
|
|
|
201
|
-
// Crop with rounded corners
|
|
202
169
|
const finalImage = await cropImage({
|
|
203
170
|
imagePath: canvas.toBuffer('image/png'),
|
|
204
171
|
width: width,
|
|
@@ -209,4 +176,4 @@ export const Fuelex = async ({
|
|
|
209
176
|
return finalImage;
|
|
210
177
|
};
|
|
211
178
|
|
|
212
|
-
|
|
179
|
+
module.exports = { Fuelex };
|
package/src/themes/Oscuro.js
CHANGED
|
@@ -5,20 +5,16 @@
|
|
|
5
5
|
* ╚═══════════════════════════════════════════════════════════════╝
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import { fileURLToPath } from 'url';
|
|
12
|
-
|
|
13
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
-
const __dirname = path.dirname(__filename);
|
|
8
|
+
const { createCanvas, loadImage } = require('@napi-rs/canvas');
|
|
9
|
+
const { cropImage } = require('cropify');
|
|
10
|
+
const path = require('path');
|
|
15
11
|
|
|
16
12
|
const MASCOT_PATH = path.join(__dirname, '../../assets/mascot.png');
|
|
17
13
|
|
|
18
14
|
/**
|
|
19
15
|
* Generate an Oscuro-styled music card (dark like reference)
|
|
20
16
|
*/
|
|
21
|
-
|
|
17
|
+
const Oscuro = async ({
|
|
22
18
|
thumbnail,
|
|
23
19
|
trackName = 'Unknown Track',
|
|
24
20
|
artistName = 'Unknown Artist',
|
|
@@ -74,7 +70,6 @@ export const Oscuro = async ({
|
|
|
74
70
|
ctx.drawImage(thumbImage, thumbX, thumbY, thumbSize, thumbSize);
|
|
75
71
|
ctx.shadowBlur = 0;
|
|
76
72
|
|
|
77
|
-
// Accent border on thumbnail
|
|
78
73
|
ctx.strokeStyle = accentColor;
|
|
79
74
|
ctx.lineWidth = 2;
|
|
80
75
|
ctx.beginPath();
|
|
@@ -154,4 +149,4 @@ export const Oscuro = async ({
|
|
|
154
149
|
return finalImage;
|
|
155
150
|
};
|
|
156
151
|
|
|
157
|
-
|
|
152
|
+
module.exports = { Oscuro };
|
package/src/themes/Rosa.js
CHANGED
|
@@ -5,20 +5,16 @@
|
|
|
5
5
|
* ╚═══════════════════════════════════════════════════════════════╝
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import { fileURLToPath } from 'url';
|
|
12
|
-
|
|
13
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
-
const __dirname = path.dirname(__filename);
|
|
8
|
+
const { createCanvas, loadImage } = require('@napi-rs/canvas');
|
|
9
|
+
const { cropImage } = require('cropify');
|
|
10
|
+
const path = require('path');
|
|
15
11
|
|
|
16
12
|
const MASCOT_PATH = path.join(__dirname, '../../assets/mascot.png');
|
|
17
13
|
|
|
18
14
|
/**
|
|
19
15
|
* Generate a Rosa-styled music card (pink/magenta like reference)
|
|
20
16
|
*/
|
|
21
|
-
|
|
17
|
+
const Rosa = async ({
|
|
22
18
|
thumbnail,
|
|
23
19
|
trackName = 'Unknown Track',
|
|
24
20
|
artistName = 'Unknown Artist',
|
|
@@ -153,4 +149,4 @@ export const Rosa = async ({
|
|
|
153
149
|
return finalImage;
|
|
154
150
|
};
|
|
155
151
|
|
|
156
|
-
|
|
152
|
+
module.exports = { Rosa };
|
package/test.js
CHANGED
|
@@ -5,13 +5,9 @@
|
|
|
5
5
|
* ╚═══════════════════════════════════════════════════════════════╝
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import { fileURLToPath } from 'url';
|
|
12
|
-
|
|
13
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
-
const __dirname = path.dirname(__filename);
|
|
8
|
+
const { Fuego, Aqua, Rosa, Oscuro, Flame, Fuelex } = require('./src/index.js');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
15
11
|
|
|
16
12
|
const outputDir = path.join(__dirname, 'test-output');
|
|
17
13
|
if (!fs.existsSync(outputDir)) {
|
|
@@ -29,11 +25,11 @@ const sampleTrack = {
|
|
|
29
25
|
|
|
30
26
|
async function generateTestCards() {
|
|
31
27
|
console.log('╔═══════════════════════════════════════════════════════════════╗');
|
|
32
|
-
console.log('║ FUELCARD TEST
|
|
28
|
+
console.log('║ FUELCARD v1.0.1 TEST ║');
|
|
33
29
|
console.log('║ By Ramkrishna & ZayDocs ║');
|
|
34
30
|
console.log('╚═══════════════════════════════════════════════════════════════╝');
|
|
35
31
|
console.log();
|
|
36
|
-
console.log('===
|
|
32
|
+
console.log('=== MAIN THEMES (Reference Style) ===');
|
|
37
33
|
console.log();
|
|
38
34
|
|
|
39
35
|
// Fuego (Orange)
|
|
@@ -77,7 +73,7 @@ async function generateTestCards() {
|
|
|
77
73
|
}
|
|
78
74
|
|
|
79
75
|
console.log();
|
|
80
|
-
console.log('===
|
|
76
|
+
console.log('=== PREMIUM THEMES ===');
|
|
81
77
|
console.log();
|
|
82
78
|
|
|
83
79
|
// Fuelex
|
|
@@ -90,26 +86,6 @@ async function generateTestCards() {
|
|
|
90
86
|
console.error(' ✗ Error:', error.message);
|
|
91
87
|
}
|
|
92
88
|
|
|
93
|
-
// Classic
|
|
94
|
-
console.log('[TEST] Generating Classic theme...');
|
|
95
|
-
try {
|
|
96
|
-
const card = await Classic(sampleTrack);
|
|
97
|
-
fs.writeFileSync(path.join(outputDir, 'classic-theme.png'), card);
|
|
98
|
-
console.log(' ✓ Saved: classic-theme.png');
|
|
99
|
-
} catch (error) {
|
|
100
|
-
console.error(' ✗ Error:', error.message);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Neon
|
|
104
|
-
console.log('[TEST] Generating Neon theme...');
|
|
105
|
-
try {
|
|
106
|
-
const card = await Neon({ ...sampleTrack, glowColor: '#00ffff', accentColor: '#ff00ff' });
|
|
107
|
-
fs.writeFileSync(path.join(outputDir, 'neon-theme.png'), card);
|
|
108
|
-
console.log(' ✓ Saved: neon-theme.png');
|
|
109
|
-
} catch (error) {
|
|
110
|
-
console.error(' ✗ Error:', error.message);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
89
|
// Flame
|
|
114
90
|
console.log('[TEST] Generating Flame theme...');
|
|
115
91
|
try {
|
|
@@ -122,7 +98,7 @@ async function generateTestCards() {
|
|
|
122
98
|
|
|
123
99
|
console.log();
|
|
124
100
|
console.log('═══════════════════════════════════════════════════════════════');
|
|
125
|
-
console.log('Test complete!
|
|
101
|
+
console.log('Test complete! v1.0.1 (CommonJS)');
|
|
126
102
|
console.log('═══════════════════════════════════════════════════════════════');
|
|
127
103
|
}
|
|
128
104
|
|