diff --git a/README.md b/README.md index 8dbc342..9364806 100644 --- a/README.md +++ b/README.md @@ -18,17 +18,6 @@ npm install songcard # or yarn add songcard ``` -
- -## Usage - -| Option | Type | Description | -|------------------------|------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------| -| imageBg | String | Image that will be display to the songcard.

Example: https://images-ext-1.discordapp.net/external/uw_-bWFyeXnWb11wGThe2CAbTYdrxzFqMJ2trxDIYVE/https/i.scdn.co/image/ab67616d0000b2738ad8f5243d6534e03b656c8b?width=468&height=468
File format: PNG/JPEG | -| imageText | String | Text that will be display to the songcard. | -| trackStream | Boolean | Whether to set the trackDuration and trackTotalDuration to `LIVE`

Example: if trackStream is `true` the trackDuration and totalTrackDuration will show as `LIVE` else it will show number. | -| trackDuration | Integer | Show current duration of the songs. If no value provide it will show `0:00`. | -| trackTotalDuration | Integer | Show the songs duration. |
@@ -37,21 +26,29 @@ yarn add songcard ## 1. Classic -![](./src/examples/assets/card1.png) +![](./src/assets/card1.png) ### Example -![](./src/examples/assets/code1.png) +![](./src/assets/code1.png)
## 2. Simple -![](./src/examples/assets/card2.png) +![](./src/assets/card2.png) + +### Example + +![](./src/assets/code2.png) + +## 3. Dynamic + +![](./src/assets/card3.png) ### Example -![](./src/examples/assets/code2.png) +![](./src/assets/code3.png) diff --git a/package-lock.json b/package-lock.json index 7192356..993ed54 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "songcard", - "version": "1.2.0", + "version": "1.2.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "songcard", - "version": "1.2.0", + "version": "1.2.1", "license": "ISC", "dependencies": { "@napi-rs/canvas": "^0.1.52", diff --git a/package.json b/package.json index dbc40b4..3119b7d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "songcard", - "version": "1.2.0", + "version": "1.2.1", "main": "src/index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" diff --git a/src/examples/assets/card1.png b/src/assets/card1.png similarity index 100% rename from src/examples/assets/card1.png rename to src/assets/card1.png diff --git a/src/examples/assets/card2.png b/src/assets/card2.png similarity index 100% rename from src/examples/assets/card2.png rename to src/assets/card2.png diff --git a/src/assets/card3.png b/src/assets/card3.png new file mode 100644 index 0000000..347994f Binary files /dev/null and b/src/assets/card3.png differ diff --git a/src/examples/assets/code1.png b/src/assets/code1.png similarity index 100% rename from src/examples/assets/code1.png rename to src/assets/code1.png diff --git a/src/examples/assets/code2.png b/src/assets/code2.png similarity index 100% rename from src/examples/assets/code2.png rename to src/assets/code2.png diff --git a/src/assets/code3.png b/src/assets/code3.png new file mode 100644 index 0000000..a5834c0 Binary files /dev/null and b/src/assets/code3.png differ diff --git a/src/assets/soundcloud.png b/src/assets/soundcloud.png new file mode 100644 index 0000000..49e4369 Binary files /dev/null and b/src/assets/soundcloud.png differ diff --git a/src/assets/spotify.png b/src/assets/spotify.png new file mode 100644 index 0000000..4e7c810 Binary files /dev/null and b/src/assets/spotify.png differ diff --git a/src/assets/youtube.png b/src/assets/youtube.png new file mode 100644 index 0000000..4085105 Binary files /dev/null and b/src/assets/youtube.png differ diff --git a/src/examples/classicCard.example.js b/src/example/classicCard.example.js similarity index 100% rename from src/examples/classicCard.example.js rename to src/example/classicCard.example.js diff --git a/src/example/dynamicCard.example.js b/src/example/dynamicCard.example.js new file mode 100644 index 0000000..01e48ee --- /dev/null +++ b/src/example/dynamicCard.example.js @@ -0,0 +1,23 @@ +const { dynamicCard } = require("songcard"); +const { AttachmentBuilder } = require("discord.js"); + +client.on("interactionCreate", async (interaction) => { + const cardImage = await dynamicCard({ + thumbnailURL: + "https://i.scdn.co/image/ab67616d00001e0240d7efd2594a2b6bda60ea18", + songTitle: "What is Love", + songArtist: "TWICE", + streamProvider: "spotify", + trackRequester: "@lewdhutao", + }); + + const attachment = new AttachmentBuilder(cardImage, { + name: "card.png", + }); + + interaction.channel.send({ + files: [attachment], + }); +}); + +client.login("token"); diff --git a/src/examples/simpleCard.example.js b/src/example/simpleCard.example.js similarity index 100% rename from src/examples/simpleCard.example.js rename to src/example/simpleCard.example.js diff --git a/src/index.js b/src/index.js index 77483ba..8255192 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,5 @@ const { classicCard } = require("./themes/classicCard"); const { simpleCard } = require("./themes/simpleCard"); +const { dynamicCard } = require("./themes/dynamicCard") -module.exports = { classicCard, simpleCard }; +module.exports = { classicCard, simpleCard, dynamicCard }; diff --git a/src/tests/classicCard.test.js b/src/test/classicCard.test.js similarity index 100% rename from src/tests/classicCard.test.js rename to src/test/classicCard.test.js diff --git a/src/test/dynamicCard.test.js b/src/test/dynamicCard.test.js new file mode 100644 index 0000000..98d6913 --- /dev/null +++ b/src/test/dynamicCard.test.js @@ -0,0 +1,27 @@ +const fs = require("fs"); +const { dynamicCard } = require("../themes/dynamicCard"); + +async function testdynamicCard() { + const thumbnailURL = + "https://i.scdn.co/image/ab67616d00001e0240d7efd2594a2b6bda60ea18"; + const songTitle = "What is Love"; + const songArtist = "TWICE"; + const streamProvider = "spotify"; + const trackRequester = "@lewdhutao"; + + try { + const buffer = await dynamicCard({ + thumbnailURL, + songTitle, + songArtist, + streamProvider, + trackRequester, + }); + fs.writeFileSync("dynamicCard.png", buffer); + console.log("Canvas generated successfully."); + } catch (error) { + console.error("Error generating canvas:", error); + } +} + +testdynamicCard(); diff --git a/src/tests/simpleCard.test.js b/src/test/simpleCard.test.js similarity index 100% rename from src/tests/simpleCard.test.js rename to src/test/simpleCard.test.js diff --git a/src/tests/classicCard.png b/src/tests/classicCard.png deleted file mode 100644 index bae1768..0000000 Binary files a/src/tests/classicCard.png and /dev/null differ diff --git a/src/tests/simpleCard.png b/src/tests/simpleCard.png deleted file mode 100644 index c275f9a..0000000 Binary files a/src/tests/simpleCard.png and /dev/null differ diff --git a/src/themes/dynamicCard.js b/src/themes/dynamicCard.js new file mode 100644 index 0000000..c54c196 --- /dev/null +++ b/src/themes/dynamicCard.js @@ -0,0 +1,178 @@ +const { createCanvas, loadImage, GlobalFonts } = require("@napi-rs/canvas"); +const path = require("path"); + +async function dynamicCard({ + thumbnailURL, // required + songTitle, // required + songArtist, // optional + streamProvider, // optional + trackRequester, // optional +}) { + const cardWidth = 800; + const cardHeight = 250; + const backgroundColor = "#2B2D31"; + + const canvas = createCanvas(cardWidth, cardHeight); + const ctx = canvas.getContext("2d"); + + const fontPath = path.join(__dirname, "..", "fonts", "ArialUnicodeMS.ttf"); + GlobalFonts.registerFromPath(fontPath, "ArialUnicodeMS"); + + function roundRect(ctx, x, y, width, height, radius, fill, stroke) { + if (typeof stroke === "undefined") { + stroke = true; + } + if (typeof radius === "undefined") { + radius = 5; + } + if (typeof radius === "number") { + radius = { tl: radius, tr: radius, br: radius, bl: radius }; + } else { + var defaultRadius = { tl: 0, tr: 0, br: 0, bl: 0 }; + for (var side in defaultRadius) { + radius[side] = radius[side] || defaultRadius[side]; + } + } + ctx.beginPath(); + ctx.moveTo(x + radius.tl, y); + ctx.lineTo(x + width - radius.tr, y); + ctx.quadraticCurveTo(x + width, y, x + width, y + radius.tr); + ctx.lineTo(x + width, y + height - radius.br); + ctx.quadraticCurveTo( + x + width, + y + height, + x + width - radius.br, + y + height + ); + ctx.lineTo(x + radius.bl, y + height); + ctx.quadraticCurveTo(x, y + height, x, y + height - radius.bl); + ctx.lineTo(x, y + radius.tl); + ctx.quadraticCurveTo(x, y, x + radius.tl, y); + ctx.closePath(); + if (fill) { + ctx.fill(); + } + if (stroke) { + ctx.stroke(); + } + } + + ctx.fillStyle = backgroundColor; + roundRect(ctx, 0, 0, cardWidth, cardHeight, 20, true, false); + + const thumbnailImage = await loadImage(thumbnailURL); + + const padding = 20; + const thumbnailSize = cardHeight - 2 * padding; + + ctx.save(); + ctx.beginPath(); + ctx.moveTo(cardWidth - thumbnailSize - padding + 20, padding); + ctx.arcTo( + cardWidth - padding, + padding, + cardWidth - padding, + padding + 20, + 20 + ); + ctx.arcTo( + cardWidth - padding, + cardHeight - padding, + cardWidth - padding - 20, + cardHeight - padding, + 20 + ); + ctx.arcTo( + cardWidth - thumbnailSize - padding, + cardHeight - padding, + cardWidth - thumbnailSize - padding, + cardHeight - padding - 20, + 20 + ); + ctx.arcTo( + cardWidth - thumbnailSize - padding, + padding, + cardWidth - thumbnailSize - padding + 20, + padding, + 20 + ); + ctx.closePath(); + ctx.clip(); + ctx.drawImage( + thumbnailImage, + cardWidth - thumbnailSize - padding, + padding, + thumbnailSize, + thumbnailSize + ); + ctx.restore(); + + const streamProviderIcons = { + spotify: path.join(__dirname, "..", "assets", "spotify.png"), + youtube: path.join(__dirname, "..", "assets", "youtube.png"), + soundcloud: path.join(__dirname, "..", "assets", "soundcloud.png"), + }; + + if (streamProvider && streamProviderIcons[streamProvider.toLowerCase()]) { + const providerIcon = await loadImage( + streamProviderIcons[streamProvider.toLowerCase()] + ); + const iconSize = 30; + const iconPadding = 10; + ctx.save(); + ctx.beginPath(); + ctx.arc( + cardWidth - padding - iconPadding - iconSize / 2, + cardHeight - padding - iconPadding - iconSize / 2, + iconSize / 2, + 0, + Math.PI * 2 + ); + ctx.closePath(); + ctx.clip(); + ctx.drawImage( + providerIcon, + cardWidth - padding - iconPadding - iconSize, + cardHeight - padding - iconPadding - iconSize, + iconSize, + iconSize + ); + ctx.restore(); + } + + ctx.fillStyle = "white"; + ctx.font = "bold 35px 'ArialUnicodeMS'"; + ctx.textAlign = "left"; + ctx.textBaseline = "top"; + + const maxWidth = cardWidth - thumbnailSize - padding * 2; + let truncatedTitle = songTitle; + + while (ctx.measureText(truncatedTitle).width > maxWidth) { + truncatedTitle = truncatedTitle.slice(0, -1); + } + + if (truncatedTitle.length < songTitle.length) { + truncatedTitle = truncatedTitle.slice(0, -3) + "..."; + } + + ctx.fillText(truncatedTitle, padding + 10, padding + 20); + + ctx.fillStyle = "#A79D9D"; + ctx.font = "25px 'ArialUnicodeMS'"; + + ctx.fillText(songArtist, padding + 10, padding + 70); + + ctx.font = "20px 'ArialUnicodeMS'"; + ctx.fillText( + `Requested by: ${trackRequester}`, + padding + 10, + cardHeight - padding - 20 + ); + + const buffer = canvas.toBuffer("image/png"); + + return buffer; +} + +module.exports = { dynamicCard };