From def9e2f1f12b86e9801f5c2b41255c9acf0507bc Mon Sep 17 00:00:00 2001 From: nineteen <81378628+vixxyTW@users.noreply.github.com> Date: Sat, 15 Aug 2026 07:01:35 +0200 Subject: [PATCH 1/9] Changes & New Domain Signed-off-by: nineteen <81378628+vixxyTW@users.noreply.github.com> --- websites/L/Lunar/metadata.json | 12 ++++++------ websites/L/Lunar/presence.ts | 21 ++++++++++++++++----- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/websites/L/Lunar/metadata.json b/websites/L/Lunar/metadata.json index 4c3c63ada1ba..f5ce4f938dc9 100644 --- a/websites/L/Lunar/metadata.json +++ b/websites/L/Lunar/metadata.json @@ -1,5 +1,5 @@ { - "$schema": "https://schemas.premid.app/metadata/1.17", + "$schema": "https://schemas.premid.app/metadata/1.16", "apiVersion": 1, "author": { "name": "alex", @@ -9,11 +9,11 @@ "description": { "en": "Lunar is a free platform to stream anime and read manga and light novels, with community features, playlists and more." }, - "url": "lunaranime.ru", - "regExp": "^https?[:][/][/](www[.])?lunaranime[.]ru[/]", - "version": "1.0.1", - "logo": "https://cdn.rcd.gg/PreMiD/websites/L/Lunar/assets/logo.png", - "thumbnail": "https://cdn.rcd.gg/PreMiD/websites/L/Lunar/assets/thumbnail.png", + "url": "lunarx.to", + "regExp": "^https?[:][/][/](www[.])?lunarx[.]to[/]", + "version": "1.0.0", + "logo": "https://api.lunarx.to/static/rpc/icon.png?v=3", + "thumbnail": "https://api.lunarx.to/static/rpc/thumbnail.png", "color": "#895AF6", "category": "anime", "tags": [ diff --git a/websites/L/Lunar/presence.ts b/websites/L/Lunar/presence.ts index cce9a3350104..63b44d2eba13 100644 --- a/websites/L/Lunar/presence.ts +++ b/websites/L/Lunar/presence.ts @@ -17,7 +17,7 @@ function updateTimestamp(id: string): void { } enum ActivityAssets { - Logo = 'https://cdn.rcd.gg/PreMiD/websites/L/Lunar/assets/logo.png', + Logo = 'https://api.lunarx.to/static/rpc/icon.png?v=3', } interface LunarActivity { @@ -160,6 +160,7 @@ presence.on('UpdateData', async () => { updateTimestamp('privacy') presence.setActivity({ largeImageKey: ActivityAssets.Logo, + largeImageText: 'lunarx.to', details: 'Browsing Lunar', startTimestamp: currentTimestamp, }) @@ -183,7 +184,10 @@ presence.on('UpdateData', async () => { details: activity.title, state: activity.meta, largeImageKey, - largeImageText: activity.title, + largeImageText: + largeImageKey === ActivityAssets.Logo + ? 'lunarx.to' + : activity.title, smallImageKey: Assets.Play, smallImageText: 'Watching', startTimestamp: currentTimestamp, @@ -221,7 +225,7 @@ presence.on('UpdateData', async () => { { label: 'Watch on Lunar', url: href }, { label: 'View Anime', - url: `https://lunaranime.ru/anime/${activity.id}`, + url: `https://lunarx.to/anime/${activity.id}`, }, ] } @@ -234,6 +238,10 @@ presence.on('UpdateData', async () => { details: activity.title, state: activity.meta, largeImageKey, + largeImageText: + largeImageKey === ActivityAssets.Logo + ? 'lunarx.to' + : activity.title, smallImageKey: Assets.Reading, smallImageText: activity.type === 'manga' ? 'Reading Manga' : 'Reading Novel', @@ -245,7 +253,7 @@ presence.on('UpdateData', async () => { { label: 'Read on Lunar', url: href }, { label: activity.type === 'manga' ? 'View Series' : 'View Novel', - url: `https://lunaranime.ru/${activity.type}/${activity.id}`, + url: `https://lunarx.to/${activity.type}/${activity.id}`, }, ] } @@ -261,6 +269,7 @@ presence.on('UpdateData', async () => { const presenceData: PresenceData = { largeImageKey: ActivityAssets.Logo, + largeImageText: 'lunarx.to', details: 'Browsing Lunar', } @@ -316,8 +325,10 @@ presence.on('UpdateData', async () => { presenceData.state = pageTitle() ?? titleFromSlug(slug) presenceData.smallImageKey = Assets.Viewing const detailCover = pageCover() - if (ready_to_use_posters && cover && detailCover) + if (ready_to_use_posters && cover && detailCover) { presenceData.largeImageKey = detailCover + presenceData.largeImageText = pageTitle() ?? titleFromSlug(slug) + } if (buttons) { presenceData.buttons = [ { label: 'View on Lunar', url: href }, From 509296d55719c7e0f2e232023072a3ce58ad5c9b Mon Sep 17 00:00:00 2001 From: nineteen <81378628+vixxyTW@users.noreply.github.com> Date: Sat, 15 Aug 2026 07:03:07 +0200 Subject: [PATCH 2/9] Changes & New Domain Signed-off-by: nineteen <81378628+vixxyTW@users.noreply.github.com> --- metadata.json | 76 +++++++++++ presence.ts | 349 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 425 insertions(+) create mode 100644 metadata.json create mode 100644 presence.ts diff --git a/metadata.json b/metadata.json new file mode 100644 index 000000000000..6efd6f877076 --- /dev/null +++ b/metadata.json @@ -0,0 +1,76 @@ +{ + "$schema": "https://schemas.premid.app/metadata/1.16", + "apiVersion": 1, + "author": { + "name": "alex", + "id": "1253394430652846151" + }, + "service": "Lunar", + "description": { + "en": "Lunar is a free platform to stream anime and read manga and light novels, with community features, playlists and more." + }, + "url": "lunarx.to", + "regExp": "^https?[:][/][/](www[.])?lunarx[.]to[/]", + "version": "1.0.0", + "logo": "https://api.lunarx.to/static/rpc/icon.png", + "thumbnail": "https://api.lunarx.to/static/rpc/thumbnail.png", + "color": "#895AF6", + "category": "anime", + "tags": [ + "anime", + "manga", + "novel", + "streaming", + "reading" + ], + "settings": [ + { + "id": "privacy", + "title": "Privacy Mode", + "icon": "fad fa-user-secret", + "value": false + }, + { + "id": "browsingActivity", + "if": { + "privacy": false + }, + "title": "Show Browsing Activity", + "icon": "fad fa-book-reader", + "value": true + }, + { + "id": "cover", + "if": { + "privacy": false + }, + "title": "Show Cover", + "icon": "fad fa-images", + "value": true + }, + { + "id": "timestamps", + "if": { + "privacy": false + }, + "title": "Show Timestamps", + "icon": "fad fa-stopwatch", + "value": true + }, + { + "id": "buttons", + "if": { + "privacy": false + }, + "title": "Show Buttons", + "icon": "fad fa-compress-arrows-alt", + "value": true + }, + { + "id": "hideWhenPaused", + "title": "Hide When Paused", + "icon": "fad fa-pause", + "value": false + } + ] +} diff --git a/presence.ts b/presence.ts new file mode 100644 index 000000000000..63b44d2eba13 --- /dev/null +++ b/presence.ts @@ -0,0 +1,349 @@ +import { ActivityType, Assets, getTimestamps } from 'premid' + +const ready_to_use_posters = true + +const presence = new Presence({ + clientId: '1255462235958939689', +}) + +let currentId = '' +let currentTimestamp = Math.floor(Date.now() / 1000) + +function updateTimestamp(id: string): void { + if (currentId !== id) { + currentId = id + currentTimestamp = Math.floor(Date.now() / 1000) + } +} + +enum ActivityAssets { + Logo = 'https://api.lunarx.to/static/rpc/icon.png?v=3', +} + +interface LunarActivity { + type: 'anime' | 'manga' | 'novel' + id: string + title: string + meta?: string + cover?: string +} + +function readBridge(): LunarActivity | null { + try { + const raw = document.documentElement.getAttribute('data-premid-activity') + if (!raw) + return null + const parsed = JSON.parse(raw) + if (!parsed || typeof parsed.title !== 'string' || !parsed.title) + return null + return parsed + } + catch { + return null + } +} + +function pageTitle(): string | null { + const og = document + .querySelector('meta[property="og:title"]') + ?.content + let title = og || document.title + if (!title) + return null + title = (title.split(' | ')[0] ?? '') + .replace(/\s*-\s*(?:S\d+\s*E|Ch\.\s*)\d.*$/i, '') + .trim() + if ( + !title + || /^lunar(?:anime|\s+realms)?$/i.test(title) + || /gateway to anime/i.test(title) + ) { + return null + } + return title +} + +function pageCover(): string | null { + return ( + document.querySelector('meta[property="og:image"]') + ?.content || null + ) +} + +function titleFromSlug(slug: string): string { + return decodeURIComponent(slug) + .replace(/-/g, ' ') + .replace(/\b\w/g, c => c.toUpperCase()) +} + +function fallbackFromPath(pathname: string): LunarActivity | null { + const [section, slug, second, third] = pathname.split('/').filter(Boolean) + if (!section || !slug) + return null + const title = pageTitle() ?? titleFromSlug(slug) + const cover = pageCover() ?? undefined + if (section === 'anime' && second && third) { + return { + type: 'anime', + id: slug, + title, + meta: `Season ${second} · Episode ${third}`, + cover, + } + } + if (section === 'manga' && second) { + return { type: 'manga', id: slug, title, meta: `Ch. ${second}`, cover } + } + if (section === 'novel' && second) { + return { type: 'novel', id: slug, title, cover } + } + return null +} + +const detailSections: Record = { + anime: 'Viewing anime', + animes: 'Viewing anime', + manga: 'Viewing manga', + novel: 'Viewing a novel', + novels: 'Viewing a novel', +} + +const searchSections: Record = { + anime: 'Searching anime', + animes: 'Searching anime', + manga: 'Searching manga', + novel: 'Searching novels', + novels: 'Searching novels', +} + +const sectionDetails: Record = { + 'animes': 'Browsing anime', + 'manga': 'Browsing manga', + 'novels': 'Browsing novels', + 'home': 'Browsing the home page', + 'releases': 'Checking new releases', + 'genres': 'Browsing genres', + 'events': 'Checking events', + 'gacha': 'Rolling gacha', + 'casino': 'In the casino', + 'marketplace': 'Browsing the marketplace', + 'shop': 'Browsing the shop', + 'trade': 'Trading', + 'chat': 'Chatting', + 'rooms': 'In a room', + 'community': 'Browsing the community', + 'guilds': 'Browsing guilds', + 'leaderboard': 'Viewing the leaderboard', + 'achievements': 'Viewing achievements', + 'playlist': 'Browsing playlists', + 'manga-playlist': 'Browsing playlists', + 'novel-playlist': 'Browsing playlists', + 'video-playlist': 'Browsing playlists', + 'videos': 'Watching videos', + 'donate': 'Supporting Lunar', + 'rewards': 'Claiming rewards', + 'settings': 'Adjusting settings', +} + +presence.on('UpdateData', async () => { + const [privacy, browsingActivity, cover, timestamps, buttons, hideWhenPaused] + = await Promise.all([ + presence.getSetting('privacy'), + presence.getSetting('browsingActivity'), + presence.getSetting('cover'), + presence.getSetting('timestamps'), + presence.getSetting('buttons'), + presence.getSetting('hideWhenPaused'), + ]) + + if (privacy) { + updateTimestamp('privacy') + presence.setActivity({ + largeImageKey: ActivityAssets.Logo, + largeImageText: 'lunarx.to', + details: 'Browsing Lunar', + startTimestamp: currentTimestamp, + }) + return + } + + const { pathname, href } = document.location + const activity = readBridge() ?? fallbackFromPath(pathname) + + if (activity) { + updateTimestamp(`${activity.type}:${activity.id}`) + const artwork = activity.cover || pageCover() + const largeImageKey + = ready_to_use_posters && cover && artwork + ? artwork + : ActivityAssets.Logo + + if (activity.type === 'anime') { + const presenceData: PresenceData = { + type: ActivityType.Watching, + details: activity.title, + state: activity.meta, + largeImageKey, + largeImageText: + largeImageKey === ActivityAssets.Logo + ? 'lunarx.to' + : activity.title, + smallImageKey: Assets.Play, + smallImageText: 'Watching', + startTimestamp: currentTimestamp, + } + + const video = document.querySelector('video') + if (video && !Number.isNaN(video.duration) && video.duration > 0) { + if (video.paused) { + if (hideWhenPaused) { + presence.clearActivity() + return + } + delete presenceData.startTimestamp + presenceData.smallImageKey = Assets.Pause + presenceData.smallImageText = 'Paused' + } + else { + if (video.readyState < 3) { + presenceData.smallImageKey = Assets.Downloading + presenceData.smallImageText = 'Buffering' + } + if (timestamps) { + const [start, end] = getTimestamps( + Math.floor(video.currentTime), + Math.floor(video.duration), + ) + presenceData.startTimestamp = start + presenceData.endTimestamp = end + } + } + } + + if (buttons) { + presenceData.buttons = [ + { label: 'Watch on Lunar', url: href }, + { + label: 'View Anime', + url: `https://lunarx.to/anime/${activity.id}`, + }, + ] + } + + presence.setActivity(presenceData) + return + } + + const presenceData: PresenceData = { + details: activity.title, + state: activity.meta, + largeImageKey, + largeImageText: + largeImageKey === ActivityAssets.Logo + ? 'lunarx.to' + : activity.title, + smallImageKey: Assets.Reading, + smallImageText: + activity.type === 'manga' ? 'Reading Manga' : 'Reading Novel', + startTimestamp: currentTimestamp, + } + + if (buttons) { + presenceData.buttons = [ + { label: 'Read on Lunar', url: href }, + { + label: activity.type === 'manga' ? 'View Series' : 'View Novel', + url: `https://lunarx.to/${activity.type}/${activity.id}`, + }, + ] + } + + presence.setActivity(presenceData) + return + } + + if (!browsingActivity) { + presence.clearActivity() + return + } + + const presenceData: PresenceData = { + largeImageKey: ActivityAssets.Logo, + largeImageText: 'lunarx.to', + details: 'Browsing Lunar', + } + + const [section, slug] = pathname.split('/').filter(Boolean) + + const searchParams = new URLSearchParams(document.location.search) + const searchQuery = ( + searchParams.get('query') ?? searchParams.get('q') ?? '' + ).trim() + + const titleSearch = document.querySelector( + 'input[placeholder^="Search anime"]', + ) + const userSearch = document.querySelector( + 'input[placeholder^="Search by username"]', + ) + + if (section && slug === 'search' && searchSections[section]) { + updateTimestamp(`search:${section}`) + presenceData.details = searchSections[section] + if (searchQuery) + presenceData.state = searchQuery + presenceData.smallImageKey = Assets.Search + if (buttons) { + presenceData.buttons = [ + { label: 'View on Lunar', url: href }, + ] + } + } + else if (titleSearch?.value) { + updateTimestamp('search:titles') + presenceData.details = 'Searching titles' + presenceData.state = titleSearch.value + presenceData.smallImageKey = Assets.Search + } + else if (section === 'search' || userSearch?.value) { + updateTimestamp('search:users') + presenceData.details = 'Searching users' + const query = userSearch?.value || searchQuery + if (query) + presenceData.state = query + presenceData.smallImageKey = Assets.Search + } + else if (section === 'profile' && slug) { + updateTimestamp(`profile:${slug}`) + presenceData.details = 'Viewing a profile' + presenceData.state = decodeURIComponent(slug) + presenceData.smallImageKey = Assets.Viewing + } + else if (section && slug && detailSections[section]) { + updateTimestamp(`detail:${section}:${slug}`) + presenceData.details = detailSections[section] + presenceData.state = pageTitle() ?? titleFromSlug(slug) + presenceData.smallImageKey = Assets.Viewing + const detailCover = pageCover() + if (ready_to_use_posters && cover && detailCover) { + presenceData.largeImageKey = detailCover + presenceData.largeImageText = pageTitle() ?? titleFromSlug(slug) + } + if (buttons) { + presenceData.buttons = [ + { label: 'View on Lunar', url: href }, + ] + } + } + else if (section && sectionDetails[section]) { + updateTimestamp(`browse:${section}`) + presenceData.details = sectionDetails[section] + presenceData.smallImageKey = Assets.Viewing + } + else { + updateTimestamp('browse:home') + } + + presenceData.startTimestamp = currentTimestamp + presence.setActivity(presenceData) +}) From bc51d3a8faca443bfab2b6517695817e02e73c5e Mon Sep 17 00:00:00 2001 From: nineteen <81378628+vixxyTW@users.noreply.github.com> Date: Sat, 15 Aug 2026 07:06:47 +0200 Subject: [PATCH 3/9] v bump Signed-off-by: nineteen <81378628+vixxyTW@users.noreply.github.com> --- websites/L/Lunar/metadata.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/websites/L/Lunar/metadata.json b/websites/L/Lunar/metadata.json index f5ce4f938dc9..20f3f0e5f35b 100644 --- a/websites/L/Lunar/metadata.json +++ b/websites/L/Lunar/metadata.json @@ -11,8 +11,8 @@ }, "url": "lunarx.to", "regExp": "^https?[:][/][/](www[.])?lunarx[.]to[/]", - "version": "1.0.0", - "logo": "https://api.lunarx.to/static/rpc/icon.png?v=3", + "version": "1.0.1", + "logo": "https://api.lunarx.to/static/rpc/icon.png", "thumbnail": "https://api.lunarx.to/static/rpc/thumbnail.png", "color": "#895AF6", "category": "anime", From c80e672212aace2fa52500ede3200945ee4876ef Mon Sep 17 00:00:00 2001 From: nineteen <81378628+vixxyTW@users.noreply.github.com> Date: Sun, 16 Aug 2026 12:39:51 +0200 Subject: [PATCH 4/9] Update websites/L/Lunar/metadata.json Co-authored-by: Skully <117316808+skullysmods@users.noreply.github.com> Signed-off-by: nineteen <81378628+vixxyTW@users.noreply.github.com> --- websites/L/Lunar/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/websites/L/Lunar/metadata.json b/websites/L/Lunar/metadata.json index 20f3f0e5f35b..a2d4fe4d5636 100644 --- a/websites/L/Lunar/metadata.json +++ b/websites/L/Lunar/metadata.json @@ -1,5 +1,5 @@ { - "$schema": "https://schemas.premid.app/metadata/1.16", + "$schema": "https://schemas.premid.app/metadata/1.17", "apiVersion": 1, "author": { "name": "alex", From e5678c13a88d06caab69dd9e8f061587343e08e0 Mon Sep 17 00:00:00 2001 From: nineteen <81378628+vixxyTW@users.noreply.github.com> Date: Sun, 16 Aug 2026 12:39:59 +0200 Subject: [PATCH 5/9] Update websites/L/Lunar/metadata.json Co-authored-by: Skully <117316808+skullysmods@users.noreply.github.com> Signed-off-by: nineteen <81378628+vixxyTW@users.noreply.github.com> --- websites/L/Lunar/metadata.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/websites/L/Lunar/metadata.json b/websites/L/Lunar/metadata.json index a2d4fe4d5636..38f0c8aaf9b8 100644 --- a/websites/L/Lunar/metadata.json +++ b/websites/L/Lunar/metadata.json @@ -12,8 +12,8 @@ "url": "lunarx.to", "regExp": "^https?[:][/][/](www[.])?lunarx[.]to[/]", "version": "1.0.1", - "logo": "https://api.lunarx.to/static/rpc/icon.png", - "thumbnail": "https://api.lunarx.to/static/rpc/thumbnail.png", + "logo": "https://cdn.rcd.gg/PreMiD/websites/L/Lunar/assets/logo.png", + "thumbnail": "https://cdn.rcd.gg/PreMiD/websites/L/Lunar/assets/thumbnail.png", "color": "#895AF6", "category": "anime", "tags": [ From 9d87f729b2d666a9fa67d35b1735893a3617230f Mon Sep 17 00:00:00 2001 From: nineteen <81378628+vixxyTW@users.noreply.github.com> Date: Sun, 16 Aug 2026 12:40:12 +0200 Subject: [PATCH 6/9] Update websites/L/Lunar/presence.ts Co-authored-by: Skully <117316808+skullysmods@users.noreply.github.com> Signed-off-by: nineteen <81378628+vixxyTW@users.noreply.github.com> --- websites/L/Lunar/presence.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/websites/L/Lunar/presence.ts b/websites/L/Lunar/presence.ts index 63b44d2eba13..8d00b3d6040d 100644 --- a/websites/L/Lunar/presence.ts +++ b/websites/L/Lunar/presence.ts @@ -17,7 +17,7 @@ function updateTimestamp(id: string): void { } enum ActivityAssets { - Logo = 'https://api.lunarx.to/static/rpc/icon.png?v=3', + Logo = 'https://cdn.rcd.gg/PreMiD/websites/L/Lunar/assets/logo.png', } interface LunarActivity { From f93f2b9299340fb8745cccb3b0e71115a95c8581 Mon Sep 17 00:00:00 2001 From: nineteen <81378628+vixxyTW@users.noreply.github.com> Date: Fri, 21 Aug 2026 19:03:55 +0200 Subject: [PATCH 7/9] Delete websites/L/Lunar/metadata.json Signed-off-by: nineteen <81378628+vixxyTW@users.noreply.github.com> --- websites/L/Lunar/metadata.json | 76 ---------------------------------- 1 file changed, 76 deletions(-) delete mode 100644 websites/L/Lunar/metadata.json diff --git a/websites/L/Lunar/metadata.json b/websites/L/Lunar/metadata.json deleted file mode 100644 index 38f0c8aaf9b8..000000000000 --- a/websites/L/Lunar/metadata.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "$schema": "https://schemas.premid.app/metadata/1.17", - "apiVersion": 1, - "author": { - "name": "alex", - "id": "1253394430652846151" - }, - "service": "Lunar", - "description": { - "en": "Lunar is a free platform to stream anime and read manga and light novels, with community features, playlists and more." - }, - "url": "lunarx.to", - "regExp": "^https?[:][/][/](www[.])?lunarx[.]to[/]", - "version": "1.0.1", - "logo": "https://cdn.rcd.gg/PreMiD/websites/L/Lunar/assets/logo.png", - "thumbnail": "https://cdn.rcd.gg/PreMiD/websites/L/Lunar/assets/thumbnail.png", - "color": "#895AF6", - "category": "anime", - "tags": [ - "anime", - "manga", - "novel", - "streaming", - "reading" - ], - "settings": [ - { - "id": "privacy", - "title": "Privacy Mode", - "icon": "fad fa-user-secret", - "value": false - }, - { - "id": "browsingActivity", - "if": { - "privacy": false - }, - "title": "Show Browsing Activity", - "icon": "fad fa-book-reader", - "value": true - }, - { - "id": "cover", - "if": { - "privacy": false - }, - "title": "Show Cover", - "icon": "fad fa-images", - "value": true - }, - { - "id": "timestamps", - "if": { - "privacy": false - }, - "title": "Show Timestamps", - "icon": "fad fa-stopwatch", - "value": true - }, - { - "id": "buttons", - "if": { - "privacy": false - }, - "title": "Show Buttons", - "icon": "fad fa-compress-arrows-alt", - "value": true - }, - { - "id": "hideWhenPaused", - "title": "Hide When Paused", - "icon": "fad fa-pause", - "value": false - } - ] -} From ed433bbb55dc2258c286c21d4ecee16ec4cc98c5 Mon Sep 17 00:00:00 2001 From: nineteen <81378628+vixxyTW@users.noreply.github.com> Date: Sat, 22 Aug 2026 15:43:50 +0200 Subject: [PATCH 8/9] Delete metadata.json Signed-off-by: nineteen <81378628+vixxyTW@users.noreply.github.com> --- metadata.json | 76 --------------------------------------------------- 1 file changed, 76 deletions(-) delete mode 100644 metadata.json diff --git a/metadata.json b/metadata.json deleted file mode 100644 index 6efd6f877076..000000000000 --- a/metadata.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "$schema": "https://schemas.premid.app/metadata/1.16", - "apiVersion": 1, - "author": { - "name": "alex", - "id": "1253394430652846151" - }, - "service": "Lunar", - "description": { - "en": "Lunar is a free platform to stream anime and read manga and light novels, with community features, playlists and more." - }, - "url": "lunarx.to", - "regExp": "^https?[:][/][/](www[.])?lunarx[.]to[/]", - "version": "1.0.0", - "logo": "https://api.lunarx.to/static/rpc/icon.png", - "thumbnail": "https://api.lunarx.to/static/rpc/thumbnail.png", - "color": "#895AF6", - "category": "anime", - "tags": [ - "anime", - "manga", - "novel", - "streaming", - "reading" - ], - "settings": [ - { - "id": "privacy", - "title": "Privacy Mode", - "icon": "fad fa-user-secret", - "value": false - }, - { - "id": "browsingActivity", - "if": { - "privacy": false - }, - "title": "Show Browsing Activity", - "icon": "fad fa-book-reader", - "value": true - }, - { - "id": "cover", - "if": { - "privacy": false - }, - "title": "Show Cover", - "icon": "fad fa-images", - "value": true - }, - { - "id": "timestamps", - "if": { - "privacy": false - }, - "title": "Show Timestamps", - "icon": "fad fa-stopwatch", - "value": true - }, - { - "id": "buttons", - "if": { - "privacy": false - }, - "title": "Show Buttons", - "icon": "fad fa-compress-arrows-alt", - "value": true - }, - { - "id": "hideWhenPaused", - "title": "Hide When Paused", - "icon": "fad fa-pause", - "value": false - } - ] -} From e699645913980460b6870a1ae419d09255936a2e Mon Sep 17 00:00:00 2001 From: nineteen <81378628+vixxyTW@users.noreply.github.com> Date: Sat, 22 Aug 2026 15:44:09 +0200 Subject: [PATCH 9/9] Delete presence.ts Signed-off-by: nineteen <81378628+vixxyTW@users.noreply.github.com> --- presence.ts | 349 ---------------------------------------------------- 1 file changed, 349 deletions(-) delete mode 100644 presence.ts diff --git a/presence.ts b/presence.ts deleted file mode 100644 index 63b44d2eba13..000000000000 --- a/presence.ts +++ /dev/null @@ -1,349 +0,0 @@ -import { ActivityType, Assets, getTimestamps } from 'premid' - -const ready_to_use_posters = true - -const presence = new Presence({ - clientId: '1255462235958939689', -}) - -let currentId = '' -let currentTimestamp = Math.floor(Date.now() / 1000) - -function updateTimestamp(id: string): void { - if (currentId !== id) { - currentId = id - currentTimestamp = Math.floor(Date.now() / 1000) - } -} - -enum ActivityAssets { - Logo = 'https://api.lunarx.to/static/rpc/icon.png?v=3', -} - -interface LunarActivity { - type: 'anime' | 'manga' | 'novel' - id: string - title: string - meta?: string - cover?: string -} - -function readBridge(): LunarActivity | null { - try { - const raw = document.documentElement.getAttribute('data-premid-activity') - if (!raw) - return null - const parsed = JSON.parse(raw) - if (!parsed || typeof parsed.title !== 'string' || !parsed.title) - return null - return parsed - } - catch { - return null - } -} - -function pageTitle(): string | null { - const og = document - .querySelector('meta[property="og:title"]') - ?.content - let title = og || document.title - if (!title) - return null - title = (title.split(' | ')[0] ?? '') - .replace(/\s*-\s*(?:S\d+\s*E|Ch\.\s*)\d.*$/i, '') - .trim() - if ( - !title - || /^lunar(?:anime|\s+realms)?$/i.test(title) - || /gateway to anime/i.test(title) - ) { - return null - } - return title -} - -function pageCover(): string | null { - return ( - document.querySelector('meta[property="og:image"]') - ?.content || null - ) -} - -function titleFromSlug(slug: string): string { - return decodeURIComponent(slug) - .replace(/-/g, ' ') - .replace(/\b\w/g, c => c.toUpperCase()) -} - -function fallbackFromPath(pathname: string): LunarActivity | null { - const [section, slug, second, third] = pathname.split('/').filter(Boolean) - if (!section || !slug) - return null - const title = pageTitle() ?? titleFromSlug(slug) - const cover = pageCover() ?? undefined - if (section === 'anime' && second && third) { - return { - type: 'anime', - id: slug, - title, - meta: `Season ${second} · Episode ${third}`, - cover, - } - } - if (section === 'manga' && second) { - return { type: 'manga', id: slug, title, meta: `Ch. ${second}`, cover } - } - if (section === 'novel' && second) { - return { type: 'novel', id: slug, title, cover } - } - return null -} - -const detailSections: Record = { - anime: 'Viewing anime', - animes: 'Viewing anime', - manga: 'Viewing manga', - novel: 'Viewing a novel', - novels: 'Viewing a novel', -} - -const searchSections: Record = { - anime: 'Searching anime', - animes: 'Searching anime', - manga: 'Searching manga', - novel: 'Searching novels', - novels: 'Searching novels', -} - -const sectionDetails: Record = { - 'animes': 'Browsing anime', - 'manga': 'Browsing manga', - 'novels': 'Browsing novels', - 'home': 'Browsing the home page', - 'releases': 'Checking new releases', - 'genres': 'Browsing genres', - 'events': 'Checking events', - 'gacha': 'Rolling gacha', - 'casino': 'In the casino', - 'marketplace': 'Browsing the marketplace', - 'shop': 'Browsing the shop', - 'trade': 'Trading', - 'chat': 'Chatting', - 'rooms': 'In a room', - 'community': 'Browsing the community', - 'guilds': 'Browsing guilds', - 'leaderboard': 'Viewing the leaderboard', - 'achievements': 'Viewing achievements', - 'playlist': 'Browsing playlists', - 'manga-playlist': 'Browsing playlists', - 'novel-playlist': 'Browsing playlists', - 'video-playlist': 'Browsing playlists', - 'videos': 'Watching videos', - 'donate': 'Supporting Lunar', - 'rewards': 'Claiming rewards', - 'settings': 'Adjusting settings', -} - -presence.on('UpdateData', async () => { - const [privacy, browsingActivity, cover, timestamps, buttons, hideWhenPaused] - = await Promise.all([ - presence.getSetting('privacy'), - presence.getSetting('browsingActivity'), - presence.getSetting('cover'), - presence.getSetting('timestamps'), - presence.getSetting('buttons'), - presence.getSetting('hideWhenPaused'), - ]) - - if (privacy) { - updateTimestamp('privacy') - presence.setActivity({ - largeImageKey: ActivityAssets.Logo, - largeImageText: 'lunarx.to', - details: 'Browsing Lunar', - startTimestamp: currentTimestamp, - }) - return - } - - const { pathname, href } = document.location - const activity = readBridge() ?? fallbackFromPath(pathname) - - if (activity) { - updateTimestamp(`${activity.type}:${activity.id}`) - const artwork = activity.cover || pageCover() - const largeImageKey - = ready_to_use_posters && cover && artwork - ? artwork - : ActivityAssets.Logo - - if (activity.type === 'anime') { - const presenceData: PresenceData = { - type: ActivityType.Watching, - details: activity.title, - state: activity.meta, - largeImageKey, - largeImageText: - largeImageKey === ActivityAssets.Logo - ? 'lunarx.to' - : activity.title, - smallImageKey: Assets.Play, - smallImageText: 'Watching', - startTimestamp: currentTimestamp, - } - - const video = document.querySelector('video') - if (video && !Number.isNaN(video.duration) && video.duration > 0) { - if (video.paused) { - if (hideWhenPaused) { - presence.clearActivity() - return - } - delete presenceData.startTimestamp - presenceData.smallImageKey = Assets.Pause - presenceData.smallImageText = 'Paused' - } - else { - if (video.readyState < 3) { - presenceData.smallImageKey = Assets.Downloading - presenceData.smallImageText = 'Buffering' - } - if (timestamps) { - const [start, end] = getTimestamps( - Math.floor(video.currentTime), - Math.floor(video.duration), - ) - presenceData.startTimestamp = start - presenceData.endTimestamp = end - } - } - } - - if (buttons) { - presenceData.buttons = [ - { label: 'Watch on Lunar', url: href }, - { - label: 'View Anime', - url: `https://lunarx.to/anime/${activity.id}`, - }, - ] - } - - presence.setActivity(presenceData) - return - } - - const presenceData: PresenceData = { - details: activity.title, - state: activity.meta, - largeImageKey, - largeImageText: - largeImageKey === ActivityAssets.Logo - ? 'lunarx.to' - : activity.title, - smallImageKey: Assets.Reading, - smallImageText: - activity.type === 'manga' ? 'Reading Manga' : 'Reading Novel', - startTimestamp: currentTimestamp, - } - - if (buttons) { - presenceData.buttons = [ - { label: 'Read on Lunar', url: href }, - { - label: activity.type === 'manga' ? 'View Series' : 'View Novel', - url: `https://lunarx.to/${activity.type}/${activity.id}`, - }, - ] - } - - presence.setActivity(presenceData) - return - } - - if (!browsingActivity) { - presence.clearActivity() - return - } - - const presenceData: PresenceData = { - largeImageKey: ActivityAssets.Logo, - largeImageText: 'lunarx.to', - details: 'Browsing Lunar', - } - - const [section, slug] = pathname.split('/').filter(Boolean) - - const searchParams = new URLSearchParams(document.location.search) - const searchQuery = ( - searchParams.get('query') ?? searchParams.get('q') ?? '' - ).trim() - - const titleSearch = document.querySelector( - 'input[placeholder^="Search anime"]', - ) - const userSearch = document.querySelector( - 'input[placeholder^="Search by username"]', - ) - - if (section && slug === 'search' && searchSections[section]) { - updateTimestamp(`search:${section}`) - presenceData.details = searchSections[section] - if (searchQuery) - presenceData.state = searchQuery - presenceData.smallImageKey = Assets.Search - if (buttons) { - presenceData.buttons = [ - { label: 'View on Lunar', url: href }, - ] - } - } - else if (titleSearch?.value) { - updateTimestamp('search:titles') - presenceData.details = 'Searching titles' - presenceData.state = titleSearch.value - presenceData.smallImageKey = Assets.Search - } - else if (section === 'search' || userSearch?.value) { - updateTimestamp('search:users') - presenceData.details = 'Searching users' - const query = userSearch?.value || searchQuery - if (query) - presenceData.state = query - presenceData.smallImageKey = Assets.Search - } - else if (section === 'profile' && slug) { - updateTimestamp(`profile:${slug}`) - presenceData.details = 'Viewing a profile' - presenceData.state = decodeURIComponent(slug) - presenceData.smallImageKey = Assets.Viewing - } - else if (section && slug && detailSections[section]) { - updateTimestamp(`detail:${section}:${slug}`) - presenceData.details = detailSections[section] - presenceData.state = pageTitle() ?? titleFromSlug(slug) - presenceData.smallImageKey = Assets.Viewing - const detailCover = pageCover() - if (ready_to_use_posters && cover && detailCover) { - presenceData.largeImageKey = detailCover - presenceData.largeImageText = pageTitle() ?? titleFromSlug(slug) - } - if (buttons) { - presenceData.buttons = [ - { label: 'View on Lunar', url: href }, - ] - } - } - else if (section && sectionDetails[section]) { - updateTimestamp(`browse:${section}`) - presenceData.details = sectionDetails[section] - presenceData.smallImageKey = Assets.Viewing - } - else { - updateTimestamp('browse:home') - } - - presenceData.startTimestamp = currentTimestamp - presence.setActivity(presenceData) -})