From 4ca6a7abea76c5270eefbda97d2e3145104b947f Mon Sep 17 00:00:00 2001 From: kaisei-kto <78276334+kaisei-kto@users.noreply.github.com> Date: Sat, 21 Sep 2024 15:37:39 -0400 Subject: [PATCH] feat(Shiroko): add Presence (#8746) Signed-off-by: kaisei-kto <78276334+kaisei-kto@users.noreply.github.com> Co-authored-by: github plz bring back DarkVIllager --- websites/S/Shiroko/metadata.json | 22 ++++++ websites/S/Shiroko/presence.ts | 115 +++++++++++++++++++++++++++++++ websites/S/Shiroko/utils.ts | 46 +++++++++++++ 3 files changed, 183 insertions(+) create mode 100644 websites/S/Shiroko/metadata.json create mode 100644 websites/S/Shiroko/presence.ts create mode 100644 websites/S/Shiroko/utils.ts diff --git a/websites/S/Shiroko/metadata.json b/websites/S/Shiroko/metadata.json new file mode 100644 index 000000000000..4ec77dc9f252 --- /dev/null +++ b/websites/S/Shiroko/metadata.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://schemas.premid.app/metadata/1.11", + "apiVersion": 1, + "author": { + "id": "84719369069805568", + "name": "laziestboy" + }, + "service": "Shiroko", + "description": { + "en": "Discover your new favorite anime or manga title! Shiroko offers a vast library of high-quality content, accessible on multiple devices and without any interruptions. Start using Shiroko today!" + }, + "url": "shiroko.co", + "version": "1.0.0", + "logo": "https://i.postimg.cc/dDcKqQ6F/Shiroko.png", + "thumbnail": "https://i.postimg.cc/15CDNKWL/image.png", + "color": "#100C11", + "category": "anime", + "tags": [ + "anime", + "manga" + ] +} \ No newline at end of file diff --git a/websites/S/Shiroko/presence.ts b/websites/S/Shiroko/presence.ts new file mode 100644 index 000000000000..d7e2ccf94ad2 --- /dev/null +++ b/websites/S/Shiroko/presence.ts @@ -0,0 +1,115 @@ +import { getEpisode, getId, getImage, getTitle } from "./utils"; + +const presence = new Presence({ + clientId: "1286500193881161804", +}); + +let lastPlaybackState = false, + playback = false, + startTimestamp = Math.floor(Date.now() / 1000), + lastHref: string, + lastUpdate = 0; + +presence.on("UpdateData", async () => { + const presenceData: PresenceData = { + startTimestamp, + }, + video = document.querySelector("video"), + { pathname, href } = document.location; + + if (video) { + presenceData.type = ActivityType.Watching; + playback = !video.paused && !video.ended; + + if (lastPlaybackState !== playback) + startTimestamp = Math.floor(Date.now() / 1000); + + presenceData.smallImageKey = playback ? Assets.Play : Assets.Pause; + presenceData.smallImageText = playback ? "Playing" : "Paused"; + } + + if (video && lastPlaybackState === playback) return; + lastPlaybackState = playback; + + if (pathname.startsWith("/en/schedule")) { + presenceData.details = "Viewing Schedule..."; + + presenceData.buttons = [ + { + label: "View Schedule", + url: href, + }, + ]; + } + if (pathname.includes("/search/manga")) + presenceData.details = "Searching Manga..."; + if (pathname.includes("/search/anime")) + presenceData.details = "Searching Anime..."; + if (href.includes("/search/anime?season=")) + presenceData.details = "Viewing Season..."; + if ( + pathname.startsWith("/en/anime/") && + !pathname.startsWith("/en/anime/watch") + ) { + presenceData.details = getTitle(); + presenceData.state = "Viewing Anime..."; + + presenceData.buttons = [ + { + label: "View Anime", + url: href, + }, + ]; + } else if (pathname.startsWith("/en/manga/")) { + presenceData.details = getTitle(); + presenceData.state = "Viewing Manga..."; + + presenceData.buttons = [ + { + label: "View Manga", + url: href, + }, + ]; + } else if (typeof presenceData.details !== "string") + presenceData.details = "Viewing Home..."; + + if (pathname.startsWith("/en/anime/watch")) { + const episode = getEpisode(); + + presenceData.details = getTitle(); + presenceData.state = !isNaN(episode) + ? `Episode ${episode}` + : "Unable to retrieve episode"; + presenceData.type = ActivityType.Watching; + + presenceData.buttons = [ + { + label: "Watch Anime", + url: `https://shiroko.co/en/anime/watch?id=${getId()}&n=${episode}`, + }, + ]; + } + + if (pathname.startsWith("/en/profile/")) { + const username = pathname.split("/").pop(); + if (username !== "profile" && username !== "") { + presenceData.details = `Viewing ${username}'s Profile`; + presenceData.buttons = [ + { + label: "View Profile", + url: href, + }, + ]; + } + } + + const now = Date.now(); + if (lastHref !== href || (now - lastUpdate) / 1000 >= 1) { + lastHref = href; + lastUpdate = now; + } else return; + + presenceData.largeImageKey = getImage(); + + presence.setActivity(presenceData); +}); diff --git a/websites/S/Shiroko/utils.ts b/websites/S/Shiroko/utils.ts new file mode 100644 index 000000000000..bf2fcad3a682 --- /dev/null +++ b/websites/S/Shiroko/utils.ts @@ -0,0 +1,46 @@ +export function getImage(): string | undefined { + for (const img of document.images) { + if ( + ["poster anime", "Poster Anime", "Anime Cover"].includes( + String(img.attributes.getNamedItem("alt")?.value) + ) + ) + return String(img.src); + } +} + +export function getTitle(): string | undefined { + const nt = document.querySelector(".title-nt"), + ro = document.querySelector(".title-rm"), + en = document.querySelector(".title-en"); + + if (en) return en.textContent; + if (ro) return ro.textContent; + if (nt) return nt.textContent; + + return document.querySelector("h1")?.textContent; +} + +export function getEpisode(): number { + let episode = 1; + const query = [...document.querySelectorAll("h3")] + .find(a => a.textContent.includes("Episode")) + ?.textContent?.replace("Episode ", ""); + + if (query) episode = Number(query); + + return episode; +} + +export function getId(): number { + const { search, pathname } = document.location; + + for (const [key, value] of search + .substr(1) + .split("&") + .map(s => s.split("="))) + if (key === "id") return Number(value); + + for (const path of pathname.split("/")) + if (path.match(/^(\d+)$/)) return Number(path); +}