diff --git a/websites/M/myCANAL/metadata.json b/websites/M/myCANAL/metadata.json index 5644a128fc83..50838e72b94f 100644 --- a/websites/M/myCANAL/metadata.json +++ b/websites/M/myCANAL/metadata.json @@ -13,6 +13,10 @@ { "name": "Dark_Ville", "id": "638080361179512853" + }, + { + "name": "Arias800", + "id": "341235976766488576" } ], "service": "myCANAL", @@ -22,7 +26,7 @@ "nl": "Canal + is een Franse abonnementsaanbieder die is gekoppeld aan het kanaal met dezelfde naam. Het is eigendom van Vivendi met een aandeel van honderd procent." }, "url": "www.canalplus.com", - "version": "2.0.11", + "version": "2.0.12", "logo": "https://cdn.rcd.gg/PreMiD/websites/M/myCANAL/assets/logo.png", "thumbnail": "https://cdn.rcd.gg/PreMiD/websites/M/myCANAL/assets/thumbnail.png", "color": "#000", @@ -40,4 +44,4 @@ "value": true } ] -} \ No newline at end of file +} diff --git a/websites/M/myCANAL/presence.ts b/websites/M/myCANAL/presence.ts index 8147cf48ef2a..31f422f0cdb9 100644 --- a/websites/M/myCANAL/presence.ts +++ b/websites/M/myCANAL/presence.ts @@ -9,13 +9,131 @@ const presence = new Presence({ browsingTimestamp = Math.floor(Date.now() / 1000), containsTerm = (term: string) => document.location.pathname.includes(term); -const enum Assets { +enum myCANALAssets { Logo = "https://cdn.rcd.gg/PreMiD/websites/M/myCANAL/assets/0.png", } +// Resize fonction made by pierrequiroul https://github.com/PreMiD/Presences/pull/8910 + +export const cropPreset = { + // Crop values in percent correspond to Left, Right, Top, Bottom. + squared: [0, 0, 0, 0], + vertical: [0.22, 0.22, 0, 0.3], + horizontal: [0.425, 0.025, 0, 0], +}; + +export async function getThumbnail( + src: string = myCANALAssets.Logo, + cropPercentages: typeof cropPreset.squared = cropPreset.squared, + progress = 2, + borderWidth = 15 +): Promise { + return new Promise(resolve => { + const img = new Image(), + wh = 320; // Size of the square thumbnail + + img.crossOrigin = "anonymous"; + img.src = src; + + img.onload = function () { + let croppedWidth, + croppedHeight, + cropX = 0, + cropY = 0; + + // Determine if the image is landscape or portrait + const isLandscape = img.width > img.height; + + if (isLandscape) { + // Landscape mode: use left and right crop percentages + const cropLeft = img.width * cropPercentages[0]; + croppedWidth = img.width - cropLeft - img.width * cropPercentages[1]; + croppedHeight = img.height; + cropX = cropLeft; + } else { + // Portrait mode: use top and bottom crop percentages + const cropTop = img.height * cropPercentages[2]; + croppedWidth = img.width; + croppedHeight = img.height - cropTop - img.height * cropPercentages[3]; + cropY = cropTop; + } + + // Determine the scale to fit the cropped image into the square canvas + let newWidth, newHeight, offsetX, offsetY; + + if (isLandscape) { + newWidth = wh - 2 * borderWidth; + newHeight = (newWidth / croppedWidth) * croppedHeight; + offsetX = borderWidth; + offsetY = (wh - newHeight) / 2; + } else { + newHeight = wh - 2 * borderWidth; + newWidth = (newHeight / croppedHeight) * croppedWidth; + offsetX = (wh - newWidth) / 2; + offsetY = borderWidth; + } + + const tempCanvas = document.createElement("canvas"); + tempCanvas.width = wh; + tempCanvas.height = wh; + const ctx = tempCanvas.getContext("2d"), + // Remap progress from 0-1 to 0.03-0.97 (smallImageKey borders) + remappedProgress = 0.07 + progress * (0.93 - 0.07); + + // 1. Fill the canvas with a black background + ctx.fillStyle = "#172e4e"; + ctx.fillRect(0, 0, wh, wh); + + // 2. Draw the radial progress bar + if (remappedProgress > 0) { + ctx.beginPath(); + ctx.moveTo(wh / 2, wh / 2); + const startAngle = Math.PI / 4; // 45 degrees in radians, starting from bottom-right + + ctx.arc( + wh / 2, + wh / 2, + wh, + startAngle, + startAngle + 2 * Math.PI * remappedProgress + ); + ctx.lineTo(wh / 2, wh / 2); + + // Create a triangular gradient + const gradient = ctx.createLinearGradient(0, 0, wh, wh); + gradient.addColorStop(0, "rgba(245, 3, 26, 1)"); + gradient.addColorStop(0.5, "rgba(63, 187, 244, 1)"); + gradient.addColorStop(1, "rgba(164, 215, 12, 1)"); + ctx.fillStyle = gradient; + + ctx.fill(); + } + + // 3. Draw the cropped image centered and zoomed out based on the borderWidth + ctx.drawImage( + img, + cropX, + cropY, + croppedWidth, + croppedHeight, + offsetX, + offsetY, + newWidth, + newHeight + ); + + resolve(tempCanvas.toDataURL("image/png")); + }; + + img.onerror = function () { + resolve(src); + }; + }); +} + presence.on("UpdateData", async () => { const presenceData: PresenceData = { - largeImageKey: Assets.Logo, + largeImageKey: myCANALAssets.Logo, }, video = document.querySelector(".iIZX3IGkM2eBzzWle1QQ"), showCover = await presence.getSetting("cover"), @@ -37,6 +155,9 @@ presence.on("UpdateData", async () => { case "/series/": presenceData.state = "Séries"; break; + case "/jeunesse/": + presenceData.state = "Jeunesse"; + break; case "/live/": presenceData.state = "Chaînes en direct"; break; @@ -61,8 +182,8 @@ presence.on("UpdateData", async () => { presenceData.largeImageKey = showCover ? document.querySelector( `#\\3${channelID}_onclick > div > div.card__content_0dae1b.cardContent___DuNAN.ratio--169 > div[class*="cardLogoChannel"] > div > img` - )?.src - : Assets.Logo; + ).src + : myCANALAssets.Logo; presenceData.smallImageKey = Assets.Live; presenceData.smallImageText = "En direct"; delete presenceData.startTimestamp; @@ -76,27 +197,28 @@ presence.on("UpdateData", async () => { [presenceData.startTimestamp, presenceData.endTimestamp] = presence.getTimestamps(video.currentTime, video.duration); presenceData.largeImageKey = showCover - ? (presenceData.largeImageKey = - document.querySelector( - "[property='og:image']" - )?.content) - : Assets.Logo; + ? (presenceData.largeImageKey = await getThumbnail( + document.querySelector("[property='og:image']") + ?.content + )) + : myCANALAssets.Logo; presenceData.smallImageKey = video.paused ? Assets.Pause : Assets.Play; presenceData.smallImageText = video.paused ? (await strings).pause : (await strings).play; break; case containsTerm("series"): + case containsTerm("jeunesse"): presenceData.details = titleTvShows[0].textContent.trim(); presenceData.state = titleTvShows[1].textContent.trim(); [presenceData.startTimestamp, presenceData.endTimestamp] = presence.getTimestamps(video.currentTime, video.duration); presenceData.largeImageKey = showCover - ? (presenceData.largeImageKey = - document.querySelector( - "[property='og:image']" - )?.content) - : Assets.Logo; + ? (presenceData.largeImageKey = await getThumbnail( + document.querySelector("[property='og:image']") + ?.content + )) + : myCANALAssets.Logo; presenceData.smallImageKey = video.paused ? Assets.Pause : Assets.Play; presenceData.smallImageText = video.paused ? (await strings).pause