From f432140b95ecb5fc40a94d772ac5ec63f86291d7 Mon Sep 17 00:00:00 2001 From: dovjay Date: Tue, 14 Jul 2026 10:52:34 +0700 Subject: [PATCH 1/2] feat(anikoto): new basic activity function for anikototv.to --- websites/A/Anikoto/metadata.json | 24 +++++++++ websites/A/Anikoto/presence.ts | 90 ++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 websites/A/Anikoto/metadata.json create mode 100644 websites/A/Anikoto/presence.ts diff --git a/websites/A/Anikoto/metadata.json b/websites/A/Anikoto/metadata.json new file mode 100644 index 000000000000..42a588420e56 --- /dev/null +++ b/websites/A/Anikoto/metadata.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://schemas.premid.app/metadata/1.17", + "apiVersion": 1, + "author": { + "id": "467289851012841474", + "name": "dovjay" + }, + "service": "Anikoto", + "description": { + "en": "AniKoto is a free anime streaming destination where fans can watch high-quality anime series and movies with English subtitles or dubbed audio. With a constantly growing collection, discovering your favorite anime takes just a moment." + }, + "url": "anikototv.to", + "regExp": "^https?[:][/][/]([a-z0-9-]+[.])*anikototv[.]to[/]", + "version": "1.0.0", + "logo": "https://i.imgur.com/FYh2IFE.png", + "thumbnail": "https://i.imgur.com/WeU62dg.png", + "color": "#26A3D6", + "category": "anime", + "tags": [ + "anime", + "video", + "iframe" + ] +} diff --git a/websites/A/Anikoto/presence.ts b/websites/A/Anikoto/presence.ts new file mode 100644 index 000000000000..2d62e31a2103 --- /dev/null +++ b/websites/A/Anikoto/presence.ts @@ -0,0 +1,90 @@ +import { ActivityType } from 'premid' + +const presence = new Presence({ + clientId: '1526425052944273408', +}) + +enum ActivityAssets { // Other default assets can be found at index.d.ts + Logo = 'https://i.imgur.com/FYh2IFE.png', +} + +// Create timestamps outside UpdateData to maintain consistent timing +let browsingTimestamp = Math.floor(Date.now() / 1000) +let watchTimestamp = Math.floor(Date.now() / 1000) +let wasWatchingVideo = false +let lastHref = '' + +presence.on('UpdateData', async () => { + const { href } = document.location + const presenceData: MediaPresenceData = { + type: ActivityType.Watching, + largeImageKey: ActivityAssets.Logo, + } + + // 1. Check if the parent wrapper exists + const watchMain = document.querySelector('#watch-main') + + // 2. Check if the actual iframe player has loaded into the DOM + const playerIframe = document.querySelector('#w-player iframe') + + // If both the wrapper and the iframe exist, the user is watching + if (watchMain && playerIframe && playerIframe.src) { + // Reset the watch timer if they just transitioned to watching or swapped episodes + if (!wasWatchingVideo || href !== lastHref) { + watchTimestamp = Math.floor(Date.now() / 1000) + wasWatchingVideo = true + lastHref = href + } + + // Extract Title from the h1 element + const titleEl = document.querySelector('h1.title.d-title') + const title = titleEl ? titleEl.textContent?.trim() : 'Unknown Anime' + + // Extract Episode Number from the wrapper data attribute + const epNum = watchMain.getAttribute('data-ep-name') + const episodeText = epNum ? `Episode ${epNum}` : 'Watching' + + // Extract the Anime Cover image + const posterEl = document.querySelector('.binfo .poster img[itemprop="image"]') + const posterUrl = posterEl ? posterEl.getAttribute('src') : null + + // Build the Watching state + presenceData.type = ActivityType.Watching + presenceData.details = title + presenceData.state = episodeText + presenceData.largeImageKey = posterUrl || 'https://example.com/logo.png' + presenceData.largeImageText = title + + // Use an elapsed timer since we cannot read the iframe's internal video timeline + presenceData.startTimestamp = watchTimestamp + + presenceData.buttons = [ + { + label: 'Watch Anime', + url: href, + }, + ] + } + else { + // User is browsing the home page or menus (iframe not found) + if (wasWatchingVideo) { + browsingTimestamp = Math.floor(Date.now() / 1000) + wasWatchingVideo = false + } + + // Build the Browsing state + presenceData.largeImageKey = 'https://example.com/logo.png' + presenceData.largeImageText = 'Anikoto' + presenceData.details = 'Browsing Anime' + presenceData.state = 'Looking for something to watch' + presenceData.startTimestamp = browsingTimestamp + } + + // Set or clear the activity + if (presenceData.details) { + presence.setActivity(presenceData) + } + else { + presence.clearActivity() + } +}) From 7fb5a742b12481c2eead819cab932c054211070c Mon Sep 17 00:00:00 2001 From: dovjay Date: Fri, 17 Jul 2026 16:49:49 +0700 Subject: [PATCH 2/2] change default image link --- websites/A/Anikoto/presence.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/websites/A/Anikoto/presence.ts b/websites/A/Anikoto/presence.ts index 2d62e31a2103..1daac42f5018 100644 --- a/websites/A/Anikoto/presence.ts +++ b/websites/A/Anikoto/presence.ts @@ -52,7 +52,7 @@ presence.on('UpdateData', async () => { presenceData.type = ActivityType.Watching presenceData.details = title presenceData.state = episodeText - presenceData.largeImageKey = posterUrl || 'https://example.com/logo.png' + presenceData.largeImageKey = posterUrl || ActivityAssets.Logo presenceData.largeImageText = title // Use an elapsed timer since we cannot read the iframe's internal video timeline @@ -73,7 +73,7 @@ presence.on('UpdateData', async () => { } // Build the Browsing state - presenceData.largeImageKey = 'https://example.com/logo.png' + presenceData.largeImageKey = ActivityAssets.Logo presenceData.largeImageText = 'Anikoto' presenceData.details = 'Browsing Anime' presenceData.state = 'Looking for something to watch'