From 9ba966f0ad0112994995752a2842feee9b611268 Mon Sep 17 00:00:00 2001 From: sirschubert Date: Fri, 26 Dec 2025 09:41:34 +0400 Subject: [PATCH 1/2] feat(MovieBoxPro): add activity --- websites/M/MovieBoxPro/metadata.json | 24 +++++++ websites/M/MovieBoxPro/presence.ts | 94 ++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 websites/M/MovieBoxPro/metadata.json create mode 100644 websites/M/MovieBoxPro/presence.ts diff --git a/websites/M/MovieBoxPro/metadata.json b/websites/M/MovieBoxPro/metadata.json new file mode 100644 index 000000000000..19ef77e569f2 --- /dev/null +++ b/websites/M/MovieBoxPro/metadata.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://schemas.premid.app/metadata/1.16", + "apiVersion": 1, + "author": { + "id": "339316517558681610", + "name": "firstoften" + }, + "service": "MovieBoxPro", + "description": { + "en": "MovieBoxPro is for watching movies and TV shows, offering details like posters, cast info, trailers, and trending titles, sourced from The Movie Database" + }, + "url": "google.com", + "regExp": "^https?[:][/][/]([a-z0-9-]+[.])*movieboxpro[.]app[/]", + "version": "1.0.0", + "logo": "https://cdn.aptoide.com/imgs/b/c/8/bc87071060372ad5bbd0ee5470c05dda_icon.jpg", + "thumbnail": "https://raw.githubusercontent.com/sirschubert/assets/refs/heads/main/assets/unnamed.png", + "color": "#fcba03", + "category": "videos", + "tags": [ + "movies", + "series", + "watch" + ] +} diff --git a/websites/M/MovieBoxPro/presence.ts b/websites/M/MovieBoxPro/presence.ts new file mode 100644 index 000000000000..795f0cdd565c --- /dev/null +++ b/websites/M/MovieBoxPro/presence.ts @@ -0,0 +1,94 @@ +import { ActivityType, Assets, getTimestamps, timestampFromFormat } from 'premid' + +declare const Presence: any + +const presence = new Presence({ clientId: '1453728381903306814' }) + +function extractSeasonEpisode(text: string | null | undefined): string | null { + if (!text) + return null + const match = text.match(/S(\d+)\s*E(\d+)/i) + return (match && match[1] && match[2]) ? `Season ${Number.parseInt(match[1])}, Episode ${Number.parseInt(match[2])}` : null +} + +presence.on('UpdateData', async () => { + const video = document.querySelector('video') + const rawTitle = document.querySelector('span.name')?.textContent ?? null + + const presenceData: any = { + type: ActivityType.Watching, + largeImageKey: 'https://cdn.aptoide.com/imgs/b/c/8/bc87071060372ad5bbd0ee5470c05dda_icon.jpg', + details: 'Browsing Library', + state: 'Choosing a movie...', + } + + if (video && rawTitle) { + presenceData.details = rawTitle + + const coverImg = document.querySelector('img.cover') as HTMLImageElement + if (coverImg) + presenceData.largeImageKey = coverImg.src + + const params = new URLSearchParams(document.location.search) + let episodeInfo = (params.get('season') && params.get('episode')) + ? `Season ${params.get('season')}, Episode ${params.get('episode')}` + : null + + if (!episodeInfo) { + const jwTitle = document.querySelector('.jw-title-primary')?.textContent ?? document.querySelector('.jw-title-secondary')?.textContent + episodeInfo = extractSeasonEpisode(jwTitle) + } + + if (!episodeInfo) { + const videoContainer = document.querySelector('.video-js') + if (videoContainer) + episodeInfo = extractSeasonEpisode(videoContainer.textContent) + } + + if (!episodeInfo) { + const metaDesc = document.querySelector('meta[name="description"]')?.getAttribute('content') + episodeInfo = extractSeasonEpisode(metaDesc) || extractSeasonEpisode(document.title) + } + + presenceData.state = episodeInfo || (document.location.href.match(/tvshow|season/) ? 'Watching TV Show' : 'Watching Movie') + + if (video.paused) { + presenceData.smallImageKey = Assets.Pause + presenceData.smallImageText = 'Paused' + presenceData.state += ' (Paused)' + delete presenceData.startTimestamp + delete presenceData.endTimestamp + } + else { + presenceData.smallImageKey = Assets.Play + presenceData.smallImageText = 'Playing' + + let rawCurrent = document.querySelector('.vjs-current-time-display')?.textContent ?? '' + let rawDuration = document.querySelector('.vjs-duration-display')?.textContent ?? '' + const rawRemaining = document.querySelector('.vjs-remaining-time-display')?.textContent ?? '' + + if (!rawCurrent.trim()) { + rawCurrent = document.querySelector('.jw-text-elapsed')?.textContent ?? '' + rawDuration = document.querySelector('.jw-text-duration')?.textContent ?? '' + } + + const currentSeconds = timestampFromFormat(rawCurrent.replace(/[^\d:]/g, '')) + const durationSeconds = timestampFromFormat(rawDuration.replace(/[^\d:]/g, '')) + const remainingSeconds = timestampFromFormat(rawRemaining.replace(/[^\d:]/g, '')) + + if (durationSeconds > 0) { + [presenceData.startTimestamp, presenceData.endTimestamp] = getTimestamps(currentSeconds, durationSeconds) + } + else if (remainingSeconds > 0) { + presenceData.startTimestamp = Date.now() - (currentSeconds * 1000) + presenceData.endTimestamp = Date.now() + (remainingSeconds * 1000) + } + else { + presenceData.startTimestamp = Date.now() - (currentSeconds * 1000) + delete presenceData.endTimestamp + } + } + } + + presence.setActivity(presenceData) +}) From 3d6c2be80459eaeb6ccb76a3fe51ea26821d8958 Mon Sep 17 00:00:00 2001 From: sirschubert <100043392+sirschubert@users.noreply.github.com> Date: Wed, 7 Jan 2026 11:48:40 +0400 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: Daniel Lau <32113157+theusaf@users.noreply.github.com> Signed-off-by: sirschubert <100043392+sirschubert@users.noreply.github.com> --- websites/M/MovieBoxPro/presence.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/websites/M/MovieBoxPro/presence.ts b/websites/M/MovieBoxPro/presence.ts index 795f0cdd565c..00dea182ddf7 100644 --- a/websites/M/MovieBoxPro/presence.ts +++ b/websites/M/MovieBoxPro/presence.ts @@ -25,7 +25,7 @@ presence.on('UpdateData', async () => { if (video && rawTitle) { presenceData.details = rawTitle - const coverImg = document.querySelector('img.cover') as HTMLImageElement + const coverImg = document.querySelector('img.cover') if (coverImg) presenceData.largeImageKey = coverImg.src